def test_mktx(setup_tx_creation): """Testing exceptional conditions; not guaranteed to create valid tx objects""" #outpoint structure must be {"outpoint":{"hash":hash, "index": num}} ins = [{ 'outpoint': { "hash": x * 32, "index": 0 }, "script": "", "sequence": 4294967295 } for x in ["a", "b", "c"]] pub = vpubs[0] addr = bitcoin.pubkey_to_address(pub, magicbyte=get_p2pk_vbyte()) script = bitcoin.address_to_script(addr) outs = [ script + ":1000", addr + ":2000", { "script": script, "value": 3000 } ] tx = bitcoin.mktx(ins, outs) print(tx) #rewrite with invalid output outs.append({"foo": "bar"}) with pytest.raises(Exception) as e_info: tx = bitcoin.mktx(ins, outs)
def test_verify_tx_input(setup_tx_creation): priv = b"\xaa" * 32 + b"\x01" pub = bitcoin.privkey_to_pubkey(priv) script = bitcoin.pubkey_to_p2sh_p2wpkh_script(pub) addr = str(bitcoin.CCoinAddress.from_scriptPubKey(script)) wallet_service = make_wallets(1, [[2, 0, 0, 0, 0]], 1)[0]['wallet'] wallet_service.sync_wallet(fast=True) insfull = wallet_service.select_utxos(0, 110000000) outs = [{"address": addr, "value": 1000000}] ins = list(insfull.keys()) tx = bitcoin.mktx(ins, outs) scripts = {0: (insfull[ins[0]]["script"], bitcoin.coins_to_satoshi(1))} success, msg = wallet_service.sign_tx(tx, scripts) assert success, msg # testing Joinmarket's ability to verify transaction inputs # of others: pretend we don't have a wallet owning the transaction, # and instead verify an input using the (sig, pub, scriptCode) data # that is sent by counterparties: cScrWit = tx.wit.vtxinwit[0].scriptWitness sig = cScrWit.stack[0] pub = cScrWit.stack[1] scriptSig = tx.vin[0].scriptSig tx2 = bitcoin.mktx(ins, outs) res = bitcoin.verify_tx_input(tx2, 0, scriptSig, bitcoin.pubkey_to_p2wpkh_script(pub), amount=bitcoin.coins_to_satoshi(1), witness=bitcoin.CScriptWitness([sig, pub])) assert res
def test_verify_tx_input(setup_tx_creation, signall, mktxlist): priv = "aa" * 32 + "01" addr = bitcoin.privkey_to_address(priv, magicbyte=get_p2pk_vbyte()) wallet = make_wallets(1, [[2, 0, 0, 0, 0]], 1)[0]['wallet'] sync_wallet(wallet) insfull = wallet.select_utxos(0, 110000000) print(insfull) if not mktxlist: outs = [{"address": addr, "value": 1000000}] ins = insfull.keys() tx = bitcoin.mktx(ins, outs) else: out1 = addr + ":1000000" ins0, ins1 = insfull.keys() print("INS0 is: " + str(ins0)) print("INS1 is: " + str(ins1)) tx = bitcoin.mktx(ins0, ins1, out1) desertx = bitcoin.deserialize(tx) print(desertx) if signall: privdict = {} for index, ins in enumerate(desertx['ins']): utxo = ins['outpoint']['hash'] + ':' + str( ins['outpoint']['index']) ad = insfull[utxo]['address'] priv = wallet.get_key_from_addr(ad) privdict[utxo] = priv tx = bitcoin.signall(tx, privdict) else: for index, ins in enumerate(desertx['ins']): utxo = ins['outpoint']['hash'] + ':' + str( ins['outpoint']['index']) ad = insfull[utxo]['address'] priv = wallet.get_key_from_addr(ad) if index % 2: tx = binascii.unhexlify(tx) tx = bitcoin.sign(tx, index, priv) if index % 2: tx = binascii.hexlify(tx) desertx2 = bitcoin.deserialize(tx) print(desertx2) sig, pub = bitcoin.deserialize_script(desertx2['ins'][0]['script']) print(sig, pub) pubscript = bitcoin.address_to_script( bitcoin.pubkey_to_address(pub, magicbyte=get_p2pk_vbyte())) sig = binascii.unhexlify(sig) pub = binascii.unhexlify(pub) sig_good = bitcoin.verify_tx_input(tx, 0, pubscript, sig, pub) assert sig_good
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 = bitcoin.mktx(ins, outs) de_tx = bitcoin.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 = bitcoin.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(bitcoin.deserialize(tx), unconfirm_callback, confirm_callback, output_addr, timeout_callback) return tx
def test_add_new_utxos(setup_wallet): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') wallet = get_populated_wallet(num=1) scripts = [ wallet.get_new_script(x, BaseWallet.ADDRESS_TYPE_INTERNAL) for x in range(3) ] tx_scripts = list(scripts) tx = btc.mktx([(b"\x00" * 32, 2)], [{ "address": wallet.script_to_addr(s), "value": 10**8 } for s in tx_scripts]) added = wallet.add_new_utxos(tx, 1) assert len(added) == len(scripts) added_scripts = {x['script'] for x in added.values()} for s in scripts: assert s in added_scripts balances = wallet.get_balance_by_mixdepth() assert balances[0] == 2 * 10**8 assert balances[1] == 10**8 assert balances[2] == 10**8 assert len(balances) == wallet.max_mixdepth + 1
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 = list(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}] de_tx = btc.deserialize(btc.mktx(ins, outs)) scripts = {} for index, ins in enumerate(de_tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) script = wallet.addr_to_script(ins_full[utxo]['address']) scripts[index] = (script, ins_full[utxo]['value']) binarize_tx(de_tx) de_tx = wallet.sign_tx(de_tx, scripts, hashcode=hashcode) #pushtx returns False on any error tx = binascii.hexlify(btc.serialize(de_tx)).decode('ascii') push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: return btc.txhash(tx) else: return False
def sign(utxo, priv, destaddrs, segwit=True): """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. If segwit=True the (single) utxo is assumed to be of type segwit p2sh/p2wpkh. """ results = validate_utxo_data([(utxo, priv)], retrieve=True, segwit=segwit) if not results: return False assert results[0][0] == utxo amt = results[0][1] ins = [utxo] # TODO extend to other utxo types txtype = 'p2sh-p2wpkh' if segwit else 'p2pkh' estfee = estimate_tx_fee(1, len(destaddrs), txtype=txtype) 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) amtforsign = amt if segwit else None return btc.sign(unsigned_tx, 0, btc.from_wif_privkey(priv, vbyte=get_p2pk_vbyte()), amount=amtforsign)
def create_tx_and_offerlist(cj_addr, cj_change_addr, other_output_scripts, cj_script=None, cj_change_script=None, offertype='swreloffer'): assert len(other_output_scripts) % 2 == 0, "bug in test" cj_value = 100000000 maker_total_value = cj_value * 3 if cj_script is None: cj_script = btc.address_to_script(cj_addr) if cj_change_script is None: cj_change_script = btc.address_to_script(cj_change_addr) inputs = create_tx_inputs(3) outputs = create_tx_outputs( (cj_script, cj_value), (cj_change_script, maker_total_value - cj_value), # cjfee=0, txfee=0 *((script, cj_value + (i%2)*(50000000+i)) \ for i, script in enumerate(other_output_scripts)) ) maker_utxos = [inputs[0]] tx = btc.deserialize(btc.mktx(inputs, outputs)) offerlist = construct_tx_offerlist(cj_addr, cj_change_addr, maker_utxos, maker_total_value, cj_value, offertype) return tx, offerlist
def test_spend_p2wpkh(setup_tx_creation): #make 3 p2wpkh outputs from 3 privs privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 4)] pubs = [bitcoin.privkey_to_pubkey(priv) for priv in privs] scriptPubKeys = [bitcoin.pubkey_to_p2wpkh_script(pub) for pub in pubs] addresses = [str(bitcoin.CCoinAddress.from_scriptPubKey( spk)) for spk in scriptPubKeys] #pay into it wallet_service = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet'] wallet_service.sync_wallet(fast=True) amount = 35000000 p2wpkh_ins = [] for i, addr in enumerate(addresses): ins_full = wallet_service.select_utxos(0, amount) txid = make_sign_and_push(ins_full, wallet_service, amount, output_addr=addr) assert txid p2wpkh_ins.append((txid, 0)) txhex = jm_single().bc_interface.get_transaction(txid) #wait for mining jm_single().bc_interface.tick_forward_chain(1) #random output address output_addr = wallet_service.get_internal_addr(1) amount2 = amount*3 - 50000 outs = [{'value': amount2, 'address': output_addr}] tx = bitcoin.mktx(p2wpkh_ins, outs) for i, priv in enumerate(privs): # sign each of 3 inputs; note that bitcoin.sign # automatically validates each signature it creates. sig, msg = bitcoin.sign(tx, i, priv, amount=amount, native="p2wpkh") if not sig: assert False, msg txid = jm_single().bc_interface.pushtx(tx.serialize()) assert txid
def create_single_acp_pair(utxo_in, priv, addr_out, amount, bump, segwit=False): """Given a utxo and a signing key for it, and its amout in satoshis, sign a "transaction" consisting of only 1 input and one output, signed with single|acp sighash flags so it can be grafted into a bigger transaction. Also provide a destination address and a 'bump' value (so the creator can claim more output in the final transaction. Note that, for safety, bump *should* be positive if the recipient is untrusted, since otherwise they can waste your money by simply broadcasting this transaction without adding any inputs of their own. Returns the serialized 1 in, 1 out, and signed transaction. """ assert bump >= 0, "Output of single|acp pair must be bigger than input for safety." out = {"address": addr_out, "value": amount + bump} tx = btc.mktx([utxo_in], [out]) amt = amount if segwit else None return btc.sign(tx, 0, priv, hashcode=btc.SIGHASH_SINGLE | btc.SIGHASH_ANYONECANPAY, amount=amt)
def test_timelocked_output_signing(setup_wallet): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') ensure_bip65_activated() storage = VolatileStorage() SegwitLegacyWalletFidelityBonds.initialize(storage, get_network()) wallet = SegwitLegacyWalletFidelityBonds(storage) index = 0 timenumber = 0 script = wallet.get_script_and_update_map( FidelityBondMixin.FIDELITY_BOND_MIXDEPTH, FidelityBondMixin.BIP32_TIMELOCK_ID, index, timenumber) utxo = fund_wallet_addr(wallet, wallet.script_to_addr(script)) timestamp = wallet._time_number_to_timestamp(timenumber) tx = btc.mktx([utxo], [{ "address": str( btc.CCoinAddress.from_scriptPubKey( btc.standard_scripthash_scriptpubkey(btc.Hash160(b"\x00")))), "value": 10**8 - 9000 }], locktime=timestamp + 1) success, msg = wallet.sign_tx(tx, {0: (script, 10**8)}) assert success, msg txout = jm_single().bc_interface.pushtx(tx.serialize()) assert txout
def test_add_new_utxos(setup_wallet): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') wallet = get_populated_wallet(num=1) scripts = [wallet.get_new_script(x, True) for x in range(3)] tx_scripts = list(scripts) tx_scripts.append(b'\x22' * 17) tx = btc.deserialize( btc.mktx(['0' * 64 + ':2'], [{ 'script': hexlify(s).decode('ascii'), 'value': 10**8 } for s in tx_scripts])) binarize_tx(tx) txid = b'\x01' * 32 added = wallet.add_new_utxos_(tx, txid) assert len(added) == len(scripts) added_scripts = {x['script'] for x in added.values()} for s in scripts: assert s in added_scripts balances = wallet.get_balance_by_mixdepth() assert balances[0] == 2 * 10**8 assert balances[1] == 10**8 assert balances[2] == 10**8 assert len(balances) == wallet.max_mixdepth + 1
def test_remove_old_utxos(setup_wallet): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') wallet = get_populated_wallet() # add some more utxos to mixdepth 1 for i in range(3): txin = jm_single().bc_interface.grab_coins(wallet.get_internal_addr(1), 1) wallet.add_utxo(unhexlify(txin), 0, wallet.get_script(1, 1, i), 10**8) inputs = wallet.select_utxos_(0, 10**8) inputs.update(wallet.select_utxos_(1, 2 * 10**8)) assert len(inputs) == 3 tx_inputs = list(inputs.keys()) tx_inputs.append((b'\x12' * 32, 6)) tx = btc.deserialize( btc.mktx([ '{}:{}'.format(hexlify(txid).decode('ascii'), i) for txid, i in tx_inputs ], ['0' * 36 + ':' + str(3 * 10**8 - 1000)])) binarize_tx(tx) removed = wallet.remove_old_utxos_(tx) assert len(removed) == len(inputs) for txid in removed: assert txid in inputs balances = wallet.get_balance_by_mixdepth() assert balances[0] == 2 * 10**8 assert balances[1] == 10**8 assert balances[2] == 0 assert len(balances) == wallet.max_mixdepth + 1
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 = [ bitcoin.privkey_to_pubkey(binascii.hexlify(priv)) for priv in privs ] script = bitcoin.mk_multisig_script(pubs, 2) msig_addr = bitcoin.scriptaddr(script, magicbyte=196) #pay into it wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet'] 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(1) #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 = bitcoin.mktx(ins, outs) sigs = [] for priv in privs[:2]: sigs.append(bitcoin.multisign(tx, 0, script, binascii.hexlify(priv))) tx = bitcoin.apply_multisignatures(tx, 0, script, sigs) txid = jm_single().bc_interface.pushtx(tx) assert txid
def test_remove_old_utxos(setup_wallet): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') wallet = get_populated_wallet() # add some more utxos to mixdepth 1 for i in range(3): txin = jm_single().bc_interface.grab_coins( wallet.get_internal_addr(1), 1) wallet.add_utxo(btc.x(txin), 0, wallet.get_script(1, BaseWallet.ADDRESS_TYPE_INTERNAL, i), 10**8, 1) inputs = wallet.select_utxos(0, 10**8) inputs.update(wallet.select_utxos(1, 2 * 10**8)) assert len(inputs) == 3 tx_inputs = list(inputs.keys()) tx_inputs.append((b'\x12'*32, 6)) tx = btc.mktx(tx_inputs, [{"address": "2N9gfkUsFW7Kkb1Eurue7NzUxUt7aNJiS1U", "value": 3 * 10**8 - 1000}]) removed = wallet.remove_old_utxos(tx) assert len(removed) == len(inputs) for txid in removed: assert txid in inputs balances = wallet.get_balance_by_mixdepth() assert balances[0] == 2 * 10**8 assert balances[1] == 10**8 assert balances[2] == 0 assert len(balances) == wallet.max_mixdepth + 1
def test_spend_p2wsh(setup_tx_creation): #make 2 x 2 of 2multisig outputs; will need 4 privs privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 5)] privs = [binascii.hexlify(priv).decode('ascii') for priv in privs] pubs = [bitcoin.privkey_to_pubkey(priv) for priv in privs] redeemScripts = [ bitcoin.mk_multisig_script(pubs[i:i + 2], 2) for i in [0, 2] ] scriptPubKeys = [ bitcoin.pubkeys_to_p2wsh_script(pubs[i:i + 2]) for i in [0, 2] ] addresses = [ bitcoin.pubkeys_to_p2wsh_address(pubs[i:i + 2]) for i in [0, 2] ] #pay into it wallet_service = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet'] wallet_service.sync_wallet(fast=True) amount = 35000000 p2wsh_ins = [] for addr in addresses: ins_full = wallet_service.select_utxos(0, amount) txid = make_sign_and_push(ins_full, wallet_service, amount, output_addr=addr) assert txid p2wsh_ins.append(txid + ":0") #wait for mining time.sleep(1) #random output address and change addr output_addr = wallet_service.get_internal_addr(1) amount2 = amount * 2 - 50000 outs = [{'value': amount2, 'address': output_addr}] tx = bitcoin.mktx(p2wsh_ins, outs) sigs = [] for i in range(2): sigs = [] for priv in privs[i * 2:i * 2 + 2]: # sign input j with each of 2 keys sig = bitcoin.multisign(tx, i, redeemScripts[i], priv, amount=amount) sigs.append(sig) # check that verify_tx_input correctly validates; assert bitcoin.verify_tx_input(tx, i, scriptPubKeys[i], sig, bitcoin.privkey_to_pubkey(priv), scriptCode=redeemScripts[i], amount=amount) tx = bitcoin.apply_p2wsh_multisignatures(tx, i, redeemScripts[i], sigs) txid = jm_single().bc_interface.pushtx(tx) assert txid
def test_spend_p2wpkh(setup_tx_creation): #make 3 p2wpkh outputs from 3 privs privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 4)] pubs = [ bitcoin.privkey_to_pubkey(binascii.hexlify(priv).decode('ascii')) for priv in privs ] scriptPubKeys = [bitcoin.pubkey_to_p2wpkh_script(pub) for pub in pubs] addresses = [bitcoin.pubkey_to_p2wpkh_address(pub) for pub in pubs] #pay into it wallet_service = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet'] wallet_service.sync_wallet(fast=True) amount = 35000000 p2wpkh_ins = [] for addr in addresses: ins_full = wallet_service.select_utxos(0, amount) txid = make_sign_and_push(ins_full, wallet_service, amount, output_addr=addr) assert txid p2wpkh_ins.append(txid + ":0") #wait for mining time.sleep(1) #random output address output_addr = wallet_service.get_internal_addr(1) amount2 = amount * 3 - 50000 outs = [{'value': amount2, 'address': output_addr}] tx = bitcoin.mktx(p2wpkh_ins, outs) sigs = [] for i, priv in enumerate(privs): # sign each of 3 inputs tx = bitcoin.p2wpkh_sign(tx, i, binascii.hexlify(priv), amount, native=True) # check that verify_tx_input correctly validates; # to do this, we need to extract the signature and get the scriptCode # of this pubkey scriptCode = bitcoin.pubkey_to_p2pkh_script(pubs[i]) witness = bitcoin.deserialize(tx)['ins'][i]['txinwitness'] assert len(witness) == 2 assert witness[1] == pubs[i] sig = witness[0] assert bitcoin.verify_tx_input(tx, i, scriptPubKeys[i], sig, pubs[i], scriptCode=scriptCode, amount=amount) txid = jm_single().bc_interface.pushtx(tx) assert txid
def test_create_and_sign_psbt_with_legacy(setup_psbt_wallet): """ The purpose of this test is to check that we can create and then partially sign a PSBT where we own one input and the other input is of legacy p2pkh type. """ wallet_service = make_wallets(1, [[1, 0, 0, 0, 0]], 1)[0]['wallet'] wallet_service.sync_wallet(fast=True) utxos = wallet_service.select_utxos(0, bitcoin.coins_to_satoshi(0.5)) assert len(utxos) == 1 # create a legacy address and make a payment into it legacy_addr = bitcoin.CCoinAddress.from_scriptPubKey( bitcoin.pubkey_to_p2pkh_script(bitcoin.privkey_to_pubkey(b"\x01" * 33))) tx = direct_send(wallet_service, bitcoin.coins_to_satoshi(0.3), 0, str(legacy_addr), accept_callback=dummy_accept_callback, info_callback=dummy_info_callback, return_transaction=True) assert tx # this time we will have one utxo worth <~ 0.7 my_utxos = wallet_service.select_utxos(0, bitcoin.coins_to_satoshi(0.5)) assert len(my_utxos) == 1 # find the outpoint for the legacy address we're spending n = -1 for i, t in enumerate(tx.vout): if bitcoin.CCoinAddress.from_scriptPubKey( t.scriptPubKey) == legacy_addr: n = i assert n > -1 utxos = copy.deepcopy(my_utxos) utxos[(tx.GetTxid()[::-1], n)] = { "script": legacy_addr.to_scriptPubKey(), "value": bitcoin.coins_to_satoshi(0.3) } outs = [{ "value": bitcoin.coins_to_satoshi(0.998), "address": wallet_service.get_addr(0, 0, 0) }] tx2 = bitcoin.mktx(list(utxos.keys()), outs) spent_outs = wallet_service.witness_utxos_to_psbt_utxos(my_utxos) spent_outs.append(tx) new_psbt = wallet_service.create_psbt_from_tx(tx2, spent_outs, force_witness_utxo=False) signed_psbt_and_signresult, err = wallet_service.sign_psbt( new_psbt.serialize(), with_sign_result=True) assert err is None signresult, signed_psbt = signed_psbt_and_signresult assert signresult.num_inputs_signed == 1 assert signresult.num_inputs_final == 1 assert not signresult.is_final
def create_coinjoin_proposal(bobdata, alicedata, verbose=True, incentive=0): """A very crude/static implementation of a coinjoin for SNICKER. **VERY DELIBERATELY STUPIDLY SIMPLE VERSION!** We assume only one utxo for each side (this will certainly change, Alice side, for flexibility). Two outputs equal size are created with 1 change for Alice also (Bob's utxo is completely satisfied by 1 output). The data for each side is utxo, and amount; alice must provide privkey for partial sign. All scriptpubkeys assumed p2sh/p2wpkh for now. Bob's destination is tweaked and included as a destination which he will verify. What is returned is (tweak, partially signed tx) which is enough information for Bob to complete. """ fee = estimate_tx_fee(2, 3, 'p2sh-p2wpkh') bob_utxo, bob_pubkey, amount = bobdata alice_utxo, alice_privkey, alice_amount, alice_destination, change = alicedata ins = [bob_utxo, alice_utxo] random.shuffle(ins) tweak, dest_pt, bob_destination = create_recipient_address(bob_pubkey, segwit=True) print('using amount, alice_amount,incentive, fee: ' + ','.join([str(x) for x in [amount, alice_amount, incentive, fee]])) coinjoin_amount = amount + incentive change_amount = alice_amount - coinjoin_amount - incentive - fee outs = [{ "address": alice_destination, "value": coinjoin_amount }, { "address": bob_destination, "value": coinjoin_amount }, { "address": change, "value": change_amount }] random.shuffle(outs) unsigned_tx = btc.mktx(ins, outs) if verbose: print('here is proposed transaction:\n', pformat(btc.deserialize(unsigned_tx))) print('destination for Bob: ', bob_destination) print('destination for Alice: ', alice_destination) print('destination for Alice change: ', change) if not raw_input("Is this acceptable? (y/n):") == "y": return (None, None) #Alice signs her input; assuming segwit here for now partially_signed_tx = btc.sign(unsigned_tx, 1, alice_privkey, amount=alice_amount) #return the material to be sent to Bob return (tweak, partially_signed_tx)
def make_sign_and_push(ins_full, wallet_service, amount, output_addr=None, change_addr=None, hashcode=btc.SIGHASH_ALL, estimate_fee=False): """Utility function for easily building transactions from wallets. `ins_full` should be a list of dicts in format returned by wallet.select_utxos: {(txid, index): {"script":..,"value":..,"path":..}} ... although the path is not used. The "script" and "value" data is used to allow signing. """ assert isinstance(wallet_service, WalletService) total = sum(x['value'] for x in ins_full.values()) ins = list(ins_full.keys()) #random output address and change addr output_addr = wallet_service.get_new_addr( 1, BaseWallet.ADDRESS_TYPE_INTERNAL) if not output_addr else output_addr change_addr = wallet_service.get_new_addr( 0, BaseWallet.ADDRESS_TYPE_INTERNAL) 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) scripts = {} for i, j in enumerate(ins): scripts[i] = (ins_full[j]["script"], ins_full[j]["value"]) success, msg = wallet_service.sign_tx(tx, scripts, hashcode=hashcode) if not success: return False #pushtx returns False on any error push_succeed = jm_single().bc_interface.pushtx(tx.serialize()) if push_succeed: # in normal operation this happens automatically # but in some tests there is no monitoring loop: wallet_service.process_new_tx(tx) return tx.GetTxid()[::-1] else: return False
def test_all_same_priv(setup_tx_creation): #recipient priv = "aa" * 32 + "01" addr = bitcoin.privkey_to_address(priv, magicbyte=get_p2pk_vbyte()) wallet = make_wallets(1, [[1, 0, 0, 0, 0]], 1)[0]['wallet'] #make another utxo on the same address addrinwallet = wallet.get_addr(0, 0, 0) jm_single().bc_interface.grab_coins(addrinwallet, 1) sync_wallet(wallet) insfull = wallet.select_utxos(0, 110000000) outs = [{"address": addr, "value": 1000000}] ins = insfull.keys() tx = bitcoin.mktx(ins, outs) tx = bitcoin.signall(tx, wallet.get_key_from_addr(addrinwallet))
def test_signing_simple(setup_wallet, wallet_cls, type_check): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') storage = VolatileStorage() wallet_cls.initialize(storage, get_network()) wallet = wallet_cls(storage) utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0)) tx = btc.deserialize(btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])], ['00'*17 + ':' + str(10**8 - 9000)])) binarize_tx(tx) script = wallet.get_script(0, 1, 0) wallet.sign_tx(tx, {0: (script, 10**8)}) type_check(tx) txout = jm_single().bc_interface.pushtx(hexlify(btc.serialize(tx)).decode('ascii')) assert txout
def test_signing_simple(setup_wallet, wallet_cls, type_check): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') storage = VolatileStorage() wallet_cls.initialize(storage, get_network()) wallet = wallet_cls(storage) utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0)) # The dummy output is constructed as an unspendable p2sh: tx = btc.deserialize(btc.mktx(['{}:{}'.format( hexlify(utxo[0]).decode('ascii'), utxo[1])], [btc.p2sh_scriptaddr(b"\x00",magicbyte=196) + ':' + str(10**8 - 9000)])) script = wallet.get_script(0, 1, 0) tx = wallet.sign_tx(tx, {0: (script, 10**8)}) type_check(tx) txout = jm_single().bc_interface.pushtx(btc.serialize(tx)) assert txout
def test_spend_freeze_script(setup_tx_creation): ensure_bip65_activated() wallet_service = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet'] wallet_service.sync_wallet(fast=True) mediantime = jm_single().bc_interface.rpc("getblockchaininfo", [])["mediantime"] timeoffset_success_tests = [(2, False), (-60 * 60 * 24 * 30, True), (60 * 60 * 24 * 30, False)] for timeoffset, required_success in timeoffset_success_tests: #generate keypair priv = b"\xaa" * 32 + b"\x01" pub = bitcoin.privkey_to_pubkey(priv) addr_locktime = mediantime + timeoffset redeem_script = bitcoin.mk_freeze_script(pub, addr_locktime) script_pub_key = bitcoin.redeem_script_to_p2wsh_script(redeem_script) # cannot convert to address within wallet service, as not known # to wallet; use engine directly: addr = wallet_service._ENGINE.script_to_address(script_pub_key) #fund frozen funds address amount = 100000000 funding_ins_full = wallet_service.select_utxos(0, amount) funding_txid = make_sign_and_push(funding_ins_full, wallet_service, amount, output_addr=addr) assert funding_txid #spend frozen funds frozen_in = (funding_txid, 0) output_addr = wallet_service.get_internal_addr(1) miner_fee = 5000 outs = [{'value': amount - miner_fee, 'address': output_addr}] tx = bitcoin.mktx([frozen_in], outs, locktime=addr_locktime + 1) i = 0 sig, success = bitcoin.sign(tx, i, priv, amount=amount, native=redeem_script) assert success push_success = jm_single().bc_interface.pushtx(tx.serialize()) assert push_success == required_success
def test_signing_simple(setup_wallet, wallet_cls, type_check): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') storage = VolatileStorage() wallet_cls.initialize(storage, get_network()) wallet = wallet_cls(storage) utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0)) # The dummy output is of length 25 bytes, because, for SegwitWallet, we else # trigger the tx-size-small DOS limit in Bitcoin Core (82 bytes is the # smallest "normal" transaction size (non-segwit size, ie no witness) tx = btc.deserialize( btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])], ['00' * 25 + ':' + str(10**8 - 9000)])) script = wallet.get_script(0, 1, 0) tx = wallet.sign_tx(tx, {0: (script, 10**8)}) type_check(tx) txout = jm_single().bc_interface.pushtx(btc.serialize(tx)) assert txout
def test_signing_imported(setup_wallet, wif, keytype, type_check): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') storage = VolatileStorage() SegwitLegacyWallet.initialize(storage, get_network()) wallet = SegwitLegacyWallet(storage) MIXDEPTH = 0 path = wallet.import_private_key(MIXDEPTH, wif, keytype) utxo = fund_wallet_addr(wallet, wallet.get_addr_path(path)) tx = btc.deserialize( btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])], ['00' * 17 + ':' + str(10**8 - 9000)])) script = wallet.get_script_path(path) tx = wallet.sign_tx(tx, {0: (script, 10**8)}) type_check(tx) txout = jm_single().bc_interface.pushtx(btc.serialize(tx)) assert txout
def test_signing_simple(setup_wallet, wallet_cls, type_check): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') storage = VolatileStorage() wallet_cls.initialize(storage, get_network(), entropy=b"\xaa"*16) wallet = wallet_cls(storage) utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0)) # The dummy output is constructed as an unspendable p2sh: tx = btc.mktx([utxo], [{"address": str(btc.CCoinAddress.from_scriptPubKey( btc.CScript(b"\x00").to_p2sh_scriptPubKey())), "value": 10**8 - 9000}]) script = wallet.get_script(0, BaseWallet.ADDRESS_TYPE_INTERNAL, 0) success, msg = wallet.sign_tx(tx, {0: (script, 10**8)}) assert success, msg type_check(tx) txout = jm_single().bc_interface.pushtx(tx.serialize()) assert txout
def make_sign_and_push(ins_full, wallet_service, amount, output_addr=None, change_addr=None, hashcode=btc.SIGHASH_ALL, estimate_fee=False): """Utility function for easily building transactions from wallets """ assert isinstance(wallet_service, WalletService) total = sum(x['value'] for x in ins_full.values()) ins = list(ins_full.keys()) #random output address and change addr output_addr = wallet_service.get_new_addr( 1, 1) if not output_addr else output_addr change_addr = wallet_service.get_new_addr( 0, 1) 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 }] de_tx = btc.deserialize(btc.mktx(ins, outs)) scripts = {} for index, ins in enumerate(de_tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) script = wallet_service.addr_to_script(ins_full[utxo]['address']) scripts[index] = (script, ins_full[utxo]['value']) binarize_tx(de_tx) de_tx = wallet_service.sign_tx(de_tx, scripts, hashcode=hashcode) #pushtx returns False on any error push_succeed = jm_single().bc_interface.pushtx(btc.serialize(de_tx)) if push_succeed: txid = btc.txhash(btc.serialize(de_tx)) # in normal operation this happens automatically # but in some tests there is no monitoring loop: wallet_service.process_new_tx(de_tx, txid) return txid else: return False
def make_sign_and_push(ins_full, wallet_service, amount, output_addr=None, change_addr=None, hashcode=btc.SIGHASH_ALL, estimate_fee=False): """Utility function for easily building transactions from wallets. """ assert isinstance(wallet_service, WalletService) total = sum(x['value'] for x in ins_full.values()) ins = ins_full.keys() #random output address and change addr output_addr = wallet_service.get_new_addr( 1, BaseWallet.ADDRESS_TYPE_INTERNAL) if not output_addr else output_addr change_addr = wallet_service.get_new_addr( 0, BaseWallet.ADDRESS_TYPE_INTERNAL) 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_service.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 test_signing_imported(setup_wallet, wif, keytype, type_check): jm_single().config.set('BLOCKCHAIN', 'network', 'testnet') storage = VolatileStorage() SegwitLegacyWallet.initialize(storage, get_network()) wallet = SegwitLegacyWallet(storage) MIXDEPTH = 0 path = wallet.import_private_key(MIXDEPTH, wif, keytype) utxo = fund_wallet_addr(wallet, wallet.get_address_from_path(path)) # The dummy output is constructed as an unspendable p2sh: tx = btc.deserialize(btc.mktx(['{}:{}'.format( hexlify(utxo[0]).decode('ascii'), utxo[1])], [btc.p2sh_scriptaddr(b"\x00",magicbyte=196) + ':' + str(10**8 - 9000)])) script = wallet.get_script_from_path(path) tx = wallet.sign_tx(tx, {0: (script, 10**8)}) type_check(tx) txout = jm_single().bc_interface.pushtx(btc.serialize(tx)) assert txout