def test_spend_p2sh_utxos(setup_tx_creation): #make a multisig address from 3 privs privs = [chr(x) * 32 + '\x01' for x in range(1, 4)] pubs = [btc.privkey_to_pubkey(binascii.hexlify(priv)) for priv in privs] script = btc.mk_multisig_script(pubs, 2) msig_addr = btc.scriptaddr(script, magicbyte=196) #pay into it wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet'] jm_single().bc_interface.sync_wallet(wallet) amount = 350000000 ins_full = wallet.select_utxos(0, amount) txid = make_sign_and_push(ins_full, wallet, amount, output_addr=msig_addr) assert txid #wait for mining time.sleep(4) #spend out; the input can be constructed from the txid of previous msig_in = txid + ":0" ins = [msig_in] #random output address and change addr output_addr = wallet.get_new_addr(1, 1) amount2 = amount - 50000 outs = [{'value': amount2, 'address': output_addr}] tx = btc.mktx(ins, outs) sigs = [] for priv in privs[:2]: sigs.append(btc.multisign(tx, 0, script, binascii.hexlify(priv))) tx = btc.apply_multisignatures(tx, 0, script, sigs) txid = jm_single().bc_interface.pushtx(tx) assert txid
def cosign(master_xpriv, recovery_package): raw_txs = recovery_package['txs'] print "Signing %d transactions" % len(raw_txs) for tx_index, tx_raw in enumerate(raw_txs): print "\nTransaction #", tx_index hex_tx = tx_raw['bytes'] tx = bitcoin.transaction.deserialize(unhexlify(hex_tx)) keypaths = tx_raw['input_paths'] keypairs = {} for p in keypaths: xpriv = seedlib.bip32_child(master_xpriv, p) priv = bitcoin.bip32_extract_key(xpriv) pub = bitcoin.privtopub(priv) keypairs[pub] = priv for i, inp in enumerate(tx['ins']): (sigs, pubkeys, redeemScript, M, N) = multisig.decode_multisig_script(inp['script']) for p in pubkeys: if p in keypairs: sig_new = bitcoin.multisign(hex_tx, i, redeemScript, keypairs[p]) sigs.append(sig_new) tx_new = bitcoin.apply_multisignatures( unhexlify(hex_tx), i, redeemScript, sigs) print "Signed transaction %d input %d" % (tx_index, i) # replace tx with newly signed transaction hex_tx = hexlify(tx_new) recovery_package['txs'][tx_index]['bytes'] = hex_tx return recovery_package
def seal(self): index = 0 for p in self.participants: for i, o in enumerate(p.tx_inputs): if o.signature: signatures = [o.signature] sealing_signature = self.sealer and self.sealer.signatures.get( o.outpoint_hash + ':' + str(o.outpoint_index), None ) if not sealing_signature: raise exceptions.MissingSealerSignature( o.outpoint_hash + ':' + str(o.outpoint_index) ) sealing_signature and signatures.append(sealing_signature) if o.is_segwit: assert o.script self._raw = bitcoin.apply_segwit_multisignatures( self._raw, index, o.script, signatures, nested=True ) else: self._raw = bitcoin.apply_multisignatures(self._raw, index, o.script, signatures) else: raise exceptions.MissingClientSignature index += 1 return self
def pushPayment(self): #compliantScript = bitcoin.mk_multisig_script([bitcoin.privtopub(self.privS), self.pubKeyClient], 2,2) # inversion!!!! compliantScript = bitcoin.mk_multisig_script([self.pubKeyClient, bitcoin.privtopub(self.privS)], 2,2) sigServer = bitcoin.multisign(self.lastpayment, 0, compliantScript, self.privS) signedPtx = bitcoin.apply_multisignatures(self.lastpayment, 0, compliantScript, [self.lastsig, sigServer]) print 'Broadcast the very last payment. Just got richer. Tx hash:', bitcoin.txhash(signedPtx) bitcoin.pushtx(signedPtx)
def main(): ''' Our main function. ''' # Two of the three keys. The missing one: # 3347122612f83ad80517dfef41c8b6a6400eef1949df732ce60a4e7c9d00ccb0 priv_keys = [ fix_priv('93d716a849c4c215b3049670ef7c1eba9b19c0452caf37b8f3383dcdd5577440'), fix_priv('7b389eb21fb17ef293be410009d436a495e0942005b1e6845f64429a5ff13420'), ] # Same order that Counterparty uses. Order should not matter. pub_0 = priv2pub(fix_priv('3347122612f83ad80517dfef41c8b6a6400eef1949df732ce60a4e7c9d00ccb0')) pub_1 = priv2pub(priv_keys[0]) pub_2 = priv2pub(priv_keys[1]) script_not_real = bitcoin.mk_multisig_script(pub_0, pub_1, pub_2, 2, 3) for priv in priv_keys: check_priv_key_looks_good(priv) deserialized_txn = deserialize_tx(get_raw_tx()) print('The raw transaction: {}\n'.format(get_raw_tx())) print('The decoded transaction:\n{}\n'.format(pprint.pformat(deserialized_txn))) bitcoind_script = '52410427db4059d24bab05df3f6bcc768fb01bd976b973f93e72cce2dfbfbed5a32056c9040a2c2ea4c10c812a54fed7ff2e6a917dbc843362d398f6ace4000fafa5c641043e12a6cb1c7c156f789110abf8397b714047414b5a32c742f17ccf93ff23bdf3128f946207086bcef012558240cd16182c741123e93ed18327c4cd6ebac668a94104e4168c172283c7dfaa85d2004f763a28bf6d0f1602fc1452ccec62a7c8a66e422af1410fbf24a47355ddc43dfe3491cb1b806574ccd1c434680466dcff926f0153ae' # You might want to use _ for something. signatures_A = [] signatures_B = [] for tx_index, tx_input in enumerate(deserialized_txn['ins']): assert tx_index == 0 # Only once! script_real = tx_input['script'] assert script_real == bitcoind_script assert len(script_real) == len(script_not_real) signatures_A.append(bitcoin.multisign(get_raw_tx(), tx_index, script_real, priv_keys[0])) signatures_B.append(bitcoin.multisign(get_raw_tx(), tx_index, script_real, priv_keys[1])) fully_signed_tx = get_raw_tx() fully_signed_tx_before = fully_signed_tx for tx_index, tx_input in enumerate(deserialized_txn['ins']): assert tx_index == 0 # Only once! fully_signed_tx = bitcoin.apply_multisignatures( \ fully_signed_tx, tx_index, script_real, signatures_A[tx_index], signatures_B[tx_index]) assert fully_signed_tx_before != fully_signed_tx # may the bytes be with you print('Fully signed: {}'.format(fully_signed_tx))
def pushPayment(self): #compliantScript = bitcoin.mk_multisig_script([bitcoin.privtopub(self.privS), self.pubKeyClient], 2,2) # inversion!!!! compliantScript = bitcoin.mk_multisig_script( [self.pubKeyClient, bitcoin.privtopub(self.privS)], 2, 2) sigServer = bitcoin.multisign(self.lastpayment, 0, compliantScript, self.privS) signedPtx = bitcoin.apply_multisignatures(self.lastpayment, 0, compliantScript, [self.lastsig, sigServer]) print 'Broadcast the very last payment. Just got richer. Tx hash:', bitcoin.txhash( signedPtx) bitcoin.pushtx(signedPtx)
def tx_sign_multisig(tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction TODO: move to virtualchain """ # sign in the right order. map all possible public keys to their private key privs = {} for pk in private_keys: pubk = keylib.ECPrivateKey(pk).public_key().to_hex() compressed_pubkey = keylib.key_formatting.compress(pubk) uncompressed_pubkey = keylib.key_formatting.decompress(pubk) privs[compressed_pubkey] = pk privs[uncompressed_pubkey] = pk m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys, sigs = [], [] for public_key in public_keys: if public_key not in privs: continue if len(used_keys) == m: break assert public_key not in used_keys, 'Tried to reuse key {}'.format( public_key) pk_str = privs[public_key] used_keys.append(public_key) sig = tx_make_input_signature(tx, idx, redeem_script, pk_str, hashcode) sigs.append(sig) assert len( used_keys) == m, 'Missing private keys (used {}, required {})'.format( len(used_keys), m) return bitcoin.apply_multisignatures(tx, idx, str(redeem_script), sigs)
def tx_sign_multisig(blockstack_tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction """ # sign in the right order privs = dict([ (virtualchain.BitcoinPrivateKey(str(pk_str)).public_key().to_hex(), str(pk_str)) for pk_str in private_keys ]) m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys = [] sigs = [] for ki in xrange(0, len(public_keys)): if not privs.has_key(public_keys[ki]): continue if len(used_keys) == m: break assert public_keys[ ki] not in used_keys, "Tried to reuse key %s" % public_keys[ki] pk_str = privs[public_keys[ki]] used_keys.append(public_keys[ki]) pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex() sig = bitcoin.multisign(blockstack_tx, idx, str(redeem_script), pk_hex, hashcode=hashcode) sigs.append(sig) assert len(used_keys) == m, "Missing private keys" return bitcoin.apply_multisignatures(blockstack_tx, idx, str(redeem_script), sigs)
def tx_sign_multisig(tx, idx, redeem_script, private_keys, hashcode=bitcoin.SIGHASH_ALL): """ Sign a p2sh multisig input. Return the signed transaction TODO: move to virtualchain """ # sign in the right order privs = { virtualchain.BitcoinPrivateKey(str(pk)).public_key().to_hex(): str(pk) for pk in private_keys } m, public_keys = virtualchain.parse_multisig_redeemscript( str(redeem_script)) used_keys, sigs = [], [] for public_key in public_keys: if public_key not in privs: continue if len(used_keys) == m: break assert public_key not in used_keys, 'Tried to reuse key {}'.format( public_key) pk_str = privs[public_key] used_keys.append(public_key) pk_hex = virtualchain.BitcoinPrivateKey(str(pk_str)).to_hex() sig = tx_make_input_signature(tx, idx, redeem_script, pk_str, hashcode) # sig = bitcoin.multisign(tx, idx, str(redeem_script), pk_hex, hashcode=hashcode) sigs.append(sig) assert len(used_keys) == m, 'Missing private keys' return bitcoin.apply_multisignatures(tx, idx, str(redeem_script), sigs)
def sign( self, privkey ): if( self.tx is None or self.redeemScript is None ): raise RuntimeError( "You have not entered in a tx to sign" ) try: pub = bitcoin.privtopub( privkey ) if pub not in self.pubs: pub = bitcoin.encode_pubkey( pub, 'hex_compressed' ) if pub not in self.pubs: raise ValueError( "Key doesn't match any public keys in redeemscript" ) stx = bitcoin.serialize( self.tx ) sigs = self.extract_sigs( self.tx ) sigs.append( bitcoin.multisign( stx, 0, self.redeemScript, privkey ) ) sigs = self.reorder_sigs( sigs ) stx = bitcoin.apply_multisignatures( stx, 0, self.redeemScript, sigs ) return stx, ( len(sigs) >= self.neededSigs ) except ValueError as e: raise RuntimeError( "Key Error", str( e ) ) except Exception as e: raise RuntimeError( "Unexpected Error", "Unexpected error formating key: " + str( e ) )
def cb(ec, history, order): if ec is not None: self.log.error("Error fetching history: %s", ec) # TODO: Send error message to GUI return unspent = [row[:4] for row in history if row[4] is None] # Send all unspent outputs (everything in the address) minus # the fee inputs = [] for row in unspent: assert len(row) == 4 inputs.append( str(row[0].encode('hex')) + ":" + str(row[1]) ) seller_signatures = [] print 'private key ', self.transport.settings['privkey'] for x in range(0, len(inputs)): ms = multisign( tx, x, script, self.transport.settings['privkey'] ) print 'seller sig', ms seller_signatures.append(ms) tx2 = apply_multisignatures( tx, 0, script, seller_signatures[0], msg['signatures'][0] ) print 'FINAL SCRIPT: %s' % tx2 print 'Sent', eligius_pushtx(tx2) self.send_to_client( None, { "type": "order_notify", "msg": "Funds were released for your sale." } )
def cb(ec, history, order): if ec is not None: self.log.error("Error fetching history: %s", ec) # TODO: Send error message to GUI return unspent = [row[:4] for row in history if row[4] is None] # Send all unspent outputs (everything in the address) minus # the fee inputs = [] for row in unspent: assert len(row) == 4 inputs.append( str(row[0].encode('hex')) + ":" + str(row[1])) seller_signatures = [] print 'private key ', self.transport.settings['privkey'] for x in range(0, len(inputs)): ms = multisign(tx, x, script, self.transport.settings['privkey']) print 'seller sig', ms seller_signatures.append(ms) tx2 = apply_multisignatures(tx, 0, script, seller_signatures[0], msg['signatures'][0]) print 'FINAL SCRIPT: %s' % tx2 print 'Sent', eligius_pushtx(tx2) self.send_to_client( None, { "type": "order_notify", "msg": "Funds were released for your sale." })
def accept_receipt(self, ws, blockchain, receipt_json=None): """ Process the final receipt sent over by the buyer. If valid, broadcast the transaction to the bitcoin network. """ self.ws = ws self.blockchain = blockchain try: if receipt_json: self.contract["buyer_receipt"] = json.loads( receipt_json, object_pairs_hook=OrderedDict) contract_dict = json.loads(json.dumps(self.contract, indent=4), object_pairs_hook=OrderedDict) del contract_dict["buyer_receipt"] contract_hash = digest(json.dumps(contract_dict, indent=4)).encode("hex") ref_hash = self.contract["buyer_receipt"]["receipt"]["ref_hash"] if ref_hash != contract_hash: raise Exception("Order number doesn't match") # The buyer may have sent over this whole contract, make sure the data we added wasn't manipulated. verify_key = self.keychain.signing_key.verify_key verify_key.verify( json.dumps( self.contract["vendor_order_confirmation"]["invoice"], indent=4), unhexlify( self.contract["vendor_order_confirmation"]["signature"])) order_id = self.contract["vendor_order_confirmation"]["invoice"][ "ref_hash"] outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id)) payout_address = self.contract["vendor_order_confirmation"][ "invoice"]["payout"]["address"] redeem_script = str(self.contract["buyer_order"]["order"] ["payment"]["redeem_script"]) for output in outpoints: del output["value"] value = self.contract["vendor_order_confirmation"]["invoice"][ "payout"]["value"] outs = [{'value': value, 'address': payout_address}] tx = bitcoin.mktx(outpoints, outs) chaincode = self.contract["buyer_order"]["order"]["payment"][ "chaincode"] masterkey_b = self.contract["buyer_order"]["order"]["id"][ "pubkeys"]["bitcoin"] buyer_key = derive_childkey(masterkey_b, chaincode) vendor_sigs = self.contract["vendor_order_confirmation"][ "invoice"]["payout"]["signature(s)"] buyer_sigs = self.contract["buyer_receipt"]["receipt"]["payout"][ "signature(s)"] for index in range(0, len(outpoints)): for s in vendor_sigs: if s["input_index"] == index: sig1 = str(s["signature"]) for s in buyer_sigs: if s["input_index"] == index: sig2 = str(s["signature"]) if bitcoin.verify_tx_input(tx, index, redeem_script, sig2, buyer_key): tx = bitcoin.apply_multisignatures(tx, index, str(redeem_script), sig1, sig2) else: raise Exception("Buyer sent invalid signature") d = defer.Deferred() def on_broadcast_complete(success): if success: d.callback(order_id) else: d.callback(False) def on_validate(success): def on_fetch(ec, result): if ec: # if it's not in the blockchain, let's try broadcasting it. self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx, cb=on_broadcast_complete) else: d.callback(order_id) if success: # broadcast anyway but don't wait for callback self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx) d.callback(order_id) else: # check to see if the tx is already in the blockchain self.blockchain.fetch_transaction( unhexlify(bitcoin.txhash(tx)), on_fetch) if "txid" in self.contract["buyer_receipt"]["receipt"]["payout"] \ and bitcoin.txhash(tx) == self.contract["buyer_receipt"]["receipt"]["payout"]["txid"]: # check mempool and blockchain for tx self.blockchain.validate(tx, cb=on_validate) else: # try broadcasting self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx, cb=on_broadcast_complete) # TODO: update db and file system if successful # TODO: broadcast over websocket return d except Exception: return defer.succeed(False)
def add_receipt(self, received, libbitcoin_client, feedback=None, quality=None, description=None, delivery_time=None, customer_service=None, review="", dispute=False, claim=None, payout=True): """ Add the final piece of the contract that appends the review and payout transaction. """ self.blockchain = libbitcoin_client receipt_json = { "buyer_receipt": { "receipt": { "ref_hash": digest(json.dumps(self.contract, indent=4)).encode("hex"), "listing": { "received": received, "listing_hash": self.contract["buyer_order"]["order"]["ref_hash"] }, "dispute": { "dispute": dispute } } } } if None not in (feedback, quality, description, delivery_time, customer_service): receipt_json["buyer_receipt"]["receipt"]["rating"] = {} receipt_json["buyer_receipt"]["receipt"]["rating"][ "feedback"] = feedback receipt_json["buyer_receipt"]["receipt"]["rating"][ "quality"] = quality receipt_json["buyer_receipt"]["receipt"]["rating"][ "description"] = description receipt_json["buyer_receipt"]["receipt"]["rating"][ "delivery_time"] = delivery_time receipt_json["buyer_receipt"]["receipt"]["rating"][ "customer_service"] = customer_service receipt_json["buyer_receipt"]["receipt"]["rating"][ "review"] = review if payout: order_id = self.contract["vendor_order_confirmation"]["invoice"][ "ref_hash"] outpoints = pickle.loads( self.db.Purchases().get_outpoint(order_id)) payout_address = self.contract["vendor_order_confirmation"][ "invoice"]["payout"]["address"] redeem_script = str(self.contract["buyer_order"]["order"] ["payment"]["redeem_script"]) for output in outpoints: del output["value"] value = self.contract["vendor_order_confirmation"]["invoice"][ "payout"]["value"] outs = [{'value': value, 'address': payout_address}] tx = bitcoin.mktx(outpoints, outs) signatures = [] chaincode = self.contract["buyer_order"]["order"]["payment"][ "chaincode"] masterkey_b = bitcoin.bip32_extract_key( self.keychain.bitcoin_master_privkey) buyer_priv = derive_childkey(masterkey_b, chaincode, bitcoin.MAINNET_PRIVATE) masterkey_v = self.contract["vendor_offer"]["listing"]["id"][ "pubkeys"]["bitcoin"] vendor_key = derive_childkey(masterkey_v, chaincode) valid_inputs = 0 for index in range(0, len(outpoints)): sig = bitcoin.multisign(tx, index, redeem_script, buyer_priv) signatures.append({"input_index": index, "signature": sig}) for s in self.contract["vendor_order_confirmation"]["invoice"][ "payout"]["signature(s)"]: if s["input_index"] == index: if bitcoin.verify_tx_input(tx, index, redeem_script, s["signature"], vendor_key): tx = bitcoin.apply_multisignatures( tx, index, str(redeem_script), sig, str(s["signature"])) valid_inputs += 1 receipt_json["buyer_receipt"]["receipt"]["payout"] = {} if valid_inputs == len(outpoints): self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx) receipt_json["buyer_receipt"]["receipt"]["payout"][ "txid"] = bitcoin.txhash(tx) receipt_json["buyer_receipt"]["receipt"]["payout"][ "signature(s)"] = signatures receipt_json["buyer_receipt"]["receipt"]["payout"]["value"] = value if claim: receipt_json["buyer_receipt"]["receipt"]["dispute"][ "claim"] = claim receipt = json.dumps(receipt_json["buyer_receipt"]["receipt"], indent=4) receipt_json["buyer_receipt"]["signature"] = \ self.keychain.signing_key.sign(receipt, encoder=nacl.encoding.HexEncoder)[:128] self.contract["buyer_receipt"] = receipt_json["buyer_receipt"]
def accept_receipt(self, ws, blockchain, receipt_json=None): """ Process the final receipt sent over by the buyer. If valid, broadcast the transaction to the bitcoin network. """ self.ws = ws self.blockchain = blockchain try: if receipt_json: self.contract["buyer_receipt"] = json.loads(receipt_json, object_pairs_hook=OrderedDict) contract_dict = json.loads(json.dumps(self.contract, indent=4), object_pairs_hook=OrderedDict) del contract_dict["buyer_receipt"] contract_hash = digest(json.dumps(contract_dict, indent=4)).encode("hex") ref_hash = self.contract["buyer_receipt"]["receipt"]["ref_hash"] if ref_hash != contract_hash: raise Exception("Order number doesn't match") # The buyer may have sent over this whole contract, make sure the data we added wasn't manipulated. verify_key = self.keychain.signing_key.verify_key verify_key.verify(json.dumps(self.contract["vendor_order_confirmation"]["invoice"], indent=4), unhexlify(self.contract["vendor_order_confirmation"]["signature"])) order_id = self.contract["vendor_order_confirmation"]["invoice"]["ref_hash"] outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id)) payout_address = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["address"] redeem_script = str(self.contract["buyer_order"]["order"]["payment"]["redeem_script"]) for output in outpoints: del output["value"] value = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["value"] outs = [{'value': value, 'address': payout_address}] tx = bitcoin.mktx(outpoints, outs) chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"] masterkey_b = self.contract["buyer_order"]["order"]["id"]["pubkeys"]["bitcoin"] buyer_key = derive_childkey(masterkey_b, chaincode) vendor_sigs = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["signature(s)"] buyer_sigs = self.contract["buyer_receipt"]["receipt"]["payout"]["signature(s)"] for index in range(0, len(outpoints)): for s in vendor_sigs: if s["input_index"] == index: sig1 = str(s["signature"]) for s in buyer_sigs: if s["input_index"] == index: sig2 = str(s["signature"]) if bitcoin.verify_tx_input(tx, index, redeem_script, sig2, buyer_key): tx = bitcoin.apply_multisignatures(tx, index, str(redeem_script), sig1, sig2) else: raise Exception("Buyer sent invalid signature") d = defer.Deferred() def on_broadcast_complete(success): if success: d.callback(order_id) else: d.callback(False) def on_validate(success): def on_fetch(ec, result): if ec: # if it's not in the blockchain, let's try broadcasting it. self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx, cb=on_broadcast_complete) else: d.callback(order_id) if success: # broadcast anyway but don't wait for callback self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx) d.callback(order_id) else: # check to see if the tx is already in the blockchain self.blockchain.fetch_transaction(unhexlify(bitcoin.txhash(tx)), on_fetch) if "txid" in self.contract["buyer_receipt"]["receipt"]["payout"] \ and bitcoin.txhash(tx) == self.contract["buyer_receipt"]["receipt"]["payout"]["txid"]: # check mempool and blockchain for tx self.blockchain.validate(tx, cb=on_validate) else: # try broadcasting self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx, cb=on_broadcast_complete) # TODO: update db and file system if successful # TODO: broadcast over websocket return d except Exception: return defer.succeed(False)
def add_receipt(self, received, libbitcoin_client, feedback=None, quality=None, description=None, delivery_time=None, customer_service=None, review="", dispute=False, claim=None, payout=True): """ Add the final piece of the contract that appends the review and payout transaction. """ self.blockchain = libbitcoin_client receipt_json = { "buyer_receipt": { "receipt": { "ref_hash": digest(json.dumps(self.contract, indent=4)).encode("hex"), "listing": { "received": received, "listing_hash": self.contract["buyer_order"]["order"]["ref_hash"] }, "dispute": { "dispute": dispute } } } } if None not in (feedback, quality, description, delivery_time, customer_service): receipt_json["buyer_receipt"]["receipt"]["rating"] = {} receipt_json["buyer_receipt"]["receipt"]["rating"]["feedback"] = feedback receipt_json["buyer_receipt"]["receipt"]["rating"]["quality"] = quality receipt_json["buyer_receipt"]["receipt"]["rating"]["description"] = description receipt_json["buyer_receipt"]["receipt"]["rating"]["delivery_time"] = delivery_time receipt_json["buyer_receipt"]["receipt"]["rating"]["customer_service"] = customer_service receipt_json["buyer_receipt"]["receipt"]["rating"]["review"] = review if payout: order_id = self.contract["vendor_order_confirmation"]["invoice"]["ref_hash"] outpoints = pickle.loads(self.db.Purchases().get_outpoint(order_id)) payout_address = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["address"] redeem_script = str(self.contract["buyer_order"]["order"]["payment"]["redeem_script"]) for output in outpoints: del output["value"] value = self.contract["vendor_order_confirmation"]["invoice"]["payout"]["value"] outs = [{'value': value, 'address': payout_address}] tx = bitcoin.mktx(outpoints, outs) signatures = [] chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"] masterkey_b = bitcoin.bip32_extract_key(self.keychain.bitcoin_master_privkey) buyer_priv = derive_childkey(masterkey_b, chaincode, bitcoin.MAINNET_PRIVATE) masterkey_v = self.contract["vendor_offer"]["listing"]["id"]["pubkeys"]["bitcoin"] vendor_key = derive_childkey(masterkey_v, chaincode) valid_inputs = 0 for index in range(0, len(outpoints)): sig = bitcoin.multisign(tx, index, redeem_script, buyer_priv) signatures.append({"input_index": index, "signature": sig}) for s in self.contract["vendor_order_confirmation"]["invoice"]["payout"]["signature(s)"]: if s["input_index"] == index: if bitcoin.verify_tx_input(tx, index, redeem_script, s["signature"], vendor_key): tx = bitcoin.apply_multisignatures(tx, index, str(redeem_script), sig, str(s["signature"])) valid_inputs += 1 receipt_json["buyer_receipt"]["receipt"]["payout"] = {} if valid_inputs == len(outpoints): self.log.info("Broadcasting payout tx %s to network" % bitcoin.txhash(tx)) self.blockchain.broadcast(tx) receipt_json["buyer_receipt"]["receipt"]["payout"]["txid"] = bitcoin.txhash(tx) receipt_json["buyer_receipt"]["receipt"]["payout"]["signature(s)"] = signatures receipt_json["buyer_receipt"]["receipt"]["payout"]["value"] = value if claim: receipt_json["buyer_receipt"]["receipt"]["dispute"]["claim"] = claim receipt = json.dumps(receipt_json["buyer_receipt"]["receipt"], indent=4) receipt_json["buyer_receipt"]["signature"] = \ self.keychain.signing_key.sign(receipt, encoder=nacl.encoding.HexEncoder)[:128] self.contract["buyer_receipt"] = receipt_json["buyer_receipt"]