def recv_txio(self, nick, utxo_list, cj_pub, change_addr): if nick not in self.nonrespondants: debug('nick(' + nick + ') not in nonrespondants ' + str(self.nonrespondants)) return self.utxos[nick] = utxo_list order = self.db.execute('SELECT ordertype, txfee, cjfee FROM ' 'orderbook WHERE oid=? AND counterparty=?', (self.active_orders[nick], nick)).fetchone() utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: common.debug('ERROR outputs unconfirmed or already spent. utxo_data=' + pprint.pformat(utxo_data)) raise RuntimeError('killing taker, TODO handle this error') total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'], self.cj_amount) self.outputs.append({'address': change_addr, 'value': total_input - self.cj_amount - order['txfee'] + real_cjfee}) print 'fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % (nick, total_input, self.cj_amount, order['txfee'], real_cjfee) cj_addr = btc.pubtoaddr(cj_pub, get_addr_vbyte()) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: debug('nonrespondants = ' + str(self.nonrespondants)) return debug('got all parts, enough to build a tx cjfeetotal=' + str(self.cjfee_total)) my_total_in = 0 for u, va in self.input_utxos.iteritems(): my_total_in += va['value'] #my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()]) my_change_value = my_total_in - self.cj_amount - self.cjfee_total - self.my_txfee print 'fee breakdown for me totalin=%d txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, self.my_txfee, self.cjfee_total, my_change_value) if self.my_change_addr == None: if my_change_value != 0 and abs(my_change_value) != 1: #seems you wont always get exactly zero because of integer rounding # so 1 satoshi extra or fewer being spent as miner fees is acceptable print 'WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' + str(my_change_value) else: self.outputs.append({'address': self.my_change_addr, 'value': my_change_value}) utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])] random.shuffle(utxo_tx) random.shuffle(self.outputs) tx = btc.mktx(utxo_tx, self.outputs) debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) self.msgchan.send_tx(self.active_orders.keys(), tx) #now sign it ourselves here for index, ins in enumerate(btc.deserialize(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)) self.latest_tx = btc.deserialize(tx)
def test_native_P2WSH_SIGHASH_SINGLE_ANYONECANPAY(self): tx = TEST_CASES[2] deserialized = deserialize(tx['unsigned']) serialized = serialize(deserialized) self.assertEqual(serialized, tx['unsigned']) self.assertEqual(deserialized['locktime'], tx['locktime']) ins = self.get_pybtc_vins(tx) outs = self.get_pybtc_outs(tx) generated_tx = mktx(ins, outs) stripped_tx = strip_witness_data(generated_tx) self.assertEqual(stripped_tx, serialized) priv0 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey']) partially_signed = segwit_sign(generated_tx, 0, priv0, int(0.16777215 * 10**8), hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, script=tx['ins'][0]['txinwitness'][1]) signed = segwit_sign(partially_signed, 1, priv0, int(0.16777215 * 10 ** 8), hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, script=tx['ins'][1]['txinwitness'][1], separator_index=tx['ins'][1]['separator']) self.assertEqual(signed, tx['signed']) print('[Native P2WSH] SIGHASH_SINGLE OK')
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 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 make_tx_add_notify(): wallet_dict = make_wallets(1, [[1, 0, 0, 0, 0]], mean_amt=4, sdev_amt=0)[0] amount = 250000000 txfee = 10000 wallet = wallet_dict['wallet'] sync_wallet(wallet) inputs = wallet.select_utxos(0, amount) ins = inputs.keys() input_value = sum([i['value'] for i in inputs.values()]) output_addr = wallet.get_new_addr(1, 0) change_addr = wallet.get_new_addr(0, 1) outs = [{'value': amount, 'address': output_addr}, {'value': input_value - amount - txfee, '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 = inputs[utxo]['address'] priv = wallet.get_key_from_addr(addr) tx = btc.sign(tx, index, priv) unconfirm_called[0] = confirm_called[0] = False timeout_unconfirm_called[0] = timeout_confirm_called[0] = False jm_single().bc_interface.add_tx_notify( btc.deserialize(tx), unconfirm_callback, confirm_callback, output_addr, timeout_callback) return tx
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 send_whole_wallet(fromprivkey, toaddr): transaction_fee = 20000 # .0002 BTC fromaddress = bitcoin.privtoaddr(fromprivkey) balance = sum(transaction['value'] for transaction in bitcoin.unspent(fromaddress)) assert balance >= transaction_fee tx = bitcoin.mktx(bitcoin.history(fromaddress), [{'value': balance - transaction_fee, 'address': toaddr}]) signed_tx = bitcoin.sign(tx, 0, fromprivkey) bitcoin.pushtx(signed_tx)
def test_native_P2WSH_SIGHASH_SINGLE(self): tx = TEST_CASES[1] deserialized = deserialize(tx['unsigned']) serialized = serialize(deserialized) self.assertEqual(serialized, tx['unsigned']) self.assertEqual(deserialized['locktime'], tx['locktime']) ins = self.get_pybtc_vins(tx) outs = self.get_pybtc_outs(tx) generated_tx = mktx(ins, outs) stripped_tx = strip_witness_data(generated_tx) self.assertEqual(stripped_tx, serialized) partially_signed = p2pk_sign(stripped_tx, 0, self.append_compressed_flag_to_privkey(tx['ins'][0]['privkey']), hashcode=SIGHASH_ALL) priv0 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkeys'][0]) priv1 = self.append_compressed_flag_to_privkey(tx['ins'][1]['privkeys'][1]) pub0 = privtopub(priv0) pub1 = privtopub(priv1) REDEEM_SCRIPT_STRUCTURE = { 'keys': [ pub0, pub1 ], 'schema': [ { 'reqs': 1, 'keys': [0], }, { 'reqs': 1, 'keys': [1], } ] } witness_script = mk_OPCS_multisig_script(REDEEM_SCRIPT_STRUCTURE) sign1 = segwit_multisign(partially_signed, 1, witness_script, priv0, 49 * 10**8, hashcode=SIGHASH_SINGLE) sign2 = segwit_multisign(partially_signed, 1, witness_script, priv1, 49 * 10 ** 8, hashcode=SIGHASH_SINGLE, separator_index=1) signed = apply_segwit_multisignatures(partially_signed, 1, witness_script, [sign2, sign1], dummy=False) self.assertEqual(signed, tx['signed']) print('[Native P2WSH] SIGHASH_SINGLE OK')
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 change_stage(): global stage, tx, inputs, outputs, confirmed, last_update, stage_time, confirmed if stage == "outputs" and len(outputs) > 0: stage = "inputs" elif stage == "inputs": if len(outputs) == len(inputs) and len(outputs) > 0: tx = mktx(inputs, outputs) print tx stage = "signatures" else: # If a different number of inputs and outputs, on just one of each one, is received, the process is restarted reset_arrays() stage = "outputs" elif stage == "signatures": if len(outputs) == len(inputs) == len(signatures): tx = insert_signatures(tx) print "Final tx: " + tx stage = "confirm" else: # If the three arrays are not of the same size, the process is restarted. reset_arrays() stage = "outputs" elif stage == "confirm": if confirmed: result = blockr_pushtx(tx, 'testnet') #result = local_push(tx) # End of the mixing, starting the process again reset_arrays() confirmed = False stage = "outputs" else: # Wait for the inputs to be confirmed # Check if there are utxo unconfirmed yet if len(unconfirmed) is not 0: for tx in unconfirmed: if get_tx_info(tx)['confirmations'] >= 6: unconfirmed.remove(tx) if len(unconfirmed) is 0: confirmed = True last_update = time() t = threading.Timer(stage_time, change_stage) t.start() print " * Current stage: " + stage
def build_transaction(self, inputs, outputs): """ Thin wrapper around ``bitcoin.mktx(inputs, outputs)`` Args: inputs (dict): inputs in the form of ``{'output': 'txid:vout', 'value': amount in satoshi}`` outputs (dict): outputs in the form of ``{'address': to_address, 'value': amount in satoshi}`` Returns: transaction """ # prepare inputs and outputs for bitcoin inputs = [{'output': '{}:{}'.format(input['txid'], input['vout']), 'value': input['amount']} for input in inputs] tx = bitcoin.mktx(inputs, outputs) return tx
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 donation_address(cjtx): reusable_donation_pubkey = '02be838257fbfddabaea03afbb9f16e8529dfe2de921260a5c46036d97b5eacf2a' donation_utxo_data = cjtx.input_utxos.iteritems().next() global donation_utxo donation_utxo = donation_utxo_data[0] privkey = cjtx.wallet.get_key_from_addr(donation_utxo_data[1]['address']) tx = btc.mktx(cjtx.utxo_tx, cjtx.outputs) #tx without our inputs and outputs #address = privtoaddr(privkey) #signing_tx = signature_form(tx, 0, mk_pubkey_script(address), SIGHASH_ALL) msghash = btc.bin_txhash(tx, btc.SIGHASH_ALL) #generate unpredictable k global sign_k sign_k = btc.deterministic_generate_k(msghash, privkey) c = btc.sha256(btc.multiply(reusable_donation_pubkey, sign_k)) sender_pubkey = btc.add_pubkeys(reusable_donation_pubkey, btc.multiply(btc.G, c)) sender_address = btc.pubtoaddr(sender_pubkey, get_p2pk_vbyte()) debug('sending coins to ' + sender_address) return sender_address
def test_native_P2WPKH_SIGHASH_ALL(self): tx = TEST_CASES[0] deserialized = deserialize(tx['unsigned']) serialized = serialize(deserialized) self.assertEqual(serialized, tx['unsigned']) self.assertEqual(deserialized['locktime'], tx['locktime']) ins = self.get_pybtc_vins(tx) outs = self.get_pybtc_outs(tx) generated_tx = mktx(ins, outs, locktime=tx['locktime']) stripped_tx = strip_witness_data(generated_tx) self.assertEqual(stripped_tx, serialized) partially_signed = p2pk_sign(stripped_tx, 0, self.append_compressed_flag_to_privkey(tx['ins'][0]['privkey'])) signed = segwit_sign(partially_signed, 1, self.append_compressed_flag_to_privkey(tx['ins'][1]['privkey']), tx['ins'][1]['amount'] * 10**8) self.assertEqual(signed, tx['signed']) print('[Native P2WPKH] SIGHASH_ALL OK')
def sendCustomTransaction(self, privkeys, inputs, outputs, fee=0): success = False totalInputValue = 0 UTXOs = [] for tx_input in inputs: if 'spend' not in tx_input: totalInputValue += tx_input['value'] UTXOs.append(tx_input) totalOutputValue = 0 for tx_output in outputs: totalOutputValue += tx_output['value'] diff = totalInputValue - totalOutputValue if fee != diff: pprint("Warning: Fee incorrect! aborting transaction") else: allKeysPresent = True allInputsConfirmed = True for tx_input in UTXOs: if tx_input['address'] not in privkeys: print 'not found:', tx_input['address'] allKeysPresent = False if tx_input['block_height'] == None: allInputsConfirmed = False if allKeysPresent == True and allInputsConfirmed == True: tx = bitcoin.mktx(UTXOs, outputs) for i in range(0, len(UTXOs)): tx = bitcoin.sign(tx, i, str(privkeys[UTXOs[i]['address']])) try: bitcoin.pushtx(tx) success = True except: e = sys.exc_info() print e success = False return success
def get_hex(self, signed=True): """ Given all the data the user has given so far, make the hex using pybitcointools """ total_ins_satoshi = self.total_input_satoshis() if total_ins_satoshi == 0: raise ValueError("Can't make transaction, there are zero inputs") # Note: there can be zero outs (sweep or coalesc transactions) total_outs_satoshi = sum([x['value'] for x in self.outs]) if not self.fee_satoshi: self.fee() # use default of $0.02 change_satoshi = total_ins_satoshi - (total_outs_satoshi + self.fee_satoshi) if change_satoshi < 0: raise ValueError( "Input amount (%s) must be more than all output amounts (%s) plus fees (%s). You need more %s." % (total_ins_satoshi, total_outs_satoshi, self.fee_satoshi, self.crypto.upper()) ) ins = [x['input'] for x in self.ins] if change_satoshi > 0: if self.verbose: print("Adding change address of %s satoshis to %s" % (change_satoshi, self.change_address)) change = [{'value': change_satoshi, 'address': self.change_address}] else: change = [] # no change ?! if self.verbose: print("Inputs == Outputs, no change address needed.") tx = mktx(ins, self.outs + change) if signed: for i, input_data in enumerate(self.ins): if not input_data['private_key']: raise Exception("Can't sign transaction, missing private key for input %s" % i) tx = sign(tx, i, input_data['private_key']) return tx
def sendCustomTransaction(self, privkeys, inputs, outputs, fee=0): success = False totalInputValue = 0 UTXOs = [] for tx_input in inputs: if 'spend' not in tx_input: totalInputValue += tx_input['value'] UTXOs.append(tx_input) totalOutputValue = 0 for tx_output in outputs: totalOutputValue += tx_output['value'] diff = totalInputValue - totalOutputValue if fee != diff: self.response.write( "<br>Warning: Fee incorrect! aborting transaction") logging.error("Warning: Fee incorrect! aborting transaction") else: allKeysPresent = True allInputsConfirmed = True for tx_input in UTXOs: if tx_input['address'] not in privkeys: allKeysPresent = False if tx_input['block_height'] == None: allInputsConfirmed = False if allKeysPresent == True and allInputsConfirmed == True: tx = bitcoin.mktx(UTXOs, outputs) for i in range(0, len(UTXOs)): tx = bitcoin.sign(tx, i, str(privkeys[UTXOs[i]['address']])) bitcoin.pushtx(tx) success = True return success
def donation_address(cjtx): from bitcoin.main import multiply, G, deterministic_generate_k, add_pubkeys reusable_donation_pubkey = ('02be838257fbfddabaea03afbb9f16e852' '9dfe2de921260a5c46036d97b5eacf2a') donation_utxo_data = cjtx.input_utxos.iteritems().next() global donation_utxo donation_utxo = donation_utxo_data[0] privkey = cjtx.wallet.get_key_from_addr(donation_utxo_data[1]['address']) # tx without our inputs and outputs tx = btc.mktx(cjtx.utxo_tx, cjtx.outputs) msghash = btc.bin_txhash(tx, btc.SIGHASH_ALL) # generate unpredictable k global sign_k sign_k = deterministic_generate_k(msghash, privkey) c = btc.sha256(multiply(reusable_donation_pubkey, sign_k)) sender_pubkey = add_pubkeys( reusable_donation_pubkey, multiply( G, c)) sender_address = btc.pubtoaddr(sender_pubkey, get_p2pk_vbyte()) log.debug('sending coins to ' + sender_address) return sender_address
def test_mktx(self): address = 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7' inputs = [ { 'output': 'ff'*32 + ':0', 'value': 100, 'segregated': True } ] outputs = [ { 'value': 100, 'address': address } ] tx = mktx(inputs, outputs) print(tx) deserialized = deserialize(tx) re_encoded_address = bech32encode(deserialized['outs'][0]['script'], prefix=BECH32_BITCOIN_TESTNET_PREFIX) assert re_encoded_address == address decoded = bech32decode(re_encoded_address) self.assertEqual(deserialized['outs'][0]['script'], decoded)
def main(): scriptSig = getScriptPubKey(args.solution) addrP2SH = scriptaddr(scriptSig, 50) solution = b2a_hex(args.solution.encode()).decode('utf-8') ins, balance = getBalance(addrP2SH) len_inputs = len(ins) if balance > 0: # Output outs = [{'address': args.address, 'value': int(balance * COIN)}] # Make unsigned transaction tx = mktx(ins, outs) # Calculate fee size = len(a2b_hex(tx)) fee = int((size / 1024) * 0.01 * COIN) # MAX FEE = 0.1 CHA fee = 1e7 if fee > 1e7 else fee # sign inputs + apply fee unpacked = deserialize(tx) for puzzle_input in unpacked['ins']: puzzle_input['script'] = getHexLen(solution) + solution puzzle_input['script'] += getHexLen(scriptSig) + scriptSig unpacked['outs'][0]['value'] -= fee tx = serialize(unpacked) if len(tx) > 0: txid = broadcast(tx) print('> Transaction broadcasted, %s' % tx) else: print('> No funds :C')
def sendCustomTransaction(self, privkeys, inputs, outputs, fee=0): success = False totalInputValue = 0 UTXOs = [] for tx_input in inputs: if 'spend' not in tx_input: totalInputValue += tx_input['value'] UTXOs.append(tx_input) totalOutputValue = 0 for tx_output in outputs: totalOutputValue += tx_output['value'] diff = totalInputValue - totalOutputValue if fee != diff: self.response.write("<br>Warning: Fee incorrect! aborting transaction") logging.error("Warning: Fee incorrect! aborting transaction") else: allKeysPresent = True allInputsConfirmed = True for tx_input in UTXOs: if tx_input['address'] not in privkeys: allKeysPresent = False if tx_input['block_height'] == None: allInputsConfirmed = False if allKeysPresent == True and allInputsConfirmed == True: tx = bitcoin.mktx(UTXOs, outputs) for i in range(0, len(UTXOs)): tx = bitcoin.sign(tx, i, str(privkeys[UTXOs[i]['address']])) bitcoin.pushtx(tx) success = True return success
def send(args): if len(args.outputs) % 2 != 0: raise Exception("When sending, there must be an even number of arguments " + "for the outputs (address, price)") unspents = bitcoin.BlockchainInfo.unspent_xpub(args.xpub) def btctosatoshi(vs): return int(float(vs)*100000000.0) fee = btctosatoshi(args.fee) if fee < 0: fee = int(0.0001*100000000) #todo do something to estimated fee...make it negative or something though... DONT outaddrval = [(args.outputs[2*i],btctosatoshi(args.outputs[2*i+1])) for i in range(len(args.outputs)//2)] outtotalval = sum([o[1] for o in outaddrval]) changeindex = check_outputs_max_index(unspents,1) changeaddress = bitcoin.pubtoaddr(bitcoin.bip32_descend(args.xpub, 1, changeindex)) unspents = bitcoin.select(unspents, outtotalval+fee) #unspents cull using outtotalval unspenttotalval = sum([u['value'] for u in unspents]) changeamount = unspenttotalval - (outtotalval + abs(fee)) if changeamount < 0: raise Exception("There is unlikely to be enough unspent outputs to cover the transaction and fees") out = {} outs = outaddrval if changeamount > 0: outs += [[changeaddress, changeamount]] #print(unspents) #print(outs) otx = [{'address':o[0], 'value':o[1]} for o in outs] tx = bitcoin.mktx(unspents, otx) #compute all the underlying addresses and pubkeys into a string hash #estimate the fee #build the transaction out['tx'] = tx out['keys'] = dict([(u['output'], u['xpub']) for u in unspents]) #print(bitcoin.deserialize(tx)) json.dump(out, sys.stdout)
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, 'Obelisk returned a wonky row' inputs.append("%s:%s" % (row[0].encode('hex'), 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, ["%s:%s" % (payment_output, send_amount)]) signatures = [ multisign(tx, x, script, private_key) for x in range(len(inputs)) ] self.market.release_funds_to_merchant(buyer['buyer_order_id'], tx, script, signatures, order.get('merchant'))
def sign(utxo, priv, destaddrs): """Sign a tx sending the amount amt, from utxo utxo, equally to each of addresses in list destaddrs, after fees; the purpose is to create a large number of utxos. """ results = validate_utxo_data([(utxo, priv)], retrieve=True) if not results: return False assert results[0][0] == utxo amt = results[0][1] ins = [utxo] estfee = estimate_tx_fee(1, len(destaddrs)) outs = [] share = int((amt - estfee) / len(destaddrs)) fee = amt - share * len(destaddrs) assert fee >= estfee log.info("Using fee: " + str(fee)) for i, addr in enumerate(destaddrs): outs.append({'address': addr, 'value': share}) unsigned_tx = btc.mktx(ins, outs) return btc.sign(unsigned_tx, 0, btc.from_wif_privkey(priv, vbyte=get_p2pk_vbyte()))
def sign(utxo, priv, destaddrs): """Sign a tx sending the amount amt, from utxo utxo, equally to each of addresses in list destaddrs, after fees; the purpose is to create a large number of utxos. """ results = validate_utxo_data([(utxo, priv)], retrieve=True) if not results: return False assert results[0][0] == utxo amt = results[0][1] ins = [utxo] estfee = estimate_tx_fee(1, len(destaddrs)) outs = [] share = int((amt - estfee) / len(destaddrs)) fee = amt - share*len(destaddrs) assert fee >= estfee log.debug("Using fee: " + str(fee)) for i, addr in enumerate(destaddrs): outs.append({'address': addr, 'value': share}) unsigned_tx = btc.mktx(ins, outs) return btc.sign(unsigned_tx, 0, btc.from_wif_privkey( priv, vbyte=get_p2pk_vbyte()))
def make_sign_and_push(ins_full, wallet, amount, output_addr=None, change_addr=None, hashcode=btc.SIGHASH_ALL, estimate_fee=False): 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 get_hex(self, signed=True): """ Given all the data the user has given so far, make the hex using pybitcointools """ logging.info('2') total_ins_satoshi = self.total_input_satoshis() logging.info('3') if total_ins_satoshi == 0: raise ValueError("Can't make transaction, there are zero inputs") logging.info('4') # Note: there can be zero outs (sweep or coalesc transactions) total_outs_satoshi = sum([x['value'] for x in self.outs]) logging.info('5') if not self.fee_satoshi: self.fee() # use default of $0.02 logging.info('6') change_satoshi = total_ins_satoshi - (total_outs_satoshi + self.fee_satoshi) logging.info('7') if change_satoshi < 0: raise ValueError( "Input amount (%s) must be more than all output amounts (%s) plus fees (%s). You need more %s." % (total_ins_satoshi, total_outs_satoshi, self.fee_satoshi, self.crypto.upper())) logging.info('8') ins = [x['input'] for x in self.ins] logging.info('9') if change_satoshi > 0: if self.verbose: print("Adding change address of %s satoshis to %s" % (change_satoshi, self.change_address)) change = [{ 'value': change_satoshi, 'address': self.change_address }] logging.info('10') else: change = [] # no change ?! if self.verbose: print("Inputs == Outputs, no change address needed.") logging.info('11') tx = mktx(ins, self.outs + change) logging.info('12') if signed: for i, input_data in enumerate(self.ins): logging.info('13') if not input_data['private_key']: raise Exception( "Can't sign transaction, missing private key for input %s" % i) tx = sign(tx, i, input_data['private_key']) logging.info('14') return tx
def recv_txio(self, nick, utxo_list, cj_pub, change_addr): if nick not in self.nonrespondants: debug('nick(' + nick + ') not in nonrespondants ' + str(self.nonrespondants)) return self.utxos[nick] = utxo_list order = self.db.execute( 'SELECT ordertype, txfee, cjfee FROM ' 'orderbook WHERE oid=? AND counterparty=?', (self.active_orders[nick], nick)).fetchone() utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: common.debug( 'ERROR outputs unconfirmed or already spent. utxo_data=' + pprint.pformat(utxo_data)) raise RuntimeError('killing taker, TODO handle this error') total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'], self.cj_amount) self.outputs.append({ 'address': change_addr, 'value': total_input - self.cj_amount - order['txfee'] + real_cjfee }) print 'fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % ( nick, total_input, self.cj_amount, order['txfee'], real_cjfee) cj_addr = btc.pubtoaddr(cj_pub, get_addr_vbyte()) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: debug('nonrespondants = ' + str(self.nonrespondants)) return debug('got all parts, enough to build a tx cjfeetotal=' + str(self.cjfee_total)) my_total_in = 0 for u, va in self.input_utxos.iteritems(): my_total_in += va['value'] #my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()]) my_change_value = my_total_in - self.cj_amount - self.cjfee_total - self.my_txfee print 'fee breakdown for me totalin=%d txfee=%d cjfee_total=%d => changevalue=%d' % ( my_total_in, self.my_txfee, self.cjfee_total, my_change_value) if self.my_change_addr == None: if my_change_value != 0 and abs(my_change_value) != 1: #seems you wont always get exactly zero because of integer rounding # so 1 satoshi extra or fewer being spent as miner fees is acceptable print 'WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' + str( my_change_value) else: self.outputs.append({ 'address': self.my_change_addr, 'value': my_change_value }) utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])] random.shuffle(self.outputs) tx = btc.mktx(utxo_tx, self.outputs) debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) self.msgchan.send_tx(self.active_orders.keys(), tx) #now sign it ourselves here for index, ins in enumerate(btc.deserialize(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)) self.latest_tx = btc.deserialize(tx)
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 main(): # get current fast confirmation bitcoin fee best_fee = bitcoin_fee() # command line arguments parser = argparse.ArgumentParser( description='Create a Bitcoin transaction') parser.add_argument('-f', '--from', help='one of more from addresses', nargs='+', required=True) parser.add_argument('-t', '--to', help='address to send to', nargs=1, required=True) parser.add_argument('-m', '--fee', help='miner fee in Satoshis per byte', nargs=1, type=valid_fee, required=False, default=[best_fee]) parser.add_argument('-b', '--bitcoin', help='amount to transfer in BTC', nargs=1, type=float, required=False) parser.add_argument('-u', '--usd', help='amount to transfer in USD', nargs=1, type=float, required=False) parser.add_argument('-o', '--override', help='override high fee sanity check', action='store_true', required=False) parser.add_argument( '-e', '--envfriendly', help= 'spend small UXTO amounts first; results in higher fees, but reduces global UTXO DB size', action='store_true', required=False) parser.add_argument('-v', '--verbose', help='show verbose output', action='store_true', required=False) args = vars(parser.parse_args()) if (args['verbose']): logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) # check which currency sweep = False btc_specified = (args['bitcoin'] is not None) usd_specified = (args['usd'] is not None) amount_satoshi = None if not (btc_specified or usd_specified): sweep = True logger.debug('SWEEP all funds') elif (btc_specified and usd_specified): logger.error('Specify the amount in Bitcoin or USD, but not both') exit(1) elif btc_specified: amount_btc = args['bitcoin'][0] amount_satoshi = int(round(amount_btc * 1e8)) logger.info('AMOUNT {:,.8f} BTC = {:,.0f} Satoshi'.format( amount_btc, amount_satoshi)) elif usd_specified: amount_usd = args['usd'][0] btc_price = get_bitcoin_price() amount_btc = amount_usd / btc_price amount_satoshi = int(round(amount_btc * 1e8)) logger.info( 'AMOUNT {:,.2f} USD = {:,.8f} BTC = {:,.0f} Satoshi'.format( amount_usd, amount_btc, amount_satoshi)) # substring search for destination address in wallet dest = None item = lookup(args['to'][0]) if item is not None: dest = item['address'] logger.debug('Found destination address {} {} in wallet'.format( item['name'], dest)) else: dest = args['to'][0] logger.debug('Using destination address {}'.format(dest)) if not validate_address(dest): logger.warning( 'Destination address "{}" is not a valid Bitcoin address'.format( dest)) # gather UTXOs from inputs utxos = [] privkeys = {} from_addrs = [] for source in args['from']: entry = lookup(source) if (entry == None): logger.error( 'No source address found in wallet matching "{}"'.format( source)) exit(1) name = entry['name'] address = entry['address'] privkey = entry['privkey'] logger.debug('Found source address {} {} in wallet'.format( name, address)) from_addrs.append(address) privkeys[address] = privkey # gather UTXOs unspent = get_unspent(address) logger.debug('Address {} has {} unspent{}'.format( address, len(unspent), pluralize(len(unspent)))) has_utxos = False for tx in unspent: utxo = {} utxo['output'] = '{}:{}'.format(tx['id'], tx['vout']) utxo['address'] = address utxo['value'] = tx['amount'] logger.debug('utxo["value"] = {}'.format(utxo['value'])) utxos.append(utxo) has_utxos = True if not has_utxos: logger.warning('Address {} has no confirmed UTXOs'.format(address)) # must have at least one UTXO nutxos = len(utxos) if nutxos == 0: logger.error('No confirmed UTXOs found') exit(1) # report UTXO summary naddr = len(args['from']) btc_price = get_bitcoin_price() avail_satoshi = sum([tx['value'] for tx in utxos]) avail_usd = (avail_satoshi / 1e8) * btc_price addr_suffix = '' if naddr == 1 else 'es' logger.debug( 'UTXO Summary: {} address{} {} UTXO{} {:,.0f} Satoshi ${:,.2f} USD'. format(naddr, addr_suffix, nutxos, pluralize(nutxos), avail_satoshi, avail_usd)) # build tx txins = [] txouts = [] fee_per_byte = int(args['fee'][0]) logger.debug('Using fee of {} satoshis per byte'.format(fee_per_byte)) # sweep all BTC if sweep: logger.warning('Sweeping entire {:,.0f} satoshi from all UTXOs'.format( avail_satoshi)) est_length = config['len-base'] + (config['len-per-input'] * nutxos) + config['len-per-output'] fee = est_length * fee_per_byte # inputs n = 0 total = 0 for utxo in utxos: total += utxo['value'] logger.debug('Input {} UTXO {} Value {:,.0f} Total {:,.0f}'.format( n, utxo['output'], utxo['value'], total)) txins.append(utxo) n += 1 # output send_satoshi = avail_satoshi - fee txouts = [{'value': send_satoshi, 'address': dest}] logger.debug('OUTPUT 0 Address {} Value {:,.0f}'.format( dest, send_satoshi)) # transfer specific amount else: change_address = None send_satoshi = amount_satoshi logger.debug('transferring {:,.0f} Satoshi'.format(send_satoshi)) initial_fee = (config['len-base'] + (config['len-per-output'] * 2)) * fee_per_byte remaining = send_satoshi + initial_fee total_fees = initial_fee # Environmentally-friendly transfer spends smallest UTXOs # first. This reduces the size of UTXO database each full-node # must store, but results in a higher fee. reverse = True if args['envfriendly']: logger.warning( 'environmentally friendly mode active, higher fees apply') reverse = False ordered_utxos = sorted(utxos, key=lambda k: k['value'], reverse=reverse) # inputs n = 0 total = 0 change = 0 for utxo in ordered_utxos: fee_inc = (config['len-per-input'] * fee_per_byte) remaining += fee_inc total_fees += fee_inc remaining -= utxo['value'] total += utxo['value'] logger.debug('Input {} UTXO {} Value {:,.0f} Total {:,.0f}'.format( n, utxo['output'], utxo['value'], total)) txins.append(utxo) n += 1 if remaining < 0: change = -remaining change_address = utxo['address'] break # insufficient funds if (remaining > 0): note = '' if (remaining <= total_fees): note = 'after adding miner fees ' logger.critical( 'Insufficient funds {}{:,.0f} > {:,.0f}'.format( note, send_satoshi + total_fees, avail_satoshi)) exit(1) # outputs txouts = [{'address': dest, 'value': send_satoshi}] logger.debug('OUTPUT 0 Address {} Value {:,.0f}'.format( dest, send_satoshi)) if (change > 0): # trivial remainder condition: it costs more in fees to # use the UTXO than what actually remains there, so just # leave it for the miner and a take speed bonus. sweep_utxo_len = config['len-base'] + config[ 'len-per-input'] + config['len-per-output'] sweep_utxo_fee = sweep_utxo_len * best_fee if change < sweep_utxo_fee: change_usd = (change / 1e8) * btc_price logger.warning( 'Trivial UTXO remainder released to miner {:,.0f} Satoshi ${:,.2f} USD' .format(change, change_usd)) # return change else: # merge if change going to dest address if (change_address == dest): merged_value = send_satoshi + change logger.warning( 'Change address same as destination, merging output values {:,.0f}' .format(merged_value)) txouts = [{'address': dest, 'value': merged_value}] # add extra output else: txouts.append({'address': change_address, 'value': change}) logger.debug('OUTPUT 1 Address {} Value {:,.0f}'.format( change_address, change)) # sanity checks sum_ins = sum([x['value'] for x in txins]) logger.debug('SUM(inputs) = {:,.0f}'.format(sum_ins)) sum_outs = sum([x['value'] for x in txouts]) logger.debug('SUM(outputs) = {:,.0f}'.format(sum_outs)) fee_satoshi = (sum_ins - sum_outs) fee_btc = (fee_satoshi / 1e8) fee_usd = btc_price * fee_btc logger.info('Paying miner fee of {:,.0f} Satoshi ${:,.2f} USD'.format( fee_satoshi, fee_usd)) if (fee_usd < 0): logger.critical( 'Sanity check failed: sum of outputs {:,.0f} exceeds sum of inputs {:,.0f}' .format(sum_outs, sum_ins)) exit(1) elif (fee_usd < 0.01): logger.critical( 'Bad transaction: miner fee too small: ${:,.6f}'.format(fee_usd)) exit(1) if (fee_usd > config['insane-fee-usd']): msg = 'Sanity check failed: miner fee too large ${:,.2f} >= ${:,.2f}'.format( fee_usd, config['insane-fee-usd']) if args['override']: logger.warning(msg) logger.warning('Overriding sanity check') else: logger.error(msg) exit(1) # sign tx inputs tx = mktx(txins, txouts) for i in range(len(txins)): logger.debug('Signing input {}'.format(i)) try: tx = sign(tx, i, privkeys[txins[i]['address']]) except: logger.critical('Failed to sign UTXO {}'.format( txins[i]['output'])) exit(1) # confirm send_btc = send_satoshi / 1e8 confirm = 'Sending {:,.8f} BTC ${:,.2f} USD '.format( send_btc, send_btc * btc_price) confirm += 'from {} to {} '.format(from_addrs, [dest]) confirm += 'using fee of {:,.8f} BTC ${:,.2f} USD'.format(fee_btc, fee_usd) logger.warning(confirm) raw_input('[ Press Enter to confirm ]') # submit tid = broadcast(tx) if tid is None: logger.critical('Failed to submit transaction to the network') else: logger.warning('Broadcasted TXID {}'.format(tid))
def recv_txio(self, nick, utxo_list, cj_pub, change_addr): if nick not in self.nonrespondants: debug('recv_txio => nick=' + nick + ' not in nonrespondants ' + str(self.nonrespondants)) return self.utxos[nick] = utxo_list order = self.db.execute( 'SELECT ordertype, txfee, cjfee FROM ' 'orderbook WHERE oid=? AND counterparty=?', (self.active_orders[nick], nick)).fetchone() utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: common.debug( 'ERROR outputs unconfirmed or already spent. utxo_data=' + pprint.pformat(utxo_data)) #when internal reviewing of makers is created, add it here to immediately quit return #ignore this message, eventually the timeout thread will recover total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'], self.cj_amount) self.outputs.append({ 'address': change_addr, 'value': total_input - self.cj_amount - order['txfee'] + real_cjfee }) debug( 'fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % (nick, total_input, self.cj_amount, order['txfee'], real_cjfee)) cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte()) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.maker_txfee_contributions += order['txfee'] self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: debug('nonrespondants = ' + str(self.nonrespondants)) return self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() debug('got all parts, enough to build a tx') self.nonrespondants = list(self.active_orders.keys()) my_total_in = sum( [va['value'] for u, va in self.input_utxos.iteritems()]) my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0) my_change_value = (my_total_in - self.cj_amount - self.cjfee_total - my_txfee) debug( 'fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, my_txfee, self.maker_txfee_contributions, self.cjfee_total, my_change_value)) if self.my_change_addr == None: if my_change_value != 0 and abs(my_change_value) != 1: #seems you wont always get exactly zero because of integer rounding # so 1 satoshi extra or fewer being spent as miner fees is acceptable debug('WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' + str(my_change_value)) else: self.outputs.append({ 'address': self.my_change_addr, 'value': my_change_value }) self.utxo_tx = [ dict([('output', u)]) for u in sum(self.utxos.values(), []) ] self.outputs.append({ 'address': self.coinjoin_address(), 'value': self.cj_amount }) random.shuffle(self.utxo_tx) random.shuffle(self.outputs) tx = btc.mktx(self.utxo_tx, self.outputs) debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) self.msgchan.send_tx(self.active_orders.keys(), tx) self.latest_tx = btc.deserialize(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 #placeholders required ins['script'] = 'deadbeef'
def test_P2SH_P2WSH_ALL_SIGHASH(self): tx = TEST_CASES[3] VIN_AMOUNT = int(9.87654321 * 10**8) deserialized = deserialize(tx['unsigned']) serialized = serialize(deserialized) self.assertEqual(serialized, tx['unsigned']) self.assertEqual(deserialized['locktime'], tx['locktime']) ins = self.get_pybtc_vins(tx) outs = self.get_pybtc_outs(tx) generated_tx = mktx(ins, outs) stripped_tx = strip_witness_data(generated_tx) self.assertEqual(stripped_tx, serialized) priv0 = self.append_compressed_flag_to_privkey( tx['ins'][0]['privkeys'][0]) priv1 = self.append_compressed_flag_to_privkey( tx['ins'][0]['privkeys'][1]) priv2 = self.append_compressed_flag_to_privkey( tx['ins'][0]['privkeys'][2]) priv3 = self.append_compressed_flag_to_privkey( tx['ins'][0]['privkeys'][3]) priv4 = self.append_compressed_flag_to_privkey( tx['ins'][0]['privkeys'][4]) priv5 = self.append_compressed_flag_to_privkey( tx['ins'][0]['privkeys'][5]) witness_script = mk_multisig_script(privtopub(priv0), privtopub(priv1), privtopub(priv2), privtopub(priv3), privtopub(priv4), privtopub(priv5), 6) self.assertEqual(witness_script, tx['ins'][0]['witness_script']) sign0 = segwit_multisign(generated_tx, 0, witness_script, priv0, VIN_AMOUNT, hashcode=SIGHASH_ALL) sign1 = segwit_multisign(generated_tx, 0, witness_script, priv1, VIN_AMOUNT, hashcode=SIGHASH_NONE) sign2 = segwit_multisign(generated_tx, 0, witness_script, priv2, VIN_AMOUNT, hashcode=SIGHASH_SINGLE) sign3 = segwit_multisign(generated_tx, 0, witness_script, priv3, VIN_AMOUNT, hashcode=SIGHASH_ALL | SIGHASH_ANYONECANPAY) sign4 = segwit_multisign(generated_tx, 0, witness_script, priv4, VIN_AMOUNT, hashcode=SIGHASH_NONE | SIGHASH_ANYONECANPAY) sign5 = segwit_multisign(generated_tx, 0, witness_script, priv5, VIN_AMOUNT, hashcode=SIGHASH_SINGLE | SIGHASH_ANYONECANPAY) signed = apply_segwit_multisignatures( stripped_tx, 0, witness_script, [sign0, sign1, sign2, sign3, sign4, sign5], nested=True) self.assertEqual(signed, tx['signed']) print( '[P2WSH 6-of-6 multisig NESTED in P2SH] SIGHASH_SINGLE\SIGHASH_ALL\SIGHASH_NONE & ANYONECANPAY' )
def send(privkey, pubkey, sendto, message="", force=False): op_return = message addr = pubkey confirmed_balance, inputs, unconfirmed = insight.getunspentbalance(addr) if not force: if not crypto.pubkey_is_valid(addr): # Invalid Address # print("Invalid Address") return False elif constants.FEE_MINIMUM > confirmed_balance: # Not enough chauchas # print("No minimum fee") return False elif len(op_return) > MAX: # Message too long # print("Message to long") return False # Transformar valores a Chatoshis used_amount = int(constants.FEE_MINIMUM * constants.COIN) # Utilizar solo las unspent que se necesiten used_balance = 0 used_inputs = [] for i in inputs: used_balance += i["value"] used_inputs.append(i) if used_balance > used_amount: break # Output outputs = [{"address": sendto, "value": used_amount}] # OP_RETURN if len(op_return) > 0 and len(op_return) <= MAX: payload = __payload(op_return) script = constants.OP_RETURN + b2a_hex(payload).decode("utf-8", errors="ignore") outputs.append({"value": 0, "script": script}) # Transaction template_tx = mktx(used_inputs, outputs) size = len(a2b_hex(template_tx)) # FEE = 0.01 CHA/kb # MAX FEE = 0.1 CHA fee = int((size / constants.KYLOBYTE) * constants.FEE_RECOMMENDED * constants.COIN) fee = constants.FEE_MAX if fee > constants.FEE_MAX else fee if used_balance == confirmed_balance: outputs[0] = {"address": sendto, "value": used_amount - fee} tx = mktx(used_inputs, outputs) else: tx = mksend(used_inputs, outputs, addr, fee) for i in range(len(used_inputs)): tx = sign(tx, i, privkey) broadcasting = insight.broadcast(tx) try: msg = insight.endpoint() + "/tx/%s" % broadcasting.json()["txid"] except Exception: msg = broadcasting.text return msg
def recv_txio(self, nick, utxo_list, cj_pub, change_addr): if nick not in self.nonrespondants: log.debug(('recv_txio => nick={} not in ' 'nonrespondants {}').format(nick, self.nonrespondants)) return self.utxos[nick] = utxo_list order = self.db.execute( 'SELECT ordertype, txfee, cjfee FROM ' 'orderbook WHERE oid=? AND counterparty=?', (self.active_orders[nick], nick)).fetchone() utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: log.debug(('ERROR outputs unconfirmed or already spent. ' 'utxo_data={}').format(pprint.pformat(utxo_data))) # when internal reviewing of makers is created, add it here to # immediately quit return # ignore this message, eventually the timeout thread will recover total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'], self.cj_amount) self.outputs.append({ 'address': change_addr, 'value': total_input - self.cj_amount - order['txfee'] + real_cjfee }) fmt = ('fee breakdown for {} totalin={:d} ' 'cjamount={:d} txfee={:d} realcjfee={:d}').format log.debug( fmt(nick, total_input, self.cj_amount, order['txfee'], real_cjfee)) cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte()) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.maker_txfee_contributions += order['txfee'] self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: log.debug('nonrespondants = ' + str(self.nonrespondants)) return self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() log.debug('got all parts, enough to build a tx') self.nonrespondants = list(self.active_orders.keys()) my_total_in = sum( [va['value'] for u, va in self.input_utxos.iteritems()]) if self.my_change_addr: #Estimate fee per choice of next/3/6 blocks targetting. estimated_fee = estimate_tx_fee(len(sum(self.utxos.values(), [])), len(self.outputs) + 2) log.debug("Based on initial guess: " + str(self.total_txfee) + ", we estimated a fee of: " + str(estimated_fee)) #reset total self.total_txfee = estimated_fee my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0) my_change_value = (my_total_in - self.cj_amount - self.cjfee_total - my_txfee) #Since we could not predict the maker's inputs, we may end up needing #too much such that the change value is negative or small. Note that #we have tried to avoid this based on over-estimating the needed amount #in SendPayment.create_tx(), but it is still a possibility if one maker #uses a *lot* of inputs. if self.my_change_addr and my_change_value <= 0: raise ValueError("Calculated transaction fee of: " + str(self.total_txfee) + " is too large for our inputs;Please try again.") elif self.my_change_addr and my_change_value <= jm_single( ).DUST_THRESHOLD: log.debug("Dynamically calculated change lower than dust: " + str(my_change_value) + "; dropping.") self.my_change_addr = None my_change_value = 0 log.debug( 'fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, my_txfee, self.maker_txfee_contributions, self.cjfee_total, my_change_value)) if self.my_change_addr is None: if my_change_value != 0 and abs(my_change_value) != 1: # seems you wont always get exactly zero because of integer # rounding so 1 satoshi extra or fewer being spent as miner # fees is acceptable log.debug(('WARNING CHANGE NOT BEING ' 'USED\nCHANGEVALUE = {}').format(my_change_value)) else: self.outputs.append({ 'address': self.my_change_addr, 'value': my_change_value }) self.utxo_tx = [ dict([('output', u)]) for u in sum(self.utxos.values(), []) ] self.outputs.append({ 'address': self.coinjoin_address(), 'value': self.cj_amount }) random.shuffle(self.utxo_tx) random.shuffle(self.outputs) tx = btc.mktx(self.utxo_tx, self.outputs) log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) self.msgchan.send_tx(self.active_orders.keys(), tx) self.latest_tx = btc.deserialize(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 # placeholders required ins['script'] = 'deadbeef'
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 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 recv_txio(self, nick, utxo_list, cj_pub, change_addr): if nick not in self.nonrespondants: debug('recv_txio => nick=' + nick + ' not in nonrespondants ' + str(self.nonrespondants)) return self.utxos[nick] = utxo_list order = self.db.execute('SELECT ordertype, txfee, cjfee FROM ' 'orderbook WHERE oid=? AND counterparty=?', (self.active_orders[nick], nick)).fetchone() utxo_data = common.bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: common.debug('ERROR outputs unconfirmed or already spent. utxo_data=' + pprint.pformat(utxo_data)) #when internal reviewing of makers is created, add it here to immediately quit return #ignore this message, eventually the timeout thread will recover total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(order['ordertype'], order['cjfee'], self.cj_amount) self.outputs.append({'address': change_addr, 'value': total_input - self.cj_amount - order['txfee'] + real_cjfee}) debug('fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % (nick, total_input, self.cj_amount, order['txfee'], real_cjfee)) cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte()) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.maker_txfee_contributions += order['txfee'] self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: debug('nonrespondants = ' + str(self.nonrespondants)) return self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() debug('got all parts, enough to build a tx') self.nonrespondants = list(self.active_orders.keys()) my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()]) my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0) my_change_value = (my_total_in - self.cj_amount - self.cjfee_total - my_txfee) debug('fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, my_txfee, self.maker_txfee_contributions, self.cjfee_total, my_change_value)) if self.my_change_addr == None: if my_change_value != 0 and abs(my_change_value) != 1: #seems you wont always get exactly zero because of integer rounding # so 1 satoshi extra or fewer being spent as miner fees is acceptable debug('WARNING CHANGE NOT BEING USED\nCHANGEVALUE = ' + str(my_change_value)) else: self.outputs.append({'address': self.my_change_addr, 'value': my_change_value}) self.utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])] self.outputs.append({'address': self.coinjoin_address(), 'value': self.cj_amount}) random.shuffle(self.utxo_tx) random.shuffle(self.outputs) tx = btc.mktx(self.utxo_tx, self.outputs) debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) self.msgchan.send_tx(self.active_orders.keys(), tx) self.latest_tx = btc.deserialize(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 #placeholders required ins['script'] = 'deadbeef'
bitprint('\nPublic keys\n', pubkeys) bitprint('\nAddresses\n', addresses) # Total: 0.06 funding_txs = [ bitcoin_client.sendtoaddress(address, 0.01 * (i + 1)) for i, address in enumerate(addresses) ] bitprint('\nFunding txs') bitprint(funding_txs) outpoints = list(_get_outpoints(bitcoin_client, addresses, funding_txs)) bitprint(outpoints) tx = bitcoin.mktx( outpoints, [ {'value': int(0.055 * 10**8), 'address': an_external_address}, {'value': int(0.004 * 10 ** 8), 'address': addresses[0]} ] ) bitprint('\nUnsigned tx') bitprint(bitcoin.deserialize(tx)) bitprint('\nUnsigned raw tx\n', tx) for i, key in enumerate(keys): tx = bitcoin.sign(tx, i, key) bitprint('\nSigned tx') bitprint(bitcoin.deserialize(tx)) bitprint('\nSigned raw tx\n', tx) bitprint('\nHash of submitted transaction:', bitcoin_client.sendrawtransaction(tx)) bitprint('\nMempool:', bitcoin_client.getrawmempool()) block_hash = bitcoin_client.generate(1)[0]
import binascii import bitcoin address = '3BSBPJyP7r6W7WBz74ghL6W3A1aB7F6SoF' pk = 'b3a6b8c655d471f003be2d0ccf5766c48cc36cdf02110cfcf3920971571c02b5' msg = 'Grazie mille per il bellissimo ed originale regalo di matrimonio!' OP_RETURN = 106 OP_PUSHDATA1 = 76 inputs = [{ 'output': '5377427396f9ba7ca186bd7c82e42a599e19115e7b8818d1051fa153aab5f0f6:3', 'value': 10000000 }] outputs = [{ 'address': address, 'value': 10000000 - 29800 }, { 'script': '6a4c41' + binascii.hexlify(msg.encode()).decode(), 'value': 0 }] tx = bitcoin.mktx(inputs, outputs) tx = bitcoin.sign(tx, 0, pk) print(tx) print(len(tx) / 2000)
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 recv_txio(self, nick, utxo_list, cj_pub, change_addr): if nick not in self.nonrespondants: log.debug(('recv_txio => nick={} not in ' 'nonrespondants {}').format(nick, self.nonrespondants)) return self.utxos[nick] = utxo_list utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: log.debug(('ERROR outputs unconfirmed or already spent. ' 'utxo_data={}').format(pprint.pformat(utxo_data))) # when internal reviewing of makers is created, add it here to # immediately quit return # ignore this message, eventually the timeout thread will recover total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(self.active_orders[nick]['ordertype'], self.active_orders[nick]['cjfee'], self.cj_amount) self.outputs.append({'address': change_addr, 'value': total_input - self.cj_amount - self.active_orders[nick]['txfee'] + real_cjfee}) fmt = ('fee breakdown for {} totalin={:d} ' 'cjamount={:d} txfee={:d} realcjfee={:d}').format log.debug(fmt(nick, total_input, self.cj_amount, self.active_orders[nick]['txfee'], real_cjfee)) cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte()) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.maker_txfee_contributions += self.active_orders[nick]['txfee'] self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: log.debug('nonrespondants = ' + str(self.nonrespondants)) return self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() log.debug('got all parts, enough to build a tx') self.nonrespondants = list(self.active_orders.keys()) my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()]) if self.my_change_addr: #Estimate fee per choice of next/3/6 blocks targetting. estimated_fee = estimate_tx_fee(len(sum( self.utxos.values(),[])), len(self.outputs)+2) log.debug("Based on initial guess: "+str( self.total_txfee)+", we estimated a fee of: "+str(estimated_fee)) #reset total self.total_txfee = estimated_fee my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0) my_change_value = ( my_total_in - self.cj_amount - self.cjfee_total - my_txfee) #Since we could not predict the maker's inputs, we may end up needing #too much such that the change value is negative or small. Note that #we have tried to avoid this based on over-estimating the needed amount #in SendPayment.create_tx(), but it is still a possibility if one maker #uses a *lot* of inputs. if self.my_change_addr and my_change_value <= 0: raise ValueError("Calculated transaction fee of: "+str( self.total_txfee)+" is too large for our inputs;Please try again.") elif self.my_change_addr and my_change_value <= jm_single().DUST_THRESHOLD: log.debug("Dynamically calculated change lower than dust: "+str( my_change_value)+"; dropping.") self.my_change_addr = None my_change_value = 0 log.debug('fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, my_txfee, self.maker_txfee_contributions, self.cjfee_total, my_change_value)) if self.my_change_addr is None: if my_change_value != 0 and abs(my_change_value) != 1: # seems you wont always get exactly zero because of integer # rounding so 1 satoshi extra or fewer being spent as miner # fees is acceptable log.debug(('WARNING CHANGE NOT BEING ' 'USED\nCHANGEVALUE = {}').format(my_change_value)) else: self.outputs.append({'address': self.my_change_addr, 'value': my_change_value}) self.utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])] self.outputs.append({'address': self.coinjoin_address(), 'value': self.cj_amount}) random.shuffle(self.utxo_tx) random.shuffle(self.outputs) tx = btc.mktx(self.utxo_tx, self.outputs) log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) self.msgchan.send_tx(self.active_orders.keys(), tx) self.latest_tx = btc.deserialize(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 # placeholders required ins['script'] = 'deadbeef'
print 'addr', addr #check if you have any token left h = b.blockr_unspent(addr) print 'history', h balance = sum([i['value'] for i in h]) print 'balance', balance fee = 3000 #build a get change back out outs = [{'value': balance - fee, 'address': addr}] # #%create a transaction tx_hex = b.mktx(h, outs) #create a op_return hexcode tx_opr = b.mk_opreturn("hello world! by mepppp", tx_hex) assert (tx_opr != tx_hex) #%% #generate the tx dict to append the op_ret script #tx=b.deserialize(tx_hex) #tx['outs'].append({'value':0,'script':op_ret}) print "====== tx_opr ======" print tx_opr
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 commitTo(self, testnet, commit_address, private_key): """ Commit to some address on the blockchain """ # Check if we have the keys for the BM address public_key = highlevelcrypto.privToPub( private_key.encode('hex')).decode('hex') fromAddress = self.getAddress(testnet, public_key) result = self.getUnspentTransactions(testnet, [fromAddress]) if not 'unspent' in result or len(result['unspent']) == 0: log_debug("commitTo: No unspent TXs (%s)" % (fromAddress)) return False unspent_txs = result['unspent'] # filter for those with an amount >= minimum amount unspent_txs = filter( lambda tx: float(tx['amount']) > BitcoinThread. BTC_UNSPENT_MIN_AVAILABLE, unspent_txs) if len(unspent_txs) == 0: log_debug("commitTo: No unspent TXs >= %d (%s, %s)" % (BitcoinThread.BTC_UNSPENT_MIN_AVAILABLE, fromAddress, result['unspent'])) return False # Find random unspent with an amount >= 0.00010 mBTC random.shuffle(unspent_txs) while len(unspent_txs) > 0: tx = unspent_txs.pop(0) log_debug("Trying unspent tx: %s" % tx) amount = float(tx['amount']) amount_satoshis = int(amount * 100000000) change_satoshis = amount_satoshis - BitcoinThread.SATOSHI_COMMITMENT_AMOUNT - BitcoinThread.SATOSHI_TRANSACTION_FEE # Code in bitcoin.mktx separates the input string into tx=input[:64] and n=input[65:] input_tx = "%s %d" % (tx['tx'], tx['n']) commit_payable = { "address": commit_address, "value": BitcoinThread.SATOSHI_COMMITMENT_AMOUNT } change_payable = {"address": fromAddress, "value": change_satoshis} tx = bitcoin.mktx([input_tx], [commit_payable, change_payable]) signed_tx = bitcoin.sign(tx, 0, private_key) log_debug("Pushing tx: %s" % bitcoin.deserialize(tx)) if testnet: try: result = json.loads( bitcoin.blockr_pushtx(signed_tx, 'testnet')) except Exception, e: # If we get {"status":"fail","data":"Could not push your transaction!","code":500,"message":"Did you sign your transaction?"} # in an exception here, it probably means that the referenced inputs in our transaction have been spent in the meantime. try: e_obj = json.loads(e.message) if e_obj["data"] == "Could not push your transaction!": from debug import logger log_warn( "Couldn't push transaction. Sometimes this is because the referenced inputs have been spent in the meantime, %s" % e_obj) # Continue to try the next unspent tx continue else: log_warn(e) except: log_warn(e) return 'status' in result and result['status'] == "success" else: # if not testnet # I had problems pushing non-testnet transactions to blockr.io, # so we're using blockchain.info for this, and that works fine. try: result = bitcoin.pushtx(signed_tx) if result.lower() == "transaction submitted": log_debug("Committed to %s" % commit_address) return True else: log_warn("Transaction push fail: %s" % (result, )) except Exception, e: log_warn("Transaction push exception: %s" % (e, )) continue
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 recv_txio(self, nick, utxo_list, cj_pub, change_addr): if nick not in self.nonrespondants: log.debug(('recv_txio => nick={} not in ' 'nonrespondants {}').format(nick, self.nonrespondants)) return self.utxos[nick] = utxo_list utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: log.debug(('ERROR outputs unconfirmed or already spent. ' 'utxo_data={}').format(pprint.pformat(utxo_data))) # when internal reviewing of makers is created, add it here to # immediately quit; currently, the timeout thread suffices. return total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(self.active_orders[nick]['ordertype'], self.active_orders[nick]['cjfee'], self.cj_amount) change_amount = (total_input - self.cj_amount - self.active_orders[nick]['txfee'] + real_cjfee) # certain malicious and/or incompetent liquidity providers send # inputs totalling less than the coinjoin amount! this leads to # a change output of zero satoshis, so the invalid transaction # fails harmlessly; let's fail earlier, with a clear message. if change_amount < jm_single().DUST_THRESHOLD: fmt = ('ERROR counterparty requires sub-dust change. nick={}' 'totalin={:d} cjamount={:d} change={:d}').format log.debug(fmt(nick, total_input, self.cj_amount, change_amount)) return # timeout marks this maker as nonresponsive self.outputs.append({'address': change_addr, 'value': change_amount}) fmt = ('fee breakdown for {} totalin={:d} ' 'cjamount={:d} txfee={:d} realcjfee={:d}').format log.debug(fmt(nick, total_input, self.cj_amount, self.active_orders[nick]['txfee'], real_cjfee)) cj_addr = btc.pubtoaddr(cj_pub, get_p2pk_vbyte()) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.maker_txfee_contributions += self.active_orders[nick]['txfee'] self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: log.debug('nonrespondants = ' + str(self.nonrespondants)) return log.debug('got all parts, enough to build a tx') self.nonrespondants = list(self.active_orders.keys()) my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems()]) if self.my_change_addr: #Estimate fee per choice of next/3/6 blocks targetting. estimated_fee = estimate_tx_fee(len(sum( self.utxos.values(),[])), len(self.outputs)+2) log.debug("Based on initial guess: "+str( self.total_txfee)+", we estimated a fee of: "+str(estimated_fee)) #reset total self.total_txfee = estimated_fee my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0) my_change_value = ( my_total_in - self.cj_amount - self.cjfee_total - my_txfee) #Since we could not predict the maker's inputs, we may end up needing #too much such that the change value is negative or small. Note that #we have tried to avoid this based on over-estimating the needed amount #in SendPayment.create_tx(), but it is still a possibility if one maker #uses a *lot* of inputs. if self.my_change_addr and my_change_value <= 0: raise ValueError("Calculated transaction fee of: "+str( self.total_txfee)+" is too large for our inputs;Please try again.") elif self.my_change_addr and my_change_value <= jm_single().DUST_THRESHOLD: log.debug("Dynamically calculated change lower than dust: "+str( my_change_value)+"; dropping.") self.my_change_addr = None my_change_value = 0 log.debug('fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, my_txfee, self.maker_txfee_contributions, self.cjfee_total, my_change_value)) if self.my_change_addr is None: if my_change_value != 0 and abs(my_change_value) != 1: # seems you wont always get exactly zero because of integer # rounding so 1 satoshi extra or fewer being spent as miner # fees is acceptable log.debug(('WARNING CHANGE NOT BEING ' 'USED\nCHANGEVALUE = {}').format(my_change_value)) else: self.outputs.append({'address': self.my_change_addr, 'value': my_change_value}) self.utxo_tx = [dict([('output', u)]) for u in sum(self.utxos.values(), [])] self.outputs.append({'address': self.coinjoin_address(), 'value': self.cj_amount}) random.shuffle(self.utxo_tx) random.shuffle(self.outputs) tx = btc.mktx(self.utxo_tx, self.outputs) log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) #Re-calculate a sensible timeout wait based on the throttling #settings and the tx size. #Calculation: Let tx size be S; tx undergoes two b64 expansions, 1.8*S #So we're sending N*1.8*S over the wire, and the #maximum bytes/sec = B, means we need (1.8*N*S/B) seconds, #and need to add some leeway for network delays, we just add the #contents of jm_single().maker_timeout_sec (the user configured value) self.maker_timeout_sec = (len(tx) * 1.8 * len( self.active_orders.keys()))/(B_PER_SEC) + jm_single().maker_timeout_sec log.debug("Based on transaction size: " + str( len(tx)) + ", calculated time to wait for replies: " + str( self.maker_timeout_sec)) self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() self.msgchan.send_tx(self.active_orders.keys(), tx) self.latest_tx = btc.deserialize(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 # placeholders required ins['script'] = 'deadbeef'
def test_P2SH_P2WSH_ALL_SIGHASH(self): tx = TEST_CASES[3] VIN_AMOUNT = int(9.87654321 * 10**8) deserialized = deserialize(tx['unsigned']) serialized = serialize(deserialized) self.assertEqual(serialized, tx['unsigned']) self.assertEqual(deserialized['locktime'], tx['locktime']) ins = self.get_pybtc_vins(tx) outs = self.get_pybtc_outs(tx) generated_tx = mktx(ins, outs) stripped_tx = strip_witness_data(generated_tx) self.assertEqual(stripped_tx, serialized) priv0 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][0]) priv1 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][1]) priv2 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][2]) priv3 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][3]) priv4 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][4]) priv5 = self.append_compressed_flag_to_privkey(tx['ins'][0]['privkeys'][5]) witness_script = mk_multisig_script(privtopub(priv0), privtopub(priv1), privtopub(priv2), privtopub(priv3), privtopub(priv4), privtopub(priv5), 6) self.assertEqual(witness_script, tx['ins'][0]['witness_script']) sign0 = segwit_multisign(generated_tx, 0, witness_script, priv0, VIN_AMOUNT, hashcode=SIGHASH_ALL) sign1 = segwit_multisign(generated_tx, 0, witness_script, priv1, VIN_AMOUNT, hashcode=SIGHASH_NONE) sign2 = segwit_multisign(generated_tx, 0, witness_script, priv2, VIN_AMOUNT, hashcode=SIGHASH_SINGLE) sign3 = segwit_multisign(generated_tx, 0, witness_script, priv3, VIN_AMOUNT, hashcode=SIGHASH_ALL|SIGHASH_ANYONECANPAY) sign4 = segwit_multisign(generated_tx, 0, witness_script, priv4, VIN_AMOUNT, hashcode=SIGHASH_NONE|SIGHASH_ANYONECANPAY) sign5 = segwit_multisign(generated_tx, 0, witness_script, priv5, VIN_AMOUNT, hashcode=SIGHASH_SINGLE|SIGHASH_ANYONECANPAY) signed = apply_segwit_multisignatures(stripped_tx, 0, witness_script, [sign0, sign1, sign2, sign3, sign4, sign5], nested=True) self.assertEqual(signed, tx['signed']) print('[P2WSH 6-of-6 multisig NESTED in P2SH] SIGHASH_SINGLE\SIGHASH_ALL\SIGHASH_NONE & ANYONECANPAY')
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 commitTo(self, testnet, commit_address, private_key): """ Commit to some address on the blockchain """ # Check if we have the keys for the BM address public_key = highlevelcrypto.privToPub( private_key.encode('hex') ).decode('hex') fromAddress = self.getAddress( testnet, public_key ) result = self.getUnspentTransactions( testnet, [fromAddress] ) if not 'unspent' in result or len( result['unspent'] ) == 0: log_debug( "commitTo: No unspent TXs (%s)" % ( fromAddress ) ) return False unspent_txs = result['unspent'] # filter for those with an amount >= minimum amount unspent_txs = filter( lambda tx: float( tx['amount'] ) > BitcoinThread.BTC_UNSPENT_MIN_AVAILABLE, unspent_txs ) if len( unspent_txs ) == 0: log_debug( "commitTo: No unspent TXs >= %d (%s, %s)" % ( BitcoinThread.BTC_UNSPENT_MIN_AVAILABLE, fromAddress, result['unspent'] ) ) return False # Find random unspent with an amount >= 0.00010 mBTC random.shuffle( unspent_txs ) while len( unspent_txs ) > 0: tx = unspent_txs.pop( 0 ) log_debug( "Trying unspent tx: %s" % tx ) amount = float( tx['amount'] ) amount_satoshis = int( amount * 100000000 ) change_satoshis = amount_satoshis - BitcoinThread.SATOSHI_COMMITMENT_AMOUNT - BitcoinThread.SATOSHI_TRANSACTION_FEE # Code in bitcoin.mktx separates the input string into tx=input[:64] and n=input[65:] input_tx = "%s %d" % ( tx['tx'], tx['n'] ) commit_payable = { "address": commit_address, "value": BitcoinThread.SATOSHI_COMMITMENT_AMOUNT } change_payable = { "address": fromAddress, "value": change_satoshis } tx = bitcoin.mktx( [input_tx], [ commit_payable, change_payable ] ) signed_tx = bitcoin.sign(tx, 0, private_key ) log_debug( "Pushing tx: %s" % bitcoin.deserialize( tx ) ) if testnet: try: result = json.loads( bitcoin.blockr_pushtx(signed_tx, 'testnet') ) except Exception, e: # If we get {"status":"fail","data":"Could not push your transaction!","code":500,"message":"Did you sign your transaction?"} # in an exception here, it probably means that the referenced inputs in our transaction have been spent in the meantime. try: e_obj = json.loads( e.message ) if e_obj["data"] == "Could not push your transaction!": from debug import logger log_warn( "Couldn't push transaction. Sometimes this is because the referenced inputs have been spent in the meantime, %s" % e_obj ) # Continue to try the next unspent tx continue else: log_warn( e ) except: log_warn( e ) return 'status' in result and result['status'] == "success" else: # if not testnet # I had problems pushing non-testnet transactions to blockr.io, # so we're using blockchain.info for this, and that works fine. try: result = bitcoin.pushtx( signed_tx ) if result.lower() == "transaction submitted": log_debug( "Committed to %s" % commit_address ) return True else: log_warn( "Transaction push fail: %s" % ( result, ) ) except Exception, e: log_warn( "Transaction push exception: %s" % ( e, ) ) continue
def recv_txio(self, nick, utxo_list, auth_pub, cj_addr, change_addr): if nick not in self.nonrespondants: log.debug(('recv_txio => nick={} not in ' 'nonrespondants {}').format(nick, self.nonrespondants)) return self.utxos[nick] = utxo_list utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[nick]) if None in utxo_data: log.debug(('ERROR outputs unconfirmed or already spent. ' 'utxo_data={}').format(pprint.pformat(utxo_data))) # when internal reviewing of makers is created, add it here to # immediately quit; currently, the timeout thread suffices. return #Complete maker authorization: #Extract the address fields from the utxos #Construct the Bitcoin address for the auth_pub field #Ensure that at least one address from utxos corresponds. input_addresses = [d['address'] for d in utxo_data] auth_address = btc.pubkey_to_address(auth_pub, get_p2pk_vbyte()) if not auth_address in input_addresses: log.debug("ERROR maker's authorising pubkey is not included " "in the transaction: " + str(auth_address)) return total_input = sum([d['value'] for d in utxo_data]) real_cjfee = calc_cj_fee(self.active_orders[nick]['ordertype'], self.active_orders[nick]['cjfee'], self.cj_amount) change_amount = (total_input - self.cj_amount - self.active_orders[nick]['txfee'] + real_cjfee) # certain malicious and/or incompetent liquidity providers send # inputs totalling less than the coinjoin amount! this leads to # a change output of zero satoshis, so the invalid transaction # fails harmlessly; let's fail earlier, with a clear message. if change_amount < jm_single().DUST_THRESHOLD: fmt = ('ERROR counterparty requires sub-dust change. nick={}' 'totalin={:d} cjamount={:d} change={:d}').format log.debug(fmt(nick, total_input, self.cj_amount, change_amount)) return # timeout marks this maker as nonresponsive self.outputs.append({'address': change_addr, 'value': change_amount}) fmt = ('fee breakdown for {} totalin={:d} ' 'cjamount={:d} txfee={:d} realcjfee={:d}').format log.debug( fmt(nick, total_input, self.cj_amount, self.active_orders[nick]['txfee'], real_cjfee)) self.outputs.append({'address': cj_addr, 'value': self.cj_amount}) self.cjfee_total += real_cjfee self.maker_txfee_contributions += self.active_orders[nick]['txfee'] self.nonrespondants.remove(nick) if len(self.nonrespondants) > 0: log.debug('nonrespondants = ' + str(self.nonrespondants)) return log.debug('got all parts, enough to build a tx') self.nonrespondants = list(self.active_orders.keys()) my_total_in = sum( [va['value'] for u, va in self.input_utxos.iteritems()]) if self.my_change_addr: #Estimate fee per choice of next/3/6 blocks targetting. estimated_fee = estimate_tx_fee(len(sum(self.utxos.values(), [])), len(self.outputs) + 2) log.debug("Based on initial guess: " + str(self.total_txfee) + ", we estimated a fee of: " + str(estimated_fee)) #reset total self.total_txfee = estimated_fee my_txfee = max(self.total_txfee - self.maker_txfee_contributions, 0) my_change_value = (my_total_in - self.cj_amount - self.cjfee_total - my_txfee) #Since we could not predict the maker's inputs, we may end up needing #too much such that the change value is negative or small. Note that #we have tried to avoid this based on over-estimating the needed amount #in SendPayment.create_tx(), but it is still a possibility if one maker #uses a *lot* of inputs. if self.my_change_addr and my_change_value <= 0: raise ValueError("Calculated transaction fee of: " + str(self.total_txfee) + " is too large for our inputs;Please try again.") elif self.my_change_addr and my_change_value <= jm_single( ).DUST_THRESHOLD: log.debug("Dynamically calculated change lower than dust: " + str(my_change_value) + "; dropping.") self.my_change_addr = None my_change_value = 0 log.debug( 'fee breakdown for me totalin=%d my_txfee=%d makers_txfee=%d cjfee_total=%d => changevalue=%d' % (my_total_in, my_txfee, self.maker_txfee_contributions, self.cjfee_total, my_change_value)) if self.my_change_addr is None: if my_change_value != 0 and abs(my_change_value) != 1: # seems you wont always get exactly zero because of integer # rounding so 1 satoshi extra or fewer being spent as miner # fees is acceptable log.debug(('WARNING CHANGE NOT BEING ' 'USED\nCHANGEVALUE = {}').format(my_change_value)) else: self.outputs.append({ 'address': self.my_change_addr, 'value': my_change_value }) self.utxo_tx = [ dict([('output', u)]) for u in sum(self.utxos.values(), []) ] self.outputs.append({ 'address': self.coinjoin_address(), 'value': self.cj_amount }) random.shuffle(self.utxo_tx) random.shuffle(self.outputs) tx = btc.mktx(self.utxo_tx, self.outputs) log.debug('obtained tx\n' + pprint.pformat(btc.deserialize(tx))) #Re-calculate a sensible timeout wait based on the throttling #settings and the tx size. #Calculation: Let tx size be S; tx undergoes two b64 expansions, 1.8*S #So we're sending N*1.8*S over the wire, and the #maximum bytes/sec = B, means we need (1.8*N*S/B) seconds, #and need to add some leeway for network delays, we just add the #contents of jm_single().maker_timeout_sec (the user configured value) self.maker_timeout_sec = (len(tx) * 1.8 * len(self.active_orders.keys( ))) / (B_PER_SEC) + jm_single().maker_timeout_sec log.debug("Based on transaction size: " + str(len(tx)) + ", calculated time to wait for replies: " + str(self.maker_timeout_sec)) self.all_responded = True with self.timeout_lock: self.timeout_lock.notify() self.msgchan.send_tx(self.active_orders.keys(), tx) self.latest_tx = btc.deserialize(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 # placeholders required ins['script'] = 'deadbeef'
from bitcoin_101 import bitprint if __name__ == '__main__': key = bitcoin.random_key() outpoints = { '97f7c7d8ac85e40c255f8a763b6cd9a68f3a94d2e93e8bfa08f977b92e55465e:0': 50000, '4cc806bb04f730c445c60b3e0f4f44b54769a1c196ca37d8d4002135e4abd171:1': 50000 } tx = bitcoin.mktx([{ 'output': k, 'value': v } for k, v in outpoints.items()], [{ 'value': 90000, 'address': '16iw1MQ1sy1DtRPYw3ao1bCamoyBJtRB4t' }]) bitprint('Raw transaction:', tx) bitprint('Deserialized transaction') bitprint(bitcoin.deserialize(tx)) one_signed_tx = bitcoin.sign(tx, 0, key) bitprint(one_signed_tx) bitprint('ScriptSig for the first input') bitprint( bitcoin.deserialize_script( bitcoin.deserialize(one_signed_tx)['ins'][0]['script'])) bitprint('ScriptPubKey for the first output') bitprint(
def moveTokens(tokenid, outputs, change): """ move given amount of tokens to given outputs. tokenid: name of the token to deal with outputs is a list of (output, amount) pairs. change is the receiving address for change. Returns a pair (TokenTransaction, OnchainTransaction) or None if an error occured. Does not change the DB. Autocreates a mint transaction if the outputs exceed the available funds and there is a blank output plus the corresponding private key available. """ log.info("Trying to move %s tokens to %s outputs plus one change address", tokenid, len(outputs)) token_addr = tokenval.tok2addr(tokenid) for x in outputs: if x[1] < 0: log.error("Negative outputs not allowed.") return None, None # total needed amount of tokens needed = sum(x[1] for x in outputs) log.info("Total needed amount of tokens: %d", needed) # sum of incoming BCH satoshis bch_insum = 0 # sum on incoming tokens token_insum = 0 utxo_avail = tokenval.unspentOutputsForToken(token_addr) log.info("Known UTXOs for token %s: %d.", tokenid, len(utxo_avail)) # collect enough inputs until we've got everything we need privs = {} mints = [] bch_inputs = [] bch_outputs = [] for utxo in utxo_avail: value, bch_value = takeUtxo(utxo, privs, bch_inputs, skip_if_blank=True) if value is None or bch_value is None: log.info("Skipping UTXO %s.", utxo) continue token_insum += value bch_insum += bch_value if token_insum >= needed: break log.info("Collected %d inputs for a total of %d %s and %d satoshis.", len(bch_inputs), token_insum, tokenid, bch_insum) if token_insum < needed: # FIXME: code dup log.info("Less tokens %d available than needed %d.", token_insum, needed) blank = tokenval.unspentBlank(token_addr) if blank is None: log.error( "And no blank outputs are available to create new tokens.") return None, None value, bch_value = takeUtxo(blank, privs, bch_inputs) assert (value is None) if bch_value is None: log.info("Blank could not be taken.") return None, None mints.append((blank.txhash, blank.outnum, needed - token_insum)) log.info("Taking blank unspent output %s and minting %d %s.", blank, needed - token_insum, tokenid) token_insum += needed - token_insum bch_insum += bch_value l = len(outputs) + 1 # +1 for change output miner_fee = 200 * l # total miner fee for transaction min_funds = 1000 log.info("Total miner fee: %d", miner_fee) if bch_insum < l * min_funds + miner_fee: # 1 satoshi per token output plus miner fee available log.info( "Needing extra inputs for miner fees, having %d satoshis and needing %d satoshis.", bch_insum, l * min_funds + miner_fee) for utxo in tokenval.allUnspentOutputs(): if ("TOKEN_" + utxo.token_addr in tokenval.db) or utxo.value is not None: log.info("Skipping UTXO %s as it is a token output.", utxo) continue value, bch_value = takeUtxo(utxo, privs, bch_inputs) assert (value is None) if bch_value is None: continue bch_insum += bch_value if bch_insum >= l * min_funds + miner_fee: break if bch_insum < l * min_funds + miner_fee: # 1 satoshi per token output plus miner fee available log.info( "Not enough extra inputs available, having %d satoshis and needing %d satoshis", bch_insum, l * min_funds + miner_fee) return None, None tok_outputs = [] for out in outputs: addr, val = out bch_outputs.append({"value": min_funds, "address": addr}) log.info("Adding output %s with %d tokens.", addr, val) tok_outputs.append(val) bch_outputs.append({ "value": min_funds + bch_insum - miner_fee - l * min_funds, "address": change }) log.info("Adding output %s with %d tokens.", change, token_insum - needed) tok_outputs.append(token_insum - needed) log.info("Token mints: %d", len(mints)) log.info("Token outputs: %d", len(outputs)) toktxn = tokenval.TokenTransaction(token_addr, mints, tok_outputs, list(privs.values())) log.info("BCH transaction inputs: %s", bch_inputs) log.info("BCH transaction outputs: %s", bch_outputs) bchtxn = bitcoin.mktx(bch_inputs, bch_outputs) log.info("Created unsigned BCH transaction.") bchtxn = bitcoin.transaction.mk_opreturn(codecs.decode(toktxn.hsh, "hex"), bchtxn) log.info("Added OP_RETURN pointer.") for i, bch_in in enumerate(bch_inputs): prevoutstr = bch_in["output"] # bch_inputs.append( # { "output" : prevoutstr, # "amount" : utxo.bch_value}) log.info("Signing BCH input %d: %s", i, bch_in) bchtxn = bitcoin.segwit_sign(bchtxn, i, privs[prevoutstr], bch_in["amount"], hashcode=bitcoin.SIGHASH_ALL | bitcoin.SIGHASH_FORKID, separator_index=None) octxn = tokenval.OnchainTransaction(bchtxn) return toktxn, octxn