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 on_tx_received(self, nick, txhex, offerinfo): """Called when the counterparty has sent an unsigned transaction. Sigs are created and returned if and only if the transaction passes verification checks (see verify_unsigned_tx()). """ try: tx = btc.deserialize(txhex) except (IndexError, SerializationError, SerializationTruncationError) as e: return (False, 'malformed txhex. ' + repr(e)) jlog.info('obtained tx\n' + pprint.pformat(tx)) goodtx, errmsg = self.verify_unsigned_tx(tx, offerinfo) if not goodtx: jlog.info('not a good tx, reason=' + errmsg) return (False, errmsg) jlog.info('goodtx') sigs = [] utxos = offerinfo["utxos"] our_inputs = {} for index, ins in enumerate(tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) if utxo not in utxos: continue script = self.wallet_service.addr_to_script(utxos[utxo]['address']) amount = utxos[utxo]['value'] our_inputs[index] = (script, amount) txs = self.wallet_service.sign_tx(btc.deserialize(unhexlify(txhex)), our_inputs) for index in our_inputs: sigmsg = unhexlify(txs['ins'][index]['script']) if 'txinwitness' in txs['ins'][index]: # Note that this flag only implies that the transaction # *as a whole* is using segwit serialization; it doesn't # imply that this specific input is segwit type (to be # fully general, we allow that even our own wallet's # inputs might be of mixed type). So, we catch the EngineError # which is thrown by non-segwit types. This way the sigmsg # will only contain the scriptSig field if the wallet object # decides it's necessary/appropriate for this specific input # If it is segwit, we prepend the witness data since we want # (sig, pub, witnessprogram=scriptSig - note we could, better, # pass scriptCode here, but that is not backwards compatible, # as the taker uses this third field and inserts it into the # transaction scriptSig), else (non-sw) the !sig message remains # unchanged as (sig, pub). try: scriptSig = btc.pubkey_to_p2wpkh_script(txs['ins'][index]['txinwitness'][1]) sigmsg = b''.join(btc.serialize_script_unit( x) for x in txs['ins'][index]['txinwitness'] + [scriptSig]) except IndexError: #the sigmsg was already set before the segwit check pass sigs.append(base64.b64encode(sigmsg).decode('ascii')) return (True, sigs)
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 cli_broadcast(wallet_name, partial_tx_hex): """Given a partially signed transaction retrieved by running this script with the -r flag, and assuming that the utxo with which the transaction was made is in a Joinmarket wallet, this function will complete the signing and then broadcast the transaction. This function is useful if the *receiver*'s wallet is Joinmarket; if it is Core then the workflow is just `signrawtransaction` then `sendrawtransaction`; should be similar for Electrum although haven't tried. """ wallet = cli_get_wallet(wallet_name) tx = btc.deserialize(partial_tx_hex) num_sigs = 0 for index, ins in enumerate(tx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) #is the utxo in our utxos? in_wallet_utxos = wallet.get_utxos_by_mixdepth(False) for m, um in in_wallet_utxos.iteritems(): for k, v in um.iteritems(): if k == utxo: print("Found utxo in mixdepth: ", m) if isinstance(wallet, SegwitWallet): amount = v['value'] else: amount = None signed_tx = btc.sign(partial_tx_hex, index, wallet.get_key_from_addr( v['address']), amount=amount) num_sigs += 1 if num_sigs != 1: print("Something wrong, expected to get 1 sig, got: ", num_sigs) return #should be fully signed; broadcast? print("Signed tx in hex:") print(signed_tx) print("In decoded form:") print(pformat(btc.deserialize(signed_tx))) if not raw_input("Broadcast to network? (y/n): ") == "y": print("You chose not to broadcast, quitting.") return txid = btc.txhash(signed_tx) print('txid = ' + txid) pushed = jm_single().bc_interface.pushtx(signed_tx) if not pushed: print("Broadcast failed.") else: print("Broadcast was successful.")
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 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 on_JM_TX_RECEIVED(self, nick, txhex, offer): # "none" flags p2ep protocol; pass through to the generic # on_tx handler for that: if offer == "none": return self.on_p2ep_tx_received(nick, txhex) offer = json.loads(offer) retval = self.client.on_tx_received(nick, txhex, offer) if not retval[0]: jlog.info("Maker refuses to continue on receipt of tx") else: sigs = retval[1] self.finalized_offers[nick] = offer tx = btc.deserialize(txhex) self.finalized_offers[nick]["txd"] = tx jm_single().bc_interface.add_tx_notify( tx, self.unconfirm_callback, self.confirm_callback, offer["cjaddr"], wallet_name=jm_single().bc_interface.get_wallet_name( self.client.wallet), txid_flag=False, vb=get_p2sh_vbyte()) d = self.callRemote(commands.JMTXSigs, nick=nick, sigs=json.dumps(sigs)) self.defaultCallbacks(d) return {"accepted": True}
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 get_tx_info(txid): """ Retrieve some basic information about the given transaction. :param txid: txid as hex-str :return: tuple is_coinjoin: bool cj_amount: int, only useful if is_coinjoin==True cj_n: int, number of cj participants, only useful if is_coinjoin==True output_script_values: {script: value} dict including all outputs blocktime: int, blocktime this tx was mined txd: deserialized transaction object (hex-encoded data) """ rpctx = jm_single().bc_interface.rpc('gettransaction', [txid]) txhex = str(rpctx['hex']) txd = btc.deserialize(txhex) output_script_values = { binascii.unhexlify(sv['script']): sv['value'] for sv in txd['outs'] } value_freq_list = sorted(Counter( output_script_values.values()).most_common(), key=lambda x: -x[1]) non_cj_freq = (0 if len(value_freq_list) == 1 else sum( next(islice(zip(*value_freq_list[1:]), 1, None)))) is_coinjoin = (value_freq_list[0][1] > 1 and value_freq_list[0][1] in [non_cj_freq, non_cj_freq + 1]) cj_amount = value_freq_list[0][0] cj_n = value_freq_list[0][1] return is_coinjoin, cj_amount, cj_n, output_script_values,\ rpctx.get('blocktime', 0), txd
def fund_wallet_addr(wallet, addr, value_btc=1): txin_id = jm_single().bc_interface.grab_coins(addr, value_btc) txinfo = jm_single().bc_interface.rpc('gettransaction', [txin_id]) txin = btc.deserialize(unhexlify(txinfo['hex'])) utxo_in = wallet.add_new_utxos_(txin, unhexlify(txin_id)) assert len(utxo_in) == 1 return list(utxo_in.keys())[0]
def cli_receive(filename): wif_privkey = raw_input("Enter private key in WIF compressed format: ") try: privkey = btc.from_wif_privkey(wif_privkey, vbyte=get_p2pk_vbyte()) except: print("Could not parse WIF privkey, quitting.") return amount = raw_input("Enter amount of utxo being spent, in satoshis: ") valid_coinjoins = scan_for_coinjoins(privkey, int(amount), filename) if not valid_coinjoins: print("Found no valid coinjoins") return for vc in valid_coinjoins: addr, priv, tx = vc print("Found signable coinjoin with destination address: ", addr) #TODO find a more sensible file naming fn = btc.txhash(tx) + ".txt" with open(fn, "wb") as f: f.write("SNICKER output file for receiver\n" "================================\n") f.write("The serialized transaction in hex:\n") f.write(tx + "\n") f.write("YOUR DESTINATION: " + addr + "\n") f.write("PRIVATE KEY FOR THIS DESTINATION ADDRESS:\n") f.write( btc.wif_compressed_privkey(priv, vbyte=get_p2pk_vbyte()) + "\n") f.write("The decoded transaction:\n") f.write(pformat(btc.deserialize(tx)) + "\n") print("The partially signed transaction and the private key for your " "output are stored in the file: " + fn) print( "Pass the transaction hex to `signrawtransaction` in Bitcoin Core " "or similar if you wish to broadcast the transaction.")
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_coinjoin_mixdepth_wrap_maker(monkeypatch, tmpdir, setup_cj): def raise_exit(i): raise Exception("sys.exit called") monkeypatch.setattr(sys, 'exit', raise_exit) set_commitment_file(str(tmpdir.join('commitments.json'))) MAKER_NUM = 2 wallet_services = make_wallets_to_list( make_wallets(MAKER_NUM + 1, wallet_structures=[[0, 0, 0, 0, 4]] * MAKER_NUM + [[3, 0, 0, 0, 0]], mean_amt=1)) for wallet_service in wallet_services: assert wallet_service.max_mixdepth == 4 jm_single().bc_interface.tickchain() jm_single().bc_interface.tickchain() sync_wallets(wallet_services) cj_fee = 2000 makers = [ YieldGeneratorBasic(wallet_services[i], [0, cj_fee, 0, 'swabsoffer', 10**7]) for i in range(MAKER_NUM) ] orderbook = create_orderbook(makers) assert len(orderbook) == MAKER_NUM cj_amount = int(1.1 * 10**8) # mixdepth, amount, counterparties, dest_addr, waittime, rounding schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)] taker = create_taker(wallet_services[-1], schedule, monkeypatch) active_orders, maker_data = init_coinjoin(taker, makers, orderbook, cj_amount) txdata = taker.receive_utxos(maker_data) assert txdata[0], "taker.receive_utxos error" taker_final_result = do_tx_signing(taker, makers, active_orders, txdata) assert taker_final_result is not False tx = btc.deserialize(txdata[2]) binarize_tx(tx) for i in range(MAKER_NUM): wallet_service = wallet_services[i] # TODO as above re: monitoring wallet_service.remove_old_utxos_(tx) wallet_service.add_new_utxos_(tx, b'\x00' * 32) # fake txid balances = wallet_service.get_balance_by_mixdepth() assert balances[0] == cj_amount assert balances[4] == 4 * 10**8 - cj_amount + cj_fee
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 sign_tx(wallet_service, tx, utxos): stx = deserialize(tx) our_inputs = {} for index, ins in enumerate(stx['ins']): utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) script = wallet_service.addr_to_script(utxos[utxo]['address']) amount = utxos[utxo]['value'] our_inputs[index] = (script, amount) return wallet_service.sign_tx(stx, our_inputs)
def test_segwit_valid_txs(setup_segwit): with open("test/tx_segwit_valid.json", "r") as f: json_data = f.read() valid_txs = json.loads(json_data) for j in valid_txs: if len(j) < 2: continue deserialized_tx = btc.deserialize(str(j[1])) print pformat(deserialized_tx) assert btc.serialize(deserialized_tx) == str(j[1])
def get_deser_from_gettransaction(self, rpcretval): """Get full transaction deserialization from a call to `gettransaction` """ if not "hex" in rpcretval: log.info("Malformed gettransaction output") return None #str cast for unicode hexval = str(rpcretval["hex"]) return btc.deserialize(hexval)
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 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 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 signature_form(self, index): """Construct the transaction template (confusingly, an entirely different notion of 'transaction template' to the one seen lower down in this module; here we're talking about the template for sighashing in Bitcoin) for input at index index. Can only be called after all the keys for this input have been set, so that the signing redeem script exists; otherwise an Exception is raised. """ assert self.signing_redeem_scripts[index] return btc.segwit_signature_form(btc.deserialize(self.base_form), index, self.signing_redeem_scripts[index], self.ins[index][1])
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 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 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
def test_serialization_roundtrip2(): #Data extracted from: #https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json #These are a variety of rather strange edge case transactions, which are #still valid. #Note that of course this is only a serialization, not validity test, so #only currently of very limited significance with open(os.path.join(testdir, "tx_valid.json"), "r") as f: json_data = f.read() valid_txs = json.loads(json_data) for j in valid_txs: #ignore comment entries if len(j) < 2: continue print j deserialized = btc.deserialize(str(j[0])) print deserialized assert j[0] == btc.serialize(deserialized)
def find_secret_from_tx3_redeem(self, expected_txid=None): """Given a txid assumed to be a transaction which spends from TX1 (so must be TX3 whether ours or theirs, since this is the only doubly-signed tx), and assuming it has been spent from (so this function is only called if redeeming TX3 fails), find the redeeming transaction and extract the coinswap secret from its scriptSig(s). The secret is returned. If expected_txid is provided, checks that this is the redeeming txid, in which case returns "True". """ assert self.tx3.spending_tx deser_spending_tx = btc.deserialize(self.tx3.spending_tx) vins = deser_spending_tx['ins'] self.secret = get_secret_from_vin(vins, self.hashed_secret) if not self.secret: cslog.info("Critical error; TX3 spent but no " "coinswap secret was found.") return False return self.secret
def __str__(self): """Convenience function for showing tx in current state in human readable form. This is not an object serialization (see serialize). """ msg = [] tx = self.base_form if not self.fully_signed_tx: msg.append("Not fully signed") msg.append("Signatures: " + str(self.signatures)) if self.txid: msg.append("Txid: " + self.txid) else: msg.append("Fully signed.") if self.txid: msg.append("Txid: " + self.txid) tx = self.fully_signed_tx msg.append(tx) dtx = btc.deserialize(tx) return pformat(dtx) + "\n" + "\n".join(msg)
def on_JM_TX_RECEIVED(self, nick, txhex, offer): # "none" flags p2ep protocol; pass through to the generic # on_tx handler for that: if offer == "none": return self.on_p2ep_tx_received(nick, txhex) offer = json.loads(offer) retval = self.client.on_tx_received(nick, txhex, offer) if not retval[0]: jlog.info("Maker refuses to continue on receipt of tx") else: sigs = retval[1] self.finalized_offers[nick] = offer tx = btc.deserialize(txhex) self.finalized_offers[nick]["txd"] = tx txid = btc.txhash(btc.serialize(tx)) # we index the callback by the out-set of the transaction, # because the txid is not known until all scriptSigs collected # (hence this is required for Makers, but not Takers). # For more info see WalletService.transaction_monitor(): txinfo = tuple((x["script"], x["value"]) for x in tx["outs"]) self.client.wallet_service.register_callbacks( [self.unconfirm_callback], txinfo, "unconfirmed") self.client.wallet_service.register_callbacks( [self.confirm_callback], txinfo, "confirmed") task.deferLater( reactor, float(jm_single().config.getint("TIMEOUT", "unconfirm_timeout_sec")), self.client.wallet_service.check_callback_called, txinfo, self.unconfirm_callback, "unconfirmed", "transaction with outputs: " + str(txinfo) + " not broadcast.") d = self.callRemote(commands.JMTXSigs, nick=nick, sigs=json.dumps(sigs)) self.defaultCallbacks(d) return {"accepted": True}