Skip to content
This repository was archived by the owner on May 16, 2019. It is now read-only.

Commit dd50df7

Browse files
author
Tom Galloway
committed
Merge branch 'master' into market_listeners_tests
2 parents f2dcdc6 + 19b54d7 commit dd50df7

File tree

11 files changed

+157
-114
lines changed

11 files changed

+157
-114
lines changed

api/restapi.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from twisted.protocols.basic import FileSender
1919

2020
from config import DATA_FOLDER, RESOLVER, LIBBITCOIN_SERVER_TESTNET, LIBBITCOIN_SERVER, \
21-
set_value, get_value, str_to_bool
21+
set_value, get_value, str_to_bool, TRANSACTION_FEE
2222
from protos.countries import CountryCode
2323
from protos import objects
2424
from keys import blockchainid
@@ -858,7 +858,8 @@ def get_settings(self, request):
858858
"terms_conditions": "" if settings[9] is None else settings[9],
859859
"refund_policy": "" if settings[10] is None else settings[10],
860860
"resolver": get_value("CONSTANTS", "RESOLVER"),
861-
"network_connection": nat_type
861+
"network_connection": nat_type,
862+
"transaction_fee": TRANSACTION_FEE
862863
}
863864
mods = []
864865
try:

db/datastore.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,17 @@ def get_conversations(self):
562562
WHERE guid=? and messageType=?''', (g[0], "CHAT"))
563563
val = cursor.fetchone()
564564
if val[0] is not None:
565+
avatar_hash = None
566+
try:
567+
with open(DATA_FOLDER + 'cache/' + g[0], "r") as filename:
568+
profile = filename.read()
569+
p = objects.Profile()
570+
p.ParseFromString(profile)
571+
avatar_hash = p.avatar_hash.encode("hex")
572+
except Exception:
573+
pass
565574
ret.append({"guid": g[0],
566-
"avatar_hash": val[0].encode("hex"),
575+
"avatar_hash": avatar_hash,
567576
"last_message": val[1],
568577
"timestamp": val[2],
569578
"public_key": val[3].encode("hex"),
@@ -842,7 +851,7 @@ def get_all(self):
842851
def get_unfunded(self):
843852
conn = Database.connect_database(self.PATH)
844853
cursor = conn.cursor()
845-
cursor.execute('''SELECT id FROM purchases WHERE status=0''')
854+
cursor.execute('''SELECT id, timestamp FROM purchases WHERE status=0''')
846855
ret = cursor.fetchall()
847856
conn.close()
848857
return ret

dht/protocol.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def handleCallResponse(self, result, node):
224224
we get no response, make sure it's removed from the routing table.
225225
"""
226226
if result[0]:
227-
if self.router.isNewNode(node):
227+
if self.isNewConnection(node):
228228
self.log.debug("Call response from new node, transferring key/values")
229229
reactor.callLater(1, self.transferKeyValues, node)
230230
self.router.addContact(node)
@@ -239,11 +239,17 @@ def addToRouter(self, node):
239239
We add the node to our router and transfer our stored values
240240
if they are new and within our neighborhood.
241241
"""
242-
if self.router.isNewNode(node):
242+
if self.isNewConnection(node):
243243
self.log.debug("Found a new node, transferring key/values")
244244
reactor.callLater(1, self.transferKeyValues, node)
245245
self.router.addContact(node)
246246

247+
def isNewConnection(self, node):
248+
if (node.ip, node.port) in self.multiplexer:
249+
return self.multiplexer[(node.ip, node.port)].handler.check_new_connection()
250+
else:
251+
return False
252+
247253
def __iter__(self):
248254
return iter(self.handled_commands)
249255

dht/tests/test_protocol.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from dht.protocol import KademliaProtocol
1616
from dht.utils import digest
1717
from dht.storage import ForgetfulStorage
18-
from dht.tests.utils import mknode
1918
from dht.node import Node
2019
from protos import message, objects
2120
from net.wireprotocol import OpenBazaarProtocol
@@ -613,7 +612,7 @@ def _connecting_to_connected(self):
613612
self.next_seqnum = seqnum + 1
614613

615614
def test_badRPCDelete(self):
616-
n = mknode()
615+
n = Node(digest("S"), self.addr1[0], self.addr1[1])
617616
val = self.protocol.rpc_delete(n, 'testkeyword', 'key', 'testsig')
618617
self.assertEqual(val, ["False"])
619618
val = self.protocol.rpc_delete(n, '', '', '')

market/contracts.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from bitcoin.core.script import CScript, OP_2, OP_3, OP_CHECKMULTISIG
1515
from bitcoin.wallet import P2SHBitcoinAddress, P2PKHBitcoinAddress
1616
from collections import OrderedDict
17-
from config import DATA_FOLDER
17+
from config import DATA_FOLDER, TRANSACTION_FEE
1818
from copy import deepcopy
1919
from datetime import datetime
2020
from dht.utils import digest
@@ -410,6 +410,9 @@ def add_purchase_info(self,
410410
shipping_amount = float("{0:.8f}".format(float(price) / float(conversion_rate))) * quantity
411411
amount_to_pay += shipping_amount
412412

413+
if round(amount_to_pay, 8) < round(TRANSACTION_FEE / float(100000000), 8):
414+
raise Exception("Contract price is below transaction fee.")
415+
413416
order_json["buyer_order"]["order"]["payment"]["amount"] = round(amount_to_pay, 8)
414417
self.contract["buyer_order"] = order_json["buyer_order"]
415418

@@ -483,7 +486,7 @@ def add_order_confirmation(self,
483486
vendor_priv = derive_childkey(masterkey_v, chaincode, bitcointools.MAINNET_PRIVATE)
484487
tx.sign(vendor_priv)
485488
tx.broadcast(self.blockchain)
486-
self.log.info("Broadcasting payout tx %s to network" % tx.get_hash())
489+
self.log.info("broadcasting payout tx %s to network" % tx.get_hash())
487490
self.db.sales.update_payment_tx(order_id, tx.get_hash())
488491

489492
confirmation = json.dumps(conf_json["vendor_order_confirmation"]["invoice"], indent=4)
@@ -631,7 +634,7 @@ def add_receipt(self,
631634
receipt_json["buyer_receipt"]["receipt"]["payout"] = {}
632635
if tx.multisign(signatures, redeem_script):
633636
tx.broadcast(self.blockchain)
634-
self.log.info("Broadcasting payout tx %s to network" % tx.get_hash())
637+
self.log.info("broadcasting payout tx %s to network" % tx.get_hash())
635638
receipt_json["buyer_receipt"]["receipt"]["payout"]["txid"] = tx.get_hash()
636639

637640
receipt_json["buyer_receipt"]["receipt"]["payout"]["signature(s)"] = buyer_signatures
@@ -721,7 +724,7 @@ def accept_receipt(self, notification_listener, blockchain, receipt_json=None):
721724

722725
tx.multisign(signatures, redeem_script)
723726
tx.broadcast(self.blockchain)
724-
self.log.info("Broadcasting payout tx %s to network" % tx.get_hash())
727+
self.log.info("broadcasting payout tx %s to network" % tx.get_hash())
725728

726729
self.db.sales.update_payment_tx(order_id, tx.get_hash())
727730

@@ -964,6 +967,60 @@ def save(self):
964967
# save the `ListingMetadata` protobuf to the database as well
965968
self.db.listings.add_listing(data)
966969

970+
def process_refund(self, refund_json, blockchain, notification_listener):
971+
self.contract["refund"] = refund_json["refund"]
972+
order_id = refund_json["refund"]["order_id"]
973+
974+
if "txid" not in refund_json["refund"]:
975+
outpoints = json.loads(self.db.purchases.get_outpoint(order_id))
976+
refund_address = self.contract["buyer_order"]["order"]["refund_address"]
977+
redeem_script = self.contract["buyer_order"]["order"]["payment"]["redeem_script"]
978+
value = int(float(refund_json["refund"]["value"]) * 100000000)
979+
tx = BitcoinTransaction.make_unsigned(outpoints, refund_address,
980+
testnet=self.testnet,
981+
out_value=value)
982+
chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"]
983+
masterkey_b = bitcointools.bip32_extract_key(KeyChain(self.db).bitcoin_master_privkey)
984+
buyer_priv = derive_childkey(masterkey_b, chaincode, bitcointools.MAINNET_PRIVATE)
985+
buyer_sigs = tx.create_signature(buyer_priv, redeem_script)
986+
vendor_sigs = refund_json["refund"]["signature(s)"]
987+
988+
signatures = []
989+
for i in range(len(outpoints)):
990+
for vendor_sig in vendor_sigs:
991+
if vendor_sig["index"] == i:
992+
v_signature = vendor_sig["signature"]
993+
for buyer_sig in buyer_sigs:
994+
if buyer_sig["index"] == i:
995+
b_signature = buyer_sig["signature"]
996+
signature_obj = {"index": i, "signatures": [b_signature, v_signature]}
997+
signatures.append(signature_obj)
998+
999+
tx.multisign(signatures, redeem_script)
1000+
tx.broadcast(blockchain)
1001+
self.log.info("broadcasting refund tx %s to network" % tx.get_hash())
1002+
1003+
self.db.sales.update_status(order_id, 7)
1004+
file_path = DATA_FOLDER + "purchases/trade receipts/" + order_id + ".json"
1005+
with open(file_path, 'w') as outfile:
1006+
outfile.write(json.dumps(self.contract, indent=4))
1007+
file_path = DATA_FOLDER + "purchases/in progress/" + order_id + ".json"
1008+
if os.path.exists(file_path):
1009+
os.remove(file_path)
1010+
1011+
title = self.contract["vendor_offer"]["listing"]["item"]["title"]
1012+
if "image_hashes" in self.contract["vendor_offer"]["listing"]["item"]:
1013+
image_hash = unhexlify(self.contract["vendor_offer"]["listing"]["item"]["image_hashes"][0])
1014+
else:
1015+
image_hash = ""
1016+
buyer_guid = self.contract["buyer_order"]["order"]["id"]["guid"]
1017+
if "blockchain_id" in self.contract["buyer_order"]["order"]["id"]:
1018+
handle = self.contract["buyer_order"]["order"]["id"]["blockchain_id"]
1019+
else:
1020+
handle = ""
1021+
notification_listener.notify(buyer_guid, handle, "refund", order_id, title, image_hash)
1022+
1023+
9671024
def verify(self, sender_key):
9681025
"""
9691026
Validate that an order sent over by a buyer is filled out correctly.

market/network.py

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import nacl.utils
1212
import obelisk
1313
import os.path
14-
import pickle
1514
import time
1615
from binascii import unhexlify
1716
from collections import OrderedDict
@@ -197,6 +196,7 @@ def get_result(result):
197196
self.get_image(node_to_ask, p.avatar_hash)
198197
if not os.path.isfile(DATA_FOLDER + 'cache/' + p.header_hash.encode("hex")):
199198
self.get_image(node_to_ask, p.header_hash)
199+
self.cache(result[1][0], node_to_ask.id.encode("hex"))
200200
return p
201201
except Exception:
202202
return None
@@ -563,6 +563,12 @@ def parse_messages(messages):
563563
self.db, self.protocol.get_message_listener(),
564564
self.protocol.get_notification_listener(),
565565
self.protocol.multiplexer.testnet)
566+
elif p.type == objects.PlaintextMessage.Type.Value("REFUND"):
567+
refund_json = json.loads(p.message, object_pairs_hook=OrderedDict)
568+
c = Contract(self.db, hash_value=unhexlify(refund_json["refund"]["order_id"]),
569+
testnet=self.protocol.multiplexer.testnet)
570+
c.process_refund(refund_json, self.protocol.multiplexer.blockchain,
571+
self.protocol.get_notification_listener())
566572
else:
567573
listener.notify(p, signature)
568574
except Exception:
@@ -833,16 +839,20 @@ def history_fetched(ec, history):
833839

834840
outputs.append({'value': moderator_fee, 'address': moderator_address})
835841
dispute_json["dispute_resolution"]["resolution"]["moderator_address"] = moderator_address
836-
dispute_json["dispute_resolution"]["resolution"]["moderator_fee"] = moderator_fee
837-
dispute_json["dispute_resolution"]["resolution"]["transaction_fee"] = TRANSACTION_FEE
842+
dispute_json["dispute_resolution"]["resolution"]["moderator_fee"] = \
843+
round(moderator_fee / float(100000000), 8)
844+
dispute_json["dispute_resolution"]["resolution"]["transaction_fee"] = \
845+
round(TRANSACTION_FEE / float(100000000), 8)
838846
if float(buyer_percentage) > 0:
839847
amt = int(float(buyer_percentage) * satoshis)
840-
dispute_json["dispute_resolution"]["resolution"]["buyer_payout"] = amt
848+
dispute_json["dispute_resolution"]["resolution"]["buyer_payout"] = \
849+
round(amt / float(100000000), 8)
841850
outputs.append({'value': amt,
842851
'address': buyer_address})
843852
if float(vendor_percentage) > 0:
844853
amt = int(float(vendor_percentage) * satoshis)
845-
dispute_json["dispute_resolution"]["resolution"]["vendor_payout"] = amt
854+
dispute_json["dispute_resolution"]["resolution"]["vendor_payout"] = \
855+
round(amt / float(100000000), 8)
846856
outputs.append({'value': amt,
847857
'address': vendor_address})
848858

@@ -902,32 +912,34 @@ def release_funds(self, order_id):
902912
the moderator has resolved the dispute and provided his signature.
903913
"""
904914
if os.path.exists(DATA_FOLDER + "purchases/in progress/" + order_id + ".json"):
905-
file_path = DATA_FOLDER + "purchases/trade receipts/" + order_id + ".json"
906-
outpoints = pickle.loads(self.db.purchases.get_outpoint(order_id))
915+
file_path = DATA_FOLDER + "purchases/in progress/" + order_id + ".json"
916+
outpoints = json.loads(self.db.purchases.get_outpoint(order_id))
907917
elif os.path.exists(DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"):
908918
file_path = DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"
909-
outpoints = pickle.loads(self.db.sales.get_outpoint(order_id))
919+
outpoints = json.loads(self.db.sales.get_outpoint(order_id))
910920

911921
with open(file_path, 'r') as filename:
912922
contract = json.load(filename, object_pairs_hook=OrderedDict)
913923

914-
vendor_address = contract["vendor_order_confirmation"]["invoice"]["payout"]["address"]
915-
buyer_address = contract["buyer_order"]["order"]["refund_address"]
916-
917924
outputs = []
918925

919-
outputs.append({'value': int(contract["dispute_resolution"]["resolution"]["moderator_fee"]),
926+
outputs.append({'value': int(float(contract["dispute_resolution"]
927+
["resolution"]["moderator_fee"]) * 100000000),
920928
'address': contract["dispute_resolution"]["resolution"]["moderator_address"]})
921929

922930
if "buyer_payout" in contract["dispute_resolution"]["resolution"]:
923-
outputs.append({'value': int(contract["dispute_resolution"]["resolution"]["buyer_payout"]),
931+
buyer_address = contract["buyer_order"]["order"]["refund_address"]
932+
outputs.append({'value': int(float(contract["dispute_resolution"]
933+
["resolution"]["buyer_payout"]) * 100000000),
924934
'address': buyer_address})
925935

926936
if "vendor_payout" in contract["dispute_resolution"]["resolution"]:
927-
outputs.append({'value': int(contract["dispute_resolution"]["resolution"]["vendor_payout"]),
937+
vendor_address = contract["vendor_order_confirmation"]["invoice"]["payout"]["address"]
938+
outputs.append({'value': int(float(contract["dispute_resolution"]
939+
["resolution"]["vendor_payout"]) * 100000000),
928940
'address': vendor_address})
929941

930-
tx = BitcoinTransaction.make_unsigned(outpoints, outputs)
942+
tx = BitcoinTransaction.make_unsigned(outpoints, outputs, testnet=self.protocol.multiplexer.testnet)
931943
chaincode = contract["buyer_order"]["order"]["payment"]["chaincode"]
932944
redeem_script = str(contract["buyer_order"]["order"]["payment"]["redeem_script"])
933945
masterkey = bitcointools.bip32_extract_key(KeyChain(self.db).bitcoin_master_privkey)
@@ -948,7 +960,7 @@ def release_funds(self, order_id):
948960

949961
tx.multisign(signatures, redeem_script)
950962
tx.broadcast(self.protocol.multiplexer.blockchain)
951-
self.log.info("Broadcasting payout tx %s to network" % tx.get_hash())
963+
self.log.info("broadcasting payout tx %s to network" % tx.get_hash())
952964

953965
if self.db.purchases.get_purchase(order_id) is not None:
954966
self.db.purchases.update_status(order_id, 6)
@@ -986,8 +998,6 @@ def get_result(result):
986998
pass
987999
return ret
9881000
except Exception:
989-
import traceback
990-
traceback.print_exc()
9911001
return None
9921002

9931003
if node_to_ask.ip is None:
@@ -1004,7 +1014,7 @@ def refund(self, order_id):
10041014
to the buyer with contain the signature.
10051015
"""
10061016
file_path = DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"
1007-
outpoints = pickle.loads(self.db.sales.get_outpoint(order_id))
1017+
outpoints = json.loads(self.db.sales.get_outpoint(order_id))
10081018

10091019
with open(file_path, 'r') as filename:
10101020
contract = json.load(filename, object_pairs_hook=OrderedDict)
@@ -1014,7 +1024,6 @@ def refund(self, order_id):
10141024
contract["buyer_order"]["order"]["id"]["pubkeys"]["guid"],
10151025
encoder=nacl.encoding.HexEncoder).to_curve25519_public_key()
10161026
refund_address = contract["buyer_order"]["order"]["refund_address"]
1017-
redeem_script = contract["buyer_order"]["order"]["payment"]["redeem_script"]
10181027
tx = BitcoinTransaction.make_unsigned(outpoints, refund_address,
10191028
testnet=self.protocol.multiplexer.testnet)
10201029
chaincode = contract["buyer_order"]["order"]["payment"]["chaincode"]
@@ -1024,14 +1033,25 @@ def refund(self, order_id):
10241033
refund_json = {"refund": {}}
10251034
refund_json["refund"]["order_id"] = order_id
10261035
if "moderator" in contract["buyer_order"]["order"]:
1036+
redeem_script = contract["buyer_order"]["order"]["payment"]["redeem_script"]
10271037
sigs = tx.create_signature(vendor_priv, redeem_script)
1028-
refund_json["refund"]["value"] = tx.get_out_value()
1038+
refund_json["refund"]["value"] = round(tx.get_out_value() / float(100000000), 8)
10291039
refund_json["refund"]["signature(s)"] = sigs
10301040
else:
10311041
tx.sign(vendor_priv)
10321042
tx.broadcast(self.protocol.multiplexer.blockchain)
1043+
self.log.info("broadcasting refund tx %s to network" % tx.get_hash())
10331044
refund_json["refund"]["txid"] = tx.get_hash()
10341045

1046+
contract["refund"] = refund_json["refund"]
1047+
self.db.sales.update_status(order_id, 7)
1048+
file_path = DATA_FOLDER + "store/contracts/trade receipts/" + order_id + ".json"
1049+
with open(file_path, 'w') as outfile:
1050+
outfile.write(json.dumps(contract, indent=4))
1051+
file_path = DATA_FOLDER + "store/contracts/in progress/" + order_id + ".json"
1052+
if os.path.exists(file_path):
1053+
os.remove(file_path)
1054+
10351055
def get_node(node_to_ask):
10361056
def parse_response(response):
10371057
if not response[0]:
@@ -1059,8 +1079,7 @@ def parse_response(response):
10591079
@staticmethod
10601080
def cache(file_to_save, filename):
10611081
"""
1062-
Saves the file to a cache folder if it doesn't already exist.
1082+
Saves the file to a cache folder override previous versions if any.
10631083
"""
1064-
if not os.path.isfile(DATA_FOLDER + "cache/" + filename):
1065-
with open(DATA_FOLDER + "cache/" + filename, 'wb') as outfile:
1066-
outfile.write(file_to_save)
1084+
with open(DATA_FOLDER + "cache/" + filename, 'wb') as outfile:
1085+
outfile.write(file_to_save)

0 commit comments

Comments
 (0)