|
1 | 1 | __author__ = 'chris' |
2 | 2 |
|
| 3 | +import bitcointools |
3 | 4 | import json |
| 5 | +import os |
| 6 | +import pickle |
4 | 7 | import nacl.signing |
5 | 8 | import nacl.utils |
6 | 9 | import nacl.encoding |
7 | 10 | import nacl.hash |
| 11 | +from binascii import unhexlify |
8 | 12 | from collections import OrderedDict |
| 13 | +from config import DATA_FOLDER |
9 | 14 | from interfaces import MessageProcessor, BroadcastListener, MessageListener, NotificationListener |
10 | 15 | from keys.bip32utils import derive_childkey |
| 16 | +from keys.keychain import KeyChain |
11 | 17 | from log import Logger |
12 | 18 | from market.contracts import Contract |
13 | 19 | from market.moderation import process_dispute, close_dispute |
14 | 20 | from market.profile import Profile |
| 21 | +from market.transactions import BitcoinTransaction |
15 | 22 | from nacl.public import PublicKey, Box |
16 | 23 | from net.rpcudp import RPCProtocol |
17 | 24 | from protos.message import GET_CONTRACT, GET_IMAGE, GET_PROFILE, GET_LISTINGS, GET_USER_METADATA,\ |
@@ -346,6 +353,73 @@ def rpc_get_ratings(self, sender, listing_hash=None): |
346 | 353 | self.log.warning("could not load ratings for contract %s" % a) |
347 | 354 | return None |
348 | 355 |
|
| 356 | + def rpc_refund(self, sender, pubkey, encrypted): |
| 357 | + try: |
| 358 | + box = Box(self.signing_key.to_curve25519_private_key(), PublicKey(pubkey)) |
| 359 | + refund = box.decrypt(encrypted) |
| 360 | + refund_json = json.loads(refund, object_pairs_hook=OrderedDict) |
| 361 | + order_id = refund_json["order_id"] |
| 362 | + |
| 363 | + file_path = DATA_FOLDER + "purchases/in progress/" + order_id + ".json" |
| 364 | + with open(file_path, 'r') as filename: |
| 365 | + order = json.load(filename, object_pairs_hook=OrderedDict) |
| 366 | + order["refund"] = refund_json["refund"] |
| 367 | + |
| 368 | + if "txid" not in refund_json: |
| 369 | + outpoints = pickle.loads(self.db.sales.get_outpoint(order_id)) |
| 370 | + refund_address = order["buyer_order"]["order"]["refund_address"] |
| 371 | + redeem_script = order["buyer_order"]["order"]["payment"]["redeem_script"] |
| 372 | + tx = BitcoinTransaction.make_unsigned(outpoints, refund_address, |
| 373 | + testnet=self.multiplexer.testnet, |
| 374 | + out_value=long(refund_json["refund"]["value"])) |
| 375 | + chaincode = order["buyer_order"]["order"]["payment"]["chaincode"] |
| 376 | + masterkey_b = bitcointools.bip32_extract_key(KeyChain(self.db).bitcoin_master_privkey) |
| 377 | + buyer_priv = derive_childkey(masterkey_b, chaincode, bitcointools.MAINNET_PRIVATE) |
| 378 | + buyer_sigs = tx.create_signature(buyer_priv, redeem_script) |
| 379 | + vendor_sigs = refund_json["refund"]["signature(s)"] |
| 380 | + |
| 381 | + signatures = [] |
| 382 | + for i in range(len(outpoints)): |
| 383 | + for vendor_sig in vendor_sigs: |
| 384 | + if vendor_sig["index"] == i: |
| 385 | + v_signature = vendor_sig["signature"] |
| 386 | + for buyer_sig in buyer_sigs: |
| 387 | + if buyer_sig["index"] == i: |
| 388 | + b_signature = buyer_sig["signature"] |
| 389 | + signature_obj = {"index": i, "signatures": [b_signature, v_signature]} |
| 390 | + signatures.append(signature_obj) |
| 391 | + |
| 392 | + tx.multisign(signatures, redeem_script) |
| 393 | + tx.broadcast(self.multiplexer.blockchain) |
| 394 | + self.log.info("Broadcasting refund tx %s to network" % tx.get_hash()) |
| 395 | + |
| 396 | + self.db.sales.update_status(order_id, 7) |
| 397 | + file_path = DATA_FOLDER + "purchases/trade receipts/" + order_id + ".json" |
| 398 | + with open(file_path, 'w') as outfile: |
| 399 | + outfile.write(json.dumps(order, indent=4)) |
| 400 | + file_path = DATA_FOLDER + "purchases/in progress/" + order_id + ".json" |
| 401 | + if os.path.exists(file_path): |
| 402 | + os.remove(file_path) |
| 403 | + |
| 404 | + title = order["vendor_offer"]["listing"]["item"]["title"] |
| 405 | + if "image_hashes" in order["vendor_offer"]["listing"]["item"]: |
| 406 | + image_hash = unhexlify(order["vendor_offer"]["listing"]["item"]["image_hashes"][0]) |
| 407 | + else: |
| 408 | + image_hash = "" |
| 409 | + buyer_guid = self.contract["buyer_order"]["order"]["id"]["guid"] |
| 410 | + if "blockchain_id" in self.contract["buyer_order"]["order"]["id"]: |
| 411 | + handle = self.contract["buyer_order"]["order"]["id"]["blockchain_id"] |
| 412 | + else: |
| 413 | + handle = "" |
| 414 | + self.get_notification_listener().notify(buyer_guid, handle, "refund", order_id, title, image_hash) |
| 415 | + |
| 416 | + self.router.addContact(sender) |
| 417 | + self.log.info("order %s refunded by vendor" % refund_json["refund"]["order_id"]) |
| 418 | + return ["True"] |
| 419 | + except Exception: |
| 420 | + self.log.error("unable to parse refund message from %s" % sender) |
| 421 | + return ["False"] |
| 422 | + |
349 | 423 | def callGetContract(self, nodeToAsk, contract_hash): |
350 | 424 | d = self.get_contract(nodeToAsk, contract_hash) |
351 | 425 | return d.addCallback(self.handleCallResponse, nodeToAsk) |
@@ -444,6 +518,7 @@ def get_notification_listener(self): |
444 | 518 | return listener |
445 | 519 | except DoesNotImplement: |
446 | 520 | pass |
| 521 | + |
447 | 522 | def get_message_listener(self): |
448 | 523 | for listener in self.listeners: |
449 | 524 | try: |
|
0 commit comments