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 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 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 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 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 cb(ec, history, order): self.log.debug('Callback for history %s', history) private_key = self.get_signing_key(seller['seller_contract_id']) if ec is not None: self.log.error("Error fetching history: %s", ec) # TODO: Send error message to GUI return # Create unsigned transaction unspent = [row[:4] for row in history if row[4] is None] # Send all unspent outputs (everything in the address) minus # the fee total_amount = 0 inputs = [] for row in unspent: assert len(row) == 4 inputs.append( str(row[0].encode('hex')) + ":" + str(row[1]) ) value = row[3] total_amount += value # Constrain fee so we don't get negative amount to send fee = min(total_amount, 10000) send_amount = total_amount - fee payment_output = order['payment_address'] tx = mktx( inputs, [str(payment_output) + ":" + str(send_amount)] ) # Sign all the inputs signatures = [] for x in range(0, len(inputs)): ms = multisign(tx, x, script, private_key) signatures.append(ms) self.log.debug('Merchant TX Signatures: %s', signatures) order['merchant_tx'] = tx order['merchant_script'] = script order['buyer_order_id'] = buyer['buyer_order_id'] order['merchant_sigs'] = signatures self.transport.send(order, bid_data_json['Buyer']['buyer_GUID'])
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 main(): locktime = args.locktime privkey = args.privkey address = args.address script = getRedeemScript(locktime, privkey) ins, balance = getBalance(scriptaddr(script, 50)) len_inputs = len(ins) tx = '' if balance > 0 and check_addr(address) and is_privkey(privkey): # Fee fee = round(base_fee + fee_per_input * len_inputs, 8) # Outputs out_value = int((balance - fee) * COIN) outs = [{'address' : address, 'value' : out_value}] # Make unsigned transaction tx = mktx(ins, outs) # Append nLockTime and reset nSequence unpacked = deserialize(tx) unpacked['locktime'] = locktime for i in range(len_inputs): unpacked['ins'][i]['sequence'] = 0 tx = serialize(unpacked) # get all signatures sigs = [] for i in range(len_inputs): sigs.append(multisign(tx, i, script, privkey)) # sign inputs unpacked = deserialize(tx) for i in range(len_inputs): unpacked['ins'][i]['script'] = getLen(sigs[i]) + sigs[i] unpacked['ins'][i]['script'] += getLen(script) + script tx = serialize(unpacked) print('> BALANCE (%s): %f' % (scriptaddr(script, 50), balance)) if len(tx) > 0: txid = broadcast(tx) print('> RESPONSE: %s' % txid.text)
def cb(ec, history, order): settings = self.market.get_settings() private_key = settings.get('privkey') if ec is not None: self.log.error("Error fetching history: %s", ec) # TODO: Send error message to GUI return # Create unsigned transaction unspent = [row[:4] for row in history if row[4] is None] # Send all unspent outputs (everything in the address) minus # the fee total_amount = 0 inputs = [] for row in unspent: assert len(row) == 4 inputs.append( str(row[0].encode('hex')) + ":" + str(row[1]) ) value = row[3] total_amount += value # Constrain fee so we don't get negative amount to send fee = min(total_amount, 10000) send_amount = total_amount - fee payment_output = order['payment_address'] tx = mktx( inputs, [str(payment_output) + ":" + str(send_amount)] ) signatures = [] for x in range(0, len(inputs)): ms = multisign(tx, x, script, private_key) signatures.append(ms) print signatures self.market.release_funds_to_merchant( buyer['buyer_order_id'], tx, script, signatures, order.get('merchant') )
def cb(ec, history, order): settings = self.market.get_settings() private_key = settings.get('privkey') if ec is not None: self.log.error("Error fetching history: %s", ec) # TODO: Send error message to GUI return # Create unsigned transaction unspent = [row[:4] for row in history if row[4] is None] # Send all unspent outputs (everything in the address) minus # the fee total_amount = 0 inputs = [] for row in unspent: assert len(row) == 4 inputs.append( str(row[0].encode('hex')) + ":" + str(row[1])) value = row[3] total_amount += value # Constrain fee so we don't get negative amount to send fee = min(total_amount, 10000) send_amount = total_amount - fee payment_output = order['payment_address'] tx = mktx(inputs, [str(payment_output) + ":" + str(send_amount)]) signatures = [] for x in range(0, len(inputs)): ms = multisign(tx, x, script, private_key) signatures.append(ms) print signatures self.market.release_funds_to_merchant(buyer['buyer_order_id'], tx, script, signatures, order.get('merchant'))
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 add_order_confirmation(self, payout_address, comments=None, shipper=None, tracking_number=None, est_delivery=None, url=None, password=None): """ Add the vendor's order confirmation to the contract. """ if not self.testnet and not (payout_address[:1] == "1" or payout_address[:1] == "3"): raise Exception("Bitcoin address is not a mainnet address") elif self.testnet and not \ (payout_address[:1] == "n" or payout_address[:1] == "m" or payout_address[:1] == "2"): raise Exception("Bitcoin address is not a testnet address") try: bitcoin.b58check_to_hex(payout_address) except AssertionError: raise Exception("Invalid Bitcoin address") conf_json = { "vendor_order_confirmation": { "invoice": { "ref_hash": digest(json.dumps(self.contract, indent=4)).encode("hex") } } } if self.contract["vendor_offer"]["listing"]["metadata"]["category"] == "physical good": shipping = {"shipper": shipper, "tracking_number": tracking_number, "est_delivery": est_delivery} conf_json["vendor_order_confirmation"]["invoice"]["shipping"] = shipping elif self.contract["vendor_offer"]["listing"]["metadata"]["category"] == "digital good": content_source = {"url": url, "password": password} conf_json["vendor_order_confirmation"]["invoice"]["content_source"] = content_source if comments: conf_json["vendor_order_confirmation"]["invoice"]["comments"] = comments confirmation = json.dumps(conf_json["vendor_order_confirmation"]["invoice"], indent=4) conf_json["vendor_order_confirmation"]["signature"] = \ self.keychain.signing_key.sign(confirmation, encoder=nacl.encoding.HexEncoder)[:128] order_id = digest(json.dumps(self.contract, indent=4)).encode("hex") # apply signatures outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id)) redeem_script = self.contract["buyer_order"]["order"]["payment"]["redeem_script"] value = 0 for output in outpoints: value += output["value"] del output["value"] value -= TRANSACTION_FEE outs = [{'value': value, 'address': payout_address}] tx = bitcoin.mktx(outpoints, outs) signatures = [] chaincode = self.contract["buyer_order"]["order"]["payment"]["chaincode"] masterkey_v = bitcoin.bip32_extract_key(self.keychain.bitcoin_master_privkey) vendor_priv = derive_childkey(masterkey_v, chaincode, bitcoin.MAINNET_PRIVATE) for index in range(0, len(outpoints)): sig = bitcoin.multisign(tx, index, redeem_script, vendor_priv) signatures.append({"input_index": index, "signature": sig}) conf_json["vendor_order_confirmation"]["invoice"]["payout"] = {} conf_json["vendor_order_confirmation"]["invoice"]["payout"]["address"] = payout_address conf_json["vendor_order_confirmation"]["invoice"]["payout"]["value"] = value conf_json["vendor_order_confirmation"]["invoice"]["payout"]["signature(s)"] = signatures self.contract["vendor_order_confirmation"] = conf_json["vendor_order_confirmation"] self.db.Sales().update_status(order_id, 2) file_path = DATA_FOLDER + "store/listings/in progress/" + order_id + ".json" with open(file_path, 'w') as outfile: outfile.write(json.dumps(self.contract, indent=4))
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 add_order_confirmation(self, payout_address, comments=None, shipper=None, tracking_number=None, est_delivery=None, url=None, password=None): """ Add the vendor's order confirmation to the contract. """ if not self.testnet and not (payout_address[:1] == "1" or payout_address[:1] == "3"): raise Exception("Bitcoin address is not a mainnet address") elif self.testnet and not \ (payout_address[:1] == "n" or payout_address[:1] == "m" or payout_address[:1] == "2"): raise Exception("Bitcoin address is not a testnet address") try: bitcoin.b58check_to_hex(payout_address) except AssertionError: raise Exception("Invalid Bitcoin address") conf_json = { "vendor_order_confirmation": { "invoice": { "ref_hash": digest(json.dumps(self.contract, indent=4)).encode("hex") } } } if self.contract["vendor_offer"]["listing"]["metadata"][ "category"] == "physical good": shipping = { "shipper": shipper, "tracking_number": tracking_number, "est_delivery": est_delivery } conf_json["vendor_order_confirmation"]["invoice"][ "shipping"] = shipping elif self.contract["vendor_offer"]["listing"]["metadata"][ "category"] == "digital good": content_source = {"url": url, "password": password} conf_json["vendor_order_confirmation"]["invoice"][ "content_source"] = content_source if comments: conf_json["vendor_order_confirmation"]["invoice"][ "comments"] = comments confirmation = json.dumps( conf_json["vendor_order_confirmation"]["invoice"], indent=4) conf_json["vendor_order_confirmation"]["signature"] = \ self.keychain.signing_key.sign(confirmation, encoder=nacl.encoding.HexEncoder)[:128] order_id = digest(json.dumps(self.contract, indent=4)).encode("hex") # apply signatures outpoints = pickle.loads(self.db.Sales().get_outpoint(order_id)) redeem_script = self.contract["buyer_order"]["order"]["payment"][ "redeem_script"] value = 0 for output in outpoints: value += output["value"] del output["value"] value -= TRANSACTION_FEE outs = [{'value': value, 'address': payout_address}] tx = bitcoin.mktx(outpoints, outs) signatures = [] chaincode = self.contract["buyer_order"]["order"]["payment"][ "chaincode"] masterkey_v = bitcoin.bip32_extract_key( self.keychain.bitcoin_master_privkey) vendor_priv = derive_childkey(masterkey_v, chaincode, bitcoin.MAINNET_PRIVATE) for index in range(0, len(outpoints)): sig = bitcoin.multisign(tx, index, redeem_script, vendor_priv) signatures.append({"input_index": index, "signature": sig}) conf_json["vendor_order_confirmation"]["invoice"]["payout"] = {} conf_json["vendor_order_confirmation"]["invoice"]["payout"][ "address"] = payout_address conf_json["vendor_order_confirmation"]["invoice"]["payout"][ "value"] = value conf_json["vendor_order_confirmation"]["invoice"]["payout"][ "signature(s)"] = signatures self.contract["vendor_order_confirmation"] = conf_json[ "vendor_order_confirmation"] self.db.Sales().update_status(order_id, 2) file_path = DATA_FOLDER + "store/listings/in progress/" + order_id + ".json" with open(file_path, 'w') as outfile: outfile.write(json.dumps(self.contract, indent=4))
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"]