def broadcast_tx(self, tx): """ Send the tx to half our peers, wait for half of the remainder to announce the tx before calling back True. """ def on_peer_anncounce(txid): self.pending_txs[txid][0] += 1 if self.pending_txs[txid][0] >= self.pending_txs[txid][1] / 2: if self.pending_txs[txid][3].active(): self.pending_txs[txid][3].cancel() self.pending_txs[txid][2].callback(True) d = defer.Deferred() self.inventory[bitcoin.txhash(tx)] = tx inv_packet = inv("TX", bitcoin.txhash(tx)) self.bloom_filter.insert(bitcoin.bin_txhash(tx)) self.pending_txs[bitcoin.txhash(tx)] = [ 0, len(self.peers) / 2, d, reactor.callLater(10, d.callback, False) ] for peer in self.peers[len(self.peers) / 2:]: peer.protocol.update_filter() peer.protocol.add_inv_callback(bitcoin.txhash(tx), on_peer_anncounce) for peer in self.peers[:len(self.peers) / 2]: peer.protocol.send_message(message(inv_packet, self.params)) return d
def segwit_txhash(tx): if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx): tx = changebase(tx, 16, 256) tx_hash = txhash(tx) tx = strip_witness_data(tx) tx_id = txhash(tx) return {'hash': tx_hash, 'txid': tx_id}
def push(self, txd): tx = btc.serialize(txd) log.debug('\n' + tx) log.debug('txid = ' + btc.txhash(tx)) # TODO send to a random maker or push myself # TODO need to check whether the other party sent it # self.msgchan.push_tx(self.active_orders.keys()[0], txhex) pushed = jm_single().bc_interface.pushtx(tx) if pushed[0]: self.txid = btc.txhash(tx) else: log.debug('unable to pushtx, reason: '+str(pushed[1])) return pushed[0]
def push(self): tx = btc.serialize(self.latest_tx) log.debug('\n' + tx) self.txid = btc.txhash(tx) log.debug('txid = ' + self.txid) tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast') if tx_broadcast == 'self': pushed = jm_single().bc_interface.pushtx(tx) elif tx_broadcast in ['random-peer', 'not-self']: n = len(self.active_orders) if tx_broadcast == 'random-peer': i = random.randrange(n + 1) else: i = random.randrange(n) if i == n: pushed = jm_single().bc_interface.pushtx(tx) else: self.msgchan.push_tx(self.active_orders.keys()[i], tx) pushed = True elif tx_broadcast == 'random-maker': crow = self.db.execute( 'SELECT DISTINCT counterparty FROM orderbook ORDER BY ' + 'RANDOM() LIMIT 1;').fetchone() counterparty = crow['counterparty'] log.debug('pushing tx to ' + counterparty) self.msgchan.push_tx(counterparty, tx) pushed = True if not pushed: log.debug('unable to pushtx') return pushed
def make_sign_and_push(ins_full, wallet, amount, output_addr=None, change_addr=None, hashcode=btc.SIGHASH_ALL): total = sum(x['value'] for x in ins_full.values()) ins = ins_full.keys() #random output address and change addr output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr outs = [{'value': amount, 'address': output_addr}, {'value': total - amount - 100000, 'address': change_addr}] tx = btc.mktx(ins, outs) de_tx = btc.deserialize(tx) for index, ins in enumerate(de_tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) addr = ins_full[utxo]['address'] priv = wallet.get_key_from_addr(addr) if index % 2: priv = binascii.unhexlify(priv) tx = btc.sign(tx, index, priv, hashcode=hashcode) #pushtx returns False on any error print btc.deserialize(tx) push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: return btc.txhash(tx) else: return False
def push(self): tx = btc.serialize(self.latest_tx) log.debug('\n' + tx) self.txid = btc.txhash(tx) log.debug('txid = ' + self.txid) tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast') if tx_broadcast == 'self': pushed = jm_single().bc_interface.pushtx(tx) elif tx_broadcast in ['random-peer', 'not-self']: n = len(self.active_orders) if tx_broadcast == 'random-peer': i = random.randrange(n + 1) else: i = random.randrange(n) if i == n: pushed = jm_single().bc_interface.pushtx(tx) else: self.msgchan.push_tx(self.active_orders.keys()[i], tx) pushed = True elif tx_broadcast == 'random-maker': crow = self.db.execute( 'SELECT DISTINCT counterparty FROM orderbook ORDER BY ' + 'RANDOM() LIMIT 1;' ).fetchone() counterparty = crow['counterparty'] log.debug('pushing tx to ' + counterparty) self.msgchan.push_tx(counterparty, tx) pushed = True if not pushed: log.debug('unable to pushtx') return pushed
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 make_sign_and_push( ins_full, wallet, amount, output_addr=None, change_addr=None, hashcode=btc.SIGHASH_ALL, estimate_fee=False ): """Utility function for easily building transactions from wallets """ total = sum(x["value"] for x in ins_full.values()) ins = ins_full.keys() # random output address and change addr output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000 outs = [{"value": amount, "address": output_addr}, {"value": total - amount - fee_est, "address": change_addr}] tx = btc.mktx(ins, outs) de_tx = btc.deserialize(tx) for index, ins in enumerate(de_tx["ins"]): utxo = ins["outpoint"]["hash"] + ":" + str(ins["outpoint"]["index"]) addr = ins_full[utxo]["address"] priv = wallet.get_key_from_addr(addr) if index % 2: priv = binascii.unhexlify(priv) tx = btc.sign(tx, index, priv, hashcode=hashcode) # pushtx returns False on any error print btc.deserialize(tx) push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: return btc.txhash(tx) else: return False
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)
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)
def on_push_tx(self, nick, txhex): log.debug('received txhex from ' + nick + ' to push\n' + txhex) pushed = jm_single().bc_interface.pushtx(txhex) if pushed: log.debug('pushed tx ' + btc.txhash(txhex)) else: log.debug('failed to push tx sent by taker') self.msgchan.send_error(nick, 'Unable to push tx')
def push_tx(self, tx_signed, raw=False): # push transactions requires a post to blockr path = '/tx/push' url = self._url + path payload = {'hex': tx_signed} response = requests.post(url, data=payload) if raw: return response return bitcoin.txhash(tx_signed)
def on_peer_announce(tx): txhash = bitcoin.txhash(bitcoin.serialize(tx["tx"])) if txhash in self.subscriptions[address][0] and self.subscriptions[address][0][txhash][0] != "complete": self.subscriptions[address][0][txhash][0] += 1 if self.subscriptions[address][0][txhash][0] >= self.subscriptions[address][0][txhash][1]: self.subscriptions[address][0][txhash][0] = "complete" self.subscriptions[address][1](tx["tx"]) elif txhash not in self.subscriptions[address][0]: self.subscriptions[address][0][txhash] = [1, len(self.peers)/2]
def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False): """Send coins directly from one mixdepth to one destination address; does not need IRC. Sweep as for normal sendpayment (set amount=0). """ #Sanity checks; note destaddr format is carefully checked in startup assert isinstance(mixdepth, int) assert mixdepth >= 0 assert isinstance(amount, int) assert amount >= 0 and amount < 10000000000 assert isinstance(wallet, Wallet) import bitcoin as btc from pprint import pformat if amount == 0: utxos = wallet.get_utxos_by_mixdepth()[mixdepth] if utxos == {}: log.error("There are no utxos in mixdepth: " + str(mixdepth) + ", quitting.") return total_inputs_val = sum([va['value'] for u, va in utxos.iteritems()]) fee_est = estimate_tx_fee(len(utxos), 1) outs = [{"address": destaddr, "value": total_inputs_val - fee_est}] else: initial_fee_est = estimate_tx_fee(8, 2) #8 inputs to be conservative utxos = wallet.select_utxos(mixdepth, amount + initial_fee_est) if len(utxos) < 8: fee_est = estimate_tx_fee(len(utxos), 2) else: fee_est = initial_fee_est total_inputs_val = sum([va['value'] for u, va in utxos.iteritems()]) changeval = total_inputs_val - fee_est - amount outs = [{"value": amount, "address": destaddr}] change_addr = wallet.get_internal_addr(mixdepth) outs.append({"value": changeval, "address": change_addr}) #Now ready to construct transaction log.info("Using a fee of : " + str(fee_est) + " satoshis.") if amount != 0: log.info("Using a change value of: " + str(changeval) + " satoshis.") tx = btc.mktx(utxos.keys(), outs) stx = btc.deserialize(tx) for index, ins in enumerate(stx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) addr = utxos[utxo]['address'] tx = btc.sign(tx, index, wallet.get_key_from_addr(addr)) txsigned = btc.deserialize(tx) log.info("Got signed transaction:\n") log.info(tx + "\n") log.info(pformat(txsigned)) if not answeryes: if raw_input( 'Would you like to push to the network? (y/n):')[0] != 'y': log.info("You chose not to broadcast the transaction, quitting.") return jm_single().bc_interface.pushtx(tx) txid = btc.txhash(tx) log.info("Transaction sent: " + txid + ", shutting down")
def push(self, tx): """ Args: tx: hex of signed transaction Returns: pushed transaction """ self._service.push_tx(tx) return bitcoin.txhash(tx)
def push(self, txd): tx = btc.serialize(txd) debug('\n' + tx) debug('txid = ' + btc.txhash(tx)) #TODO send to a random maker or push myself #TODO need to check whether the other party sent it #self.msgchan.push_tx(self.active_orders.keys()[0], txhex) self.txid = common.bc_interface.pushtx(tx) if self.txid == None: debug('unable to pushtx')
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)
def on_tx_received(self, address_version, address_hash, height, block_hash, tx): """ Fire when the libbitcoin server tells us we received a payment to this funding address. While unlikely, a user may send multiple transactions to the funding address to reach the funding level. We need to keep a running balance and increment it when a new transaction is received. If the contract is fully funded, we push a notification to the websockets. """ # decode the transaction transaction = deserialize(tx.encode("hex")) # get the amount (in satoshi) the user is expected to pay amount_to_pay = int(float(self.contract["buyer_order"]["order"]["payment"]["amount"]) * 100000000) if tx not in self.received_txs: # make sure we aren't parsing the same tx twice. output_script = 'a914' + digest(unhexlify( self.contract["buyer_order"]["order"]["payment"]["redeem_script"])).encode("hex") + '87' for output in transaction["outs"]: if output["script"] == output_script: self.amount_funded += output["value"] if tx not in self.received_txs: self.received_txs.append(tx) self.outpoints.append({"output": bitcoin.txhash(tx.encode("hex")) + ":" + str(output["index"]), "value": output["value"]}) if self.amount_funded >= amount_to_pay: # if fully funded self.timeout.cancel() self.blockchain.unsubscribe_address( self.contract["buyer_order"]["order"]["payment"]["address"], self.on_tx_received) order_id = digest(json.dumps(self.contract, indent=4)).encode("hex") if self.is_purchase: message_json = { "payment_received": { "address": self.contract["buyer_order"]["order"]["payment"]["address"], "order_id": order_id } } # update the db self.db.Purchases().update_status(order_id, 1) self.db.Purchases().update_outpoint(order_id, pickle.dumps(self.outpoints)) self.log.info("Payment for order id %s successfully broadcast to network." % order_id) else: message_json = { "new_order": { "order_id": order_id, "title": self.contract["vendor_offer"]["listing"]["item"]["title"] } } self.db.Sales().update_status(order_id, 1) self.db.Sales().update_outpoint(order_id, pickle.dumps(self.outpoints)) self.log.info("Received new order %s" % order_id) # push the message over websockets self.ws.push(json.dumps(message_json, indent=4))
def tx_verify( tx, tx_hash ): """ Confirm that a bitcoin transaction has the given hash. """ tx_serialized = tx_serialize( tx ) tx_candidate_hash = bitcoin.txhash( tx_serialized ) # tx_reversed_bin_hash = pybitcoin.bin_double_sha256( binascii.unhexlify(tx_serialized) ) # tx_candidate_hash = binascii.hexlify(tx_reversed_bin_hash[::-1]) if tx_hash != tx_candidate_hash: print tx_serialized return tx_hash == tx_candidate_hash
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 on_peer_announce(tx): txhash = bitcoin.txhash(bitcoin.serialize(tx["tx"])) if txhash in self.subscriptions[address][0] and self.subscriptions[ address][0][txhash][0] != "complete": self.subscriptions[address][0][txhash][0] += 1 if self.subscriptions[address][0][txhash][ 0] >= self.subscriptions[address][0][txhash][1]: self.subscriptions[address][0][txhash][0] = "complete" self.subscriptions[address][1](tx["tx"]) elif txhash not in self.subscriptions[address][0]: self.subscriptions[address][0][txhash] = [ 1, len(self.peers) / 2 ]
def push(self, txd): tx = btc.serialize(txd) log.debug('\n' + tx) log.debug('txid = ' + btc.txhash(tx)) # TODO send to a random maker or push myself # TODO need to check whether the other party sent it # self.msgchan.push_tx(self.active_orders.keys()[0], txhex) pushed = jm_single().bc_interface.pushtx(tx) if pushed[0]: self.txid = pushed[1] else: log.debug('unable to pushtx, reason: ' + str(pushed[1])) return pushed[0]
def broadcast_tx(self, tx): """ Send the tx to half our peers, wait for half of the remainder to announce the tx before calling back True. """ def on_peer_anncounce(txid): self.pending_txs[txid][0] += 1 if self.pending_txs[txid][0] >= self.pending_txs[txid][1] / 2: if self.pending_txs[txid][3].active(): self.pending_txs[txid][3].cancel() self.pending_txs[txid][2].callback(True) d = defer.Deferred() self.inventory[bitcoin.txhash(tx)] = tx inv_packet = inv("TX", bitcoin.txhash(tx)) self.bloom_filter.insert(bitcoin.bin_txhash(tx)) self.pending_txs[bitcoin.txhash(tx)] = [0, len(self.peers)/2, d, reactor.callLater(10, d.callback, False)] for peer in self.peers[len(self.peers)/2:]: peer.protocol.update_filter() peer.protocol.add_inv_callback(bitcoin.txhash(tx), on_peer_anncounce) for peer in self.peers[:len(self.peers)/2]: peer.protocol.send_message(message(inv_packet, self.params)) return d
def self_sign_and_push(self): #now sign it ourselves tx = btc.serialize(self.latest_tx) for index, ins in enumerate(self.latest_tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) if utxo not in self.input_utxos.keys(): continue addr = self.input_utxos[utxo]['address'] tx = btc.sign(tx, index, self.wallet.get_key_from_addr(addr)) txhex = btc.serialize(self.latest_tx) debug('\n' + txhex) debug('txid = ' + btc.txhash(tx)) #TODO send to a random maker or push myself #TODO need to check whether the other party sent it #self.msgchan.push_tx(self.active_orders.keys()[0], txhex) self.txid = common.bc_interface.pushtx(tx) if self.txid == None: debug('unable to pushtx')
def get_txs_and_heights(outs): txs = {} heights = {} for i in range(0, len(outs), 20): txhashes = [] fetched_heights = [] for j in range(i, min(i + 20, len(outs))): if outs[j]['output'][65:] == '0': txhashes.append(outs[j]['output'][:64]) fetched_heights.append(outs[j]['block_height']) else: sys.stderr.write("Non-purchase tx found (genesis output index not zero): %s\n" % outs[j]['output'][:64]) fetched_txs = fetchtx(txhashes) assert len(fetched_txs) == len(txhashes) == len(fetched_heights) for h, tx, ht in zip(txhashes, fetched_txs, fetched_heights): assert b.txhash(str(tx)) == h txs[h] = tx heights[h] = ht sys.stderr.write('Processed transactions: %d\n' % len(txs)) return {"txs": txs, "heights": heights}
def push(self): tx = btc.serialize(self.latest_tx) log.debug('\n' + tx) self.txid = btc.txhash(tx) log.info('txid = ' + self.txid) tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast') if tx_broadcast == 'self': pushed = jm_single().bc_interface.pushtx(tx) elif tx_broadcast in ['random-peer', 'not-self']: n = len(self.active_orders) if tx_broadcast == 'random-peer': i = random.randrange(n + 1) else: i = random.randrange(n) if i == n: pushed = jm_single().bc_interface.pushtx(tx) else: self.msgchan.push_tx(self.active_orders.keys()[i], tx) pushed = True elif tx_broadcast == 'random-maker': crow = self.db.execute( 'SELECT DISTINCT counterparty FROM orderbook ORDER BY ' + 'RANDOM() LIMIT 1;' ).fetchone() counterparty = crow['counterparty'] log.info('pushing tx to ' + counterparty) self.msgchan.push_tx(counterparty, tx) pushed = True elif tx_broadcast == 'tor': socks5_host = jm_single().config.get("MESSAGING", "socks5_host").split(",")[0] socks5_port = int(jm_single().config.get("MESSAGING", "socks5_port").split(",")[0]) pushed = tor_broadcast_tx(tx, (socks5_host, socks5_port), testnet=(get_network() == "testnet")) if not pushed: log.error('unable to pushtx') return pushed
def push(self): tx = btc.serialize(self.latest_tx) log.debug('\n' + tx) self.txid = btc.txhash(tx) log.info('txid = ' + self.txid) tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast') if tx_broadcast == 'self': pushed = jm_single().bc_interface.pushtx(tx) elif tx_broadcast in ['random-peer', 'not-self']: n = len(self.active_orders) if tx_broadcast == 'random-peer': i = random.randrange(n + 1) else: i = random.randrange(n) if i == n: pushed = jm_single().bc_interface.pushtx(tx) else: self.msgchan.push_tx(self.active_orders.keys()[i], tx) pushed = True elif tx_broadcast == 'random-maker': crow = self.db.execute( 'SELECT DISTINCT counterparty FROM orderbook ORDER BY ' + 'RANDOM() LIMIT 1;').fetchone() counterparty = crow['counterparty'] log.info('pushing tx to ' + counterparty) self.msgchan.push_tx(counterparty, tx) pushed = True elif tx_broadcast == 'tor': socks5_host = jm_single().config.get("MESSAGING", "socks5_host").split(",")[0] socks5_port = int(jm_single().config.get( "MESSAGING", "socks5_port").split(",")[0]) pushed = tor_broadcast_tx(tx, (socks5_host, socks5_port), testnet=(get_network() == "testnet")) if not pushed: log.error('unable to pushtx') return pushed
def test_donation_address(setup_donations, amount): wallets = make_wallets(1, wallet_structures=[[1,1,1,0,0]], mean_amt=0.5) wallet = wallets[0]['wallet'] jm_single().bc_interface.sync_wallet(wallet) #make a rdp from a simple privkey rdp_priv = "\x01"*32 reusable_donation_pubkey = binascii.hexlify(secp256k1.PrivateKey( privkey=rdp_priv, raw=True, ctx=btc.ctx).pubkey.serialize()) dest_addr, sign_k = donation_address(reusable_donation_pubkey) print dest_addr jm_single().bc_interface.rpc('importaddress', [dest_addr, '', False]) ins_full = wallet.unspent total = sum(x['value'] for x in ins_full.values()) ins = ins_full.keys() output_addr = wallet.get_new_addr(1, 1) fee_est = 10000 outs = [{'value': amount, 'address': dest_addr}, {'value': total - amount - fee_est, 'address': output_addr}] tx = btc.mktx(ins, outs) de_tx = btc.deserialize(tx) for index, ins in enumerate(de_tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) addr = ins_full[utxo]['address'] priv = wallet.get_key_from_addr(addr) priv = binascii.unhexlify(priv) usenonce = binascii.unhexlify(sign_k) if index == 0 else None if index == 0: log.debug("Applying rdp to input: " + str(ins)) tx = btc.sign(tx, index, priv, usenonce=usenonce) #pushtx returns False on any error push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: log.debug(btc.txhash(tx)) else: assert False #Role of receiver: regenerate the destination private key, #and address, from the nonce of the first input; check it has #received the coins. detx = btc.deserialize(tx) first_utxo_script = detx['ins'][0]['script'] sig, pub = btc.deserialize_script(first_utxo_script) log.debug(sig) sig = binascii.unhexlify(sig) kGlen = ord(sig[3]) kG = sig[4:4+kGlen] log.debug(binascii.hexlify(kG)) if kG[0] == "\x00": kG = kG[1:] #H(rdp private key * K) + rdp should be ==> dest addr #Open issue: re-introduce recovery without ECC shenanigans #Just cheat by trying both signs for pubkey coerced_kG_1 = "02" + binascii.hexlify(kG) coerced_kG_2 = "03" + binascii.hexlify(kG) for coerc in [coerced_kG_1, coerced_kG_2]: c = btc.sha256(btc.multiply(binascii.hexlify(rdp_priv), coerc, True)) pub_check = btc.add_pubkeys([reusable_donation_pubkey, btc.privtopub(c+'01', True)], True) addr_check = btc.pubtoaddr(pub_check, get_p2pk_vbyte()) log.debug("Found checked address: " + addr_check) if addr_check == dest_addr: time.sleep(3) received = jm_single().bc_interface.get_received_by_addr( [dest_addr], None)['data'][0]['balance'] assert received == amount return assert False
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 on_tx_received(self, address_version, address_hash, height, block_hash, tx): """ Fire when the libbitcoin server tells us we received a payment to this funding address. While unlikely, a user may send multiple transactions to the funding address to reach the funding level. We need to keep a running balance and increment it when a new transaction is received. If the contract is fully funded, we push a notification to the websockets. """ # decode the transaction transaction = deserialize(tx.encode("hex")) # get the amount (in satoshi) the user is expected to pay amount_to_pay = int( float(self.contract["buyer_order"]["order"]["payment"]["amount"]) * 100000000) if tx not in self.received_txs: # make sure we aren't parsing the same tx twice. output_script = 'a914' + digest( unhexlify(self.contract["buyer_order"]["order"]["payment"] ["redeem_script"])).encode("hex") + '87' for output in transaction["outs"]: if output["script"] == output_script: self.amount_funded += output["value"] if tx not in self.received_txs: self.received_txs.append(tx) self.outpoints.append({ "output": bitcoin.txhash(tx.encode("hex")) + ":" + str(output["index"]), "value": output["value"] }) if self.amount_funded >= amount_to_pay: # if fully funded self.timeout.cancel() self.blockchain.unsubscribe_address( self.contract["buyer_order"]["order"]["payment"] ["address"], self.on_tx_received) order_id = digest(json.dumps(self.contract, indent=4)).encode("hex") if self.is_purchase: message_json = { "payment_received": { "address": self.contract["buyer_order"]["order"]["payment"] ["address"], "order_id": order_id } } # update the db self.db.Purchases().update_status(order_id, 1) self.db.Purchases().update_outpoint( order_id, pickle.dumps(self.outpoints)) self.log.info( "Payment for order id %s successfully broadcast to network." % order_id) else: message_json = { "new_order": { "order_id": order_id, "title": self.contract["vendor_offer"]["listing"]["item"] ["title"] } } self.db.Sales().update_status(order_id, 1) self.db.Sales().update_outpoint( order_id, pickle.dumps(self.outpoints)) self.log.info("Received new order %s" % order_id) # push the message over websockets self.ws.push(json.dumps(message_json, indent=4))
def __init__(self, hexstr): self.hexstr = hexstr self.decoded = bitcoin.deserialize(hexstr) self.hsh = bitcoin.txhash(hexstr) self.handle = "BCH_" + self.hsh
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 txid(tx): return b.txhash(tx) def serialize(x):
def txid(tx): return b.txhash(tx)
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 test_donation_address(setup_donations, amount): wallets = make_wallets(1, wallet_structures=[[1, 1, 1, 0, 0]], mean_amt=0.5) wallet = wallets[0]['wallet'] jm_single().bc_interface.sync_wallet(wallet) #make a rdp from a simple privkey rdp_priv = "\x01" * 32 reusable_donation_pubkey = binascii.hexlify( secp256k1.PrivateKey(privkey=rdp_priv, raw=True, ctx=btc.ctx).pubkey.serialize()) dest_addr, sign_k = donation_address(reusable_donation_pubkey) print dest_addr jm_single().bc_interface.rpc('importaddress', [dest_addr, '', False]) ins_full = wallet.unspent total = sum(x['value'] for x in ins_full.values()) ins = ins_full.keys() output_addr = wallet.get_new_addr(1, 1) fee_est = 10000 outs = [{ 'value': amount, 'address': dest_addr }, { 'value': total - amount - fee_est, 'address': output_addr }] tx = btc.mktx(ins, outs) de_tx = btc.deserialize(tx) for index, ins in enumerate(de_tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) addr = ins_full[utxo]['address'] priv = wallet.get_key_from_addr(addr) priv = binascii.unhexlify(priv) usenonce = binascii.unhexlify(sign_k) if index == 0 else None if index == 0: log.debug("Applying rdp to input: " + str(ins)) tx = btc.sign(tx, index, priv, usenonce=usenonce) #pushtx returns False on any error push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: log.debug(btc.txhash(tx)) else: assert False #Role of receiver: regenerate the destination private key, #and address, from the nonce of the first input; check it has #received the coins. detx = btc.deserialize(tx) first_utxo_script = detx['ins'][0]['script'] sig, pub = btc.deserialize_script(first_utxo_script) log.debug(sig) sig = binascii.unhexlify(sig) kGlen = ord(sig[3]) kG = sig[4:4 + kGlen] log.debug(binascii.hexlify(kG)) if kG[0] == "\x00": kG = kG[1:] #H(rdp private key * K) + rdp should be ==> dest addr #Open issue: re-introduce recovery without ECC shenanigans #Just cheat by trying both signs for pubkey coerced_kG_1 = "02" + binascii.hexlify(kG) coerced_kG_2 = "03" + binascii.hexlify(kG) for coerc in [coerced_kG_1, coerced_kG_2]: c = btc.sha256(btc.multiply(binascii.hexlify(rdp_priv), coerc, True)) pub_check = btc.add_pubkeys( [reusable_donation_pubkey, btc.privtopub(c + '01', True)], True) addr_check = btc.pubtoaddr(pub_check, get_p2pk_vbyte()) log.debug("Found checked address: " + addr_check) if addr_check == dest_addr: time.sleep(3) received = jm_single().bc_interface.get_received_by_addr( [dest_addr], None)['data'][0]['balance'] assert received == amount return assert False
def tx_deserialize(tx_hex): """ Given a serialized transaction, return its inputs, outputs, locktime, and version Each input will have: * txid: string * vout: int * [optional] sequence: int * [optional] scriptSig: {"asm": ..., "hex": ...} Each output will have: * value: Decimal (in BTC) * script_hex: string Return tx, formatted as {'locktime': ..., 'version': ..., 'vin': ..., 'vout': ...} """ tx = bitcoin.deserialize(tx_hex) inputs = tx["ins"] outputs = tx["outs"] ret_inputs = [] ret_outputs = [] for inp in inputs: ret_inp = { "txid": inp["outpoint"]["hash"], "vout": int(inp["outpoint"]["index"]), } if "sequence" in inp: ret_inp["sequence"] = int(inp["sequence"]) if "script" in inp: ret_inp["scriptSig"] = { "asm": tx_script_to_asm(inp['script']), "hex": inp["script"] } ret_inputs.append(ret_inp) for i in xrange(0, len(outputs)): out = outputs[i] assert len( out['script'] ) > 0, "Invalid transaction scriptpubkey:\n%s" % simplejson.dumps( tx, indent=4, sort_keys=True) assert out['value'] < 1000 * ( 10**8), "High transaction value\n%s" % simplejson.dumps( tx, indent=4, sort_keys=True) ret_out = { "n": i, "value": Decimal(out["value"]) / 10**8, "scriptPubKey": { "hex": out["script"], "asm": tx_script_to_asm(out['script']) }, # compat with pybitcoin "script_hex": out["script"] } ret_outputs.append(ret_out) ret = { "txid": bitcoin.txhash(tx_hex), "hex": tx_hex, "size": len(tx_hex) / 2, "locktime": tx['locktime'], "version": tx['version'], "vin": ret_inputs, "vout": ret_outputs } return ret
def add_signature(self, nick, sigb64): if nick not in self.nonrespondants: debug('add_signature => nick=' + nick + ' not in nonrespondants ' + str(self.nonrespondants)) return sig = base64.b64decode(sigb64).encode('hex') inserted_sig = False txhex = btc.serialize(self.latest_tx) #batch retrieval of utxo data utxo = {} ctr = 0 for index, ins in enumerate(self.latest_tx['ins']): utxo_for_checking = ins['outpoint']['hash'] + ':' + str( ins['outpoint']['index']) if ins['script'] != '' or utxo_for_checking in self.input_utxos.keys( ): continue utxo[ctr] = [index, utxo_for_checking] ctr += 1 utxo_data = common.bc_interface.query_utxo_set( [x[1] for x in utxo.values()]) #insert signatures for i, u in utxo.iteritems(): if utxo_data[i] == None: continue sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'], *btc.deserialize_script(sig)) if sig_good: debug('found good sig at index=%d' % (u[0])) self.latest_tx['ins'][u[0]]['script'] = sig inserted_sig = True #check if maker has sent everything possible self.utxos[nick].remove(u[1]) if len(self.utxos[nick]) == 0: debug('nick = ' + nick + ' sent all sigs, removing from nonrespondant list') self.nonrespondants.remove(nick) break if not inserted_sig: debug('signature did not match anything in the tx') #TODO what if the signature doesnt match anything # nothing really to do except drop it, carry on and wonder why the # other guy sent a failed signature tx_signed = True for ins in self.latest_tx['ins']: if ins['script'] == '': tx_signed = False if not tx_signed: return self.end_timeout_thread = True self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() debug('the entire tx is signed, ready to pushtx()') txhex = btc.serialize(self.latest_tx) debug('\n' + txhex) self.txid = btc.txhash(txhex) debug('pushing tx ' + self.txid) if self.finishcallback != None: self.finishcallback(self) #TODO send to a random maker or push myself #self.msgchan.push_tx(self.active_orders.keys()[0], txhex) ret = None ret = common.bc_interface.pushtx(txhex) if ret == None: debug('unable to pushtx')
def txid(tx): return b.txhash(tx) SIGHASH_ALL=1
def tx_deserialize( tx_hex ): """ Given a serialized transaction, return its inputs, outputs, locktime, and version Each input will have: * txid: string * vout: int * [optional] sequence: int * [optional] scriptSig: {"asm": ..., "hex": ...} Each output will have: * value: Decimal (in BTC) * script_hex: string Return tx, formatted as {'locktime': ..., 'version': ..., 'vin': ..., 'vout': ...} """ tx = bitcoin.deserialize( tx_hex ) inputs = tx["ins"] outputs = tx["outs"] ret_inputs = [] ret_outputs = [] for inp in inputs: ret_inp = { "txid": inp["outpoint"]["hash"], "vout": int(inp["outpoint"]["index"]), } if "sequence" in inp: ret_inp["sequence"] = int(inp["sequence"]) if "script" in inp: ret_inp["scriptSig"] = { "asm": tx_script_to_asm(inp['script']), "hex": inp["script"] } ret_inputs.append( ret_inp ) for i in xrange(0, len(outputs)): out = outputs[i] assert len(out['script']) > 0, "Invalid transaction scriptpubkey:\n%s" % simplejson.dumps(tx, indent=4, sort_keys=True) assert out['value'] < 1000 * (10**8), "High transaction value\n%s" % simplejson.dumps(tx, indent=4, sort_keys=True) ret_out = { "n": i, "value": Decimal(out["value"]) / 10**8, "scriptPubKey": { "hex": out["script"], "asm": tx_script_to_asm(out['script']) }, # compat with pybitcoin "script_hex": out["script"] } ret_outputs.append( ret_out ) ret = { "txid": bitcoin.txhash(tx_hex), "hex": tx_hex, "size": len(tx_hex) / 2, "locktime": tx['locktime'], "version": tx['version'], "vin": ret_inputs, "vout": ret_outputs } return ret
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"]