def doit(num_ins, num_outs, fat=0): psbt = BasicPSBT() txn = Tx(2, [], []) for i in range(num_ins): h = TxIn(pack('4Q', 0, 0, 0, i), i) txn.txs_in.append(h) for i in range(num_outs): # random P2PKH scr = bytes([0x76, 0xa9, 0x14]) + pack( 'I', i + 1) + bytes(16) + bytes([0x88, 0xac]) h = TxOut((1E6 * i) if i else 1E8, scr) txn.txs_out.append(h) with BytesIO() as b: txn.stream(b) psbt.txn = b.getvalue() psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] if fat: for i in range(num_ins): psbt.inputs[i].utxo = os.urandom(fat) rv = BytesIO() psbt.serialize(rv) assert rv.tell() <= MAX_TXN_LEN, 'too fat' return rv.getvalue()
def spend_outputs(funding_psbt, finalized_txn, tweaker=None): # take details from PSBT that created a finalized txn (also provided) # and build a new PSBT that spends those change outputs. from pycoin.tx.Tx import Tx from pycoin.tx.TxOut import TxOut from pycoin.tx.TxIn import TxIn funding = Tx.from_bin(finalized_txn) b4 = BasicPSBT().parse(funding_psbt) # segwit change outputs only spendables = [(n, i) for n, i in enumerate(funding.tx_outs_as_spendable()) if i.script[0:2] == b'\x00\x14' and b4.outputs[n].bip32_paths ] #spendables = list(reversed(spendables)) random.shuffle(spendables) if tweaker: tweaker(spendables) nn = BasicPSBT() nn.inputs = [BasicPSBTInput(idx=i) for i in range(len(spendables))] nn.outputs = [BasicPSBTOutput(idx=0)] # copy input values from funding PSBT's output side for p_in, (f_out, sp) in zip(nn.inputs, [(b4.outputs[x], s) for x, s in spendables]): p_in.bip32_paths = f_out.bip32_paths p_in.witness_script = f_out.redeem_script with BytesIO() as fd: sp.stream(fd) p_in.witness_utxo = fd.getvalue() # build new txn: single output, no change, no miner fee act_scr = fake_dest_addr('p2wpkh') dest_out = TxOut(sum(s.coin_value for n, s in spendables), act_scr) txn = Tx(2, [s.tx_in() for _, s in spendables], [dest_out]) # put unsigned TXN into PSBT with BytesIO() as b: txn.stream(b) nn.txn = b.getvalue() with BytesIO() as rv: nn.serialize(rv) raw = rv.getvalue() open('debug/spend_outs.psbt', 'wb').write(raw) return nn, raw
def main(): the_hash = sys.argv[1] j = get_json_for_hash(the_hash) txs_in = [] for j_in in j.get("in"): txs_in.append(TxIn(h2b_rev(j_in["prev_out"]["hash"]), int(j_in["prev_out"]["n"]), tools.compile(j_in["scriptSig"]))) txs_out = [] for j_out in j.get("out"): txs_out.append(TxOut(int(float(j_out["value"]) * 1e8 + 0.5), tools.compile(j_out["scriptPubKey"]))) tx = Tx(int(j["ver"]), txs_in, txs_out, int(j["lock_time"])) assert tx.id() == the_hash s = io.BytesIO() tx.stream(s) v = s.getvalue() print(linebreak(binascii.b2a_base64(v).decode("utf8"), 72))
def main(): the_hash = sys.argv[1] j = get_json_for_hash(the_hash) txs_in = [] for j_in in j.get("in"): txs_in.append( TxIn(h2b_rev(j_in["prev_out"]["hash"]), int(j_in["prev_out"]["n"]), tools.compile(j_in["scriptSig"]))) txs_out = [] for j_out in j.get("out"): txs_out.append( TxOut(int(float(j_out["value"]) * 1e8 + 0.5), tools.compile(j_out["scriptPubKey"]))) tx = Tx(int(j["ver"]), txs_in, txs_out, int(j["lock_time"])) assert tx.id() == the_hash s = io.BytesIO() tx.stream(s) v = s.getvalue() print(linebreak(binascii.b2a_base64(v).decode("utf8"), 72))
def doit(num_ins, num_outs, master_xpub, subpath="0/%d", fee=10000): psbt = BasicPSBT() txn = Tx(2, [], []) # we have a key; use it to provide "plausible" value inputs from pycoin.key.BIP32Node import BIP32Node mk = BIP32Node.from_wallet_key(master_xpub) xfp = mk.fingerprint() psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] for i in range(num_ins): # make a fake txn to supply each of the inputs # - each input is 1BTC # addr where the fake money will be stored. subkey = mk.subkey_for_path(subpath % i) sec = subkey.sec() assert len(sec) == 33, "expect compressed" assert subpath[0:2] == '0/' psbt.inputs[i].bip32_paths[sec] = xfp + pack('<II', 0, i) # UTXO that provides the funding for to-be-signed txn supply = Tx(2, [TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)], []) scr = bytes([0x76, 0xa9, 0x14]) + subkey.hash160() + bytes( [0x88, 0xac]) supply.txs_out.append(TxOut(1E8, scr)) with BytesIO() as fd: supply.stream(fd) psbt.inputs[i].utxo = fd.getvalue() if 0: with BytesIO() as fd: supply.stream(fd, include_witness_data=True) psbt.inputs[i].witness_utxo = fd.getvalue() spendable = TxIn(supply.hash(), 0) txn.txs_in.append(spendable) for i in range(num_outs): # random P2PKH scr = bytes([0x76, 0xa9, 0x14]) + pack( 'I', i + 1) + bytes(16) + bytes([0x88, 0xac]) h = TxOut(round(((1E8 * num_ins) - fee) / num_outs, 4), scr) txn.txs_out.append(h) with BytesIO() as b: txn.stream(b) psbt.txn = b.getvalue() rv = BytesIO() psbt.serialize(rv) assert rv.tell() <= MAX_TXN_LEN, 'too fat' return rv.getvalue()
def doit(num_ins, num_outs, master_xpub, subpath="0/%d", fee=10000, outvals=None, segwit_in=False, outstyles=['p2pkh'], change_outputs=[]): psbt = BasicPSBT() txn = Tx(2, [], []) # we have a key; use it to provide "plausible" value inputs mk = BIP32Node.from_wallet_key(master_xpub) xfp = mk.fingerprint() psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] for i in range(num_ins): # make a fake txn to supply each of the inputs # - each input is 1BTC # addr where the fake money will be stored. subkey = mk.subkey_for_path(subpath % i) sec = subkey.sec() assert len(sec) == 33, "expect compressed" assert subpath[0:2] == '0/' psbt.inputs[i].bip32_paths[sec] = xfp + pack('<II', 0, i) # UTXO that provides the funding for to-be-signed txn supply = Tx(2, [TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)], []) scr = bytes([0x76, 0xa9, 0x14]) + subkey.hash160() + bytes( [0x88, 0xac]) supply.txs_out.append(TxOut(1E8, scr)) with BytesIO() as fd: if not segwit_in: supply.stream(fd) psbt.inputs[i].utxo = fd.getvalue() else: supply.txs_out[-1].stream(fd) psbt.inputs[i].witness_utxo = fd.getvalue() spendable = TxIn(supply.hash(), 0) txn.txs_in.append(spendable) for i in range(num_outs): # random P2PKH if not outstyles: style = ADDR_STYLES[i % len(ADDR_STYLES)] else: style = outstyles[i % len(outstyles)] if i in change_outputs: scr, act_scr, isw, pubkey, sp = make_change_addr(mk, style) psbt.outputs[i].bip32_paths[pubkey] = sp else: scr = act_scr = fake_dest_addr(style) isw = ('w' in style) #if style.endswith('sh'): assert scr act_scr = act_scr or scr if isw: psbt.outputs[i].witness_script = scr elif style.endswith('sh'): psbt.outputs[i].redeem_script = scr if not outvals: h = TxOut(round(((1E8 * num_ins) - fee) / num_outs, 4), act_scr) else: h = TxOut(outvals[i], act_scr) txn.txs_out.append(h) with BytesIO() as b: txn.stream(b) psbt.txn = b.getvalue() rv = BytesIO() psbt.serialize(rv) assert rv.tell() <= MAX_TXN_LEN, 'too fat' return rv.getvalue()
def doit(num_ins, num_outs, M, keys, fee=10000, outvals=None, segwit_in=False, outstyles=['p2pkh'], change_outputs=[], incl_xpubs=False): psbt = BasicPSBT() txn = Tx(2,[],[]) if incl_xpubs: # add global header with XPUB's # - assumes BIP45 for xfp, m, sk in keys: kk = pack('<II', xfp, 45|0x80000000) psbt.xpubs.append( (sk.serialize(as_private=False), kk) ) psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] for i in range(num_ins): # make a fake txn to supply each of the inputs # - each input is 1BTC # addr where the fake money will be stored. addr, scriptPubKey, script, details = make_ms_address(M, keys, idx=i) # lots of supporting details needed for p2sh inputs if segwit_in: psbt.inputs[i].witness_script = script else: psbt.inputs[i].redeem_script = script for pubkey, xfp_path in details: psbt.inputs[i].bip32_paths[pubkey] = b''.join(pack('<I', j) for j in xfp_path) # UTXO that provides the funding for to-be-signed txn supply = Tx(2,[TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)],[]) supply.txs_out.append(TxOut(1E8, scriptPubKey)) with BytesIO() as fd: if not segwit_in: supply.stream(fd) psbt.inputs[i].utxo = fd.getvalue() else: supply.txs_out[-1].stream(fd) psbt.inputs[i].witness_utxo = fd.getvalue() spendable = TxIn(supply.hash(), 0) txn.txs_in.append(spendable) for i in range(num_outs): # random P2PKH if not outstyles: style = ADDR_STYLES[i % len(ADDR_STYLES)] else: style = outstyles[i % len(outstyles)] if i in change_outputs: addr, scriptPubKey, scr, details = \ make_ms_address(M, keys, idx=i, addr_fmt=unmap_addr_fmt[style]) for pubkey, xfp_path in details: psbt.outputs[i].bip32_paths[pubkey] = b''.join(pack('<I', j) for j in xfp_path) if 'w' in style: psbt.outputs[i].witness_script = scr if style.endswith('p2sh'): psbt.outputs[i].redeem_script = b'\0\x20' + sha256(scr).digest() elif style.endswith('sh'): psbt.outputs[i].redeem_script = scr else: scr = fake_dest_addr(style) assert scr if not outvals: h = TxOut(round(((1E8*num_ins)-fee) / num_outs, 4), scriptPubKey) else: h = TxOut(outvals[i], scriptPubKey) txn.txs_out.append(h) with BytesIO() as b: txn.stream(b) psbt.txn = b.getvalue() rv = BytesIO() psbt.serialize(rv) assert rv.tell() <= MAX_TXN_LEN, 'too fat' return rv.getvalue()
def _base_size(tx: Tx) -> int: buffer = io.BytesIO() # type: io.BytesIO tx.stream(buffer) return len(buffer.getvalue())
def fake_txn(num_ins, num_outs, master_xpub=None, subpath="0/%d", fee=10000, outvals=None, segwit_in=False, outstyles=['p2pkh'], is_testnet=False, change_style='p2pkh', partial=False, change_outputs=[]): # make various size txn's ... completely fake and pointless values # - but has UTXO's to match needs # - input total = num_inputs * 1BTC from pycoin.tx.Tx import Tx from pycoin.tx.TxIn import TxIn from pycoin.tx.TxOut import TxOut from pycoin.serialize import h2b_rev from struct import pack psbt = BasicPSBT() txn = Tx(2, [], []) # we have a key; use it to provide "plausible" value inputs mk = BIP32Node.from_wallet_key(master_xpub) xfp = mk.fingerprint() psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] outputs = [] for i in range(num_ins): # make a fake txn to supply each of the inputs # - each input is 1BTC # addr where the fake money will be stored. subkey = mk.subkey_for_path(subpath % i) sec = subkey.sec() assert len(sec) == 33, "expect compressed" assert subpath[0:2] == '0/' if partial and (i == 0): psbt.inputs[i].bip32_paths[sec] = b'Nope' + pack('<II', 0, i) else: psbt.inputs[i].bip32_paths[sec] = xfp + pack('<II', 0, i) # UTXO that provides the funding for to-be-signed txn supply = Tx(2, [TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)], []) scr = bytes([0x76, 0xa9, 0x14]) + subkey.hash160() + bytes( [0x88, 0xac]) supply.txs_out.append(TxOut(1E8, scr)) with BytesIO() as fd: if not segwit_in: supply.stream(fd) psbt.inputs[i].utxo = fd.getvalue() else: supply.txs_out[-1].stream(fd) psbt.inputs[i].witness_utxo = fd.getvalue() spendable = TxIn(supply.hash(), 0) txn.txs_in.append(spendable) for i in range(num_outs): is_change = False # random P2PKH if not outstyles: style = ADDR_STYLES[i % len(ADDR_STYLES)] else: style = outstyles[i % len(outstyles)] if i in change_outputs: scr, act_scr, isw, pubkey, sp = make_change_addr(mk, change_style) psbt.outputs[i].bip32_paths[pubkey] = sp is_change = True else: scr = act_scr = fake_dest_addr(style) isw = ('w' in style) assert scr act_scr = act_scr or scr if isw: psbt.outputs[i].witness_script = scr elif style.endswith('sh'): psbt.outputs[i].redeem_script = scr if not outvals: h = TxOut(round(((1E8 * num_ins) - fee) / num_outs, 4), act_scr) else: h = TxOut(outvals[i], act_scr) outputs.append( (Decimal(h.coin_value) / Decimal(1E8), act_scr, is_change)) txn.txs_out.append(h) with BytesIO() as b: txn.stream(b) psbt.txn = b.getvalue() rv = BytesIO() psbt.serialize(rv) return rv.getvalue(), [(n, render_address(s, is_testnet), ic) for n, s, ic in outputs]
def main(): parser = create_parser() args = parser.parse_args() (txs, spendables, payables, key_iters, p2sh_lookup, tx_db, warning_tx_cache, warning_tx_for_tx_hash, warning_spendables) = parse_context(args, parser) txs_in = [] txs_out = [] unspents = [] # we use a clever trick here to keep each tx_in corresponding with its tx_out for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[:smaller]) txs_out.extend(tx.txs_out[:smaller]) unspents.extend(tx.unspents[:smaller]) for tx in txs: smaller = min(len(tx.txs_in), len(tx.txs_out)) txs_in.extend(tx.txs_in[smaller:]) txs_out.extend(tx.txs_out[smaller:]) unspents.extend(tx.unspents[smaller:]) for spendable in spendables: txs_in.append(spendable.tx_in()) unspents.append(spendable) for address, coin_value in payables: script = standard_tx_out_script(address) txs_out.append(TxOut(coin_value, script)) lock_time = args.lock_time version = args.transaction_version # if no lock_time is explicitly set, inherit from the first tx or use default if lock_time is None: if txs: lock_time = txs[0].lock_time else: lock_time = DEFAULT_LOCK_TIME # if no version is explicitly set, inherit from the first tx or use default if version is None: if txs: version = txs[0].version else: version = DEFAULT_VERSION if args.remove_tx_in: s = set(args.remove_tx_in) txs_in = [tx_in for idx, tx_in in enumerate(txs_in) if idx not in s] if args.remove_tx_out: s = set(args.remove_tx_out) txs_out = [ tx_out for idx, tx_out in enumerate(txs_out) if idx not in s ] tx = Tx(txs_in=txs_in, txs_out=txs_out, lock_time=lock_time, version=version, unspents=unspents) fee = args.fee try: if len(payables) > 0: distribute_from_split_pool(tx, fee) except ValueError as ex: print("warning: %s" % ex.args[0], file=sys.stderr) unsigned_before = tx.bad_signature_count() unsigned_after = unsigned_before if unsigned_before > 0 and key_iters: def wif_iter(iters): while len(iters) > 0: for idx, iter in enumerate(iters): try: wif = next(iter) yield wif except StopIteration: iters = iters[:idx] + iters[idx + 1:] break print("signing...", file=sys.stderr) sign_tx(tx, wif_iter(key_iters), p2sh_lookup=p2sh_lookup) unsigned_after = tx.bad_signature_count() if unsigned_after > 0: print("warning: %d TxIn items still unsigned" % unsigned_after, file=sys.stderr) if len(tx.txs_in) == 0: print("warning: transaction has no inputs", file=sys.stderr) if len(tx.txs_out) == 0: print("warning: transaction has no outputs", file=sys.stderr) include_unspents = (unsigned_after > 0) tx_as_hex = tx.as_hex(include_unspents=include_unspents) if args.output_file: f = args.output_file if f.name.endswith(".hex"): f.write(tx_as_hex.encode("utf8")) else: tx.stream(f) if include_unspents: tx.stream_unspents(f) f.close() elif args.show_unspents: for spendable in tx.tx_outs_as_spendable(): print(spendable.as_text()) else: if not tx.missing_unspents(): check_fees(tx) dump_tx(tx, args.network, args.verbose_signature, args.disassemble, args.trace, args.pdb) if include_unspents: print( "including unspents in hex dump since transaction not fully signed" ) print(tx_as_hex) if args.cache: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env( args.network) tx_db = get_tx_db(args.network) tx_db.put(tx) if args.bitcoind_url: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env( args.network) tx_db = get_tx_db(args.network) validate_bitcoind(tx, tx_db, args.bitcoind_url) if tx.missing_unspents(): print("\n** can't validate transaction as source transactions missing", file=sys.stderr) else: try: if tx_db is None: warning_tx_cache = message_about_tx_cache_env() warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env( args.network) tx_db = get_tx_db(args.network) tx.validate_unspents(tx_db) print('all incoming transaction values validated') except BadSpendableError as ex: print("\n**** ERROR: FEES INCORRECTLY STATED: %s" % ex.args[0], file=sys.stderr) except Exception as ex: print( "\n*** can't validate source transactions as untampered: %s" % ex.args[0], file=sys.stderr) # print warnings for m in [warning_tx_cache, warning_tx_for_tx_hash, warning_spendables]: if m: print("warning: %s" % m, file=sys.stderr)
def calculate_tx_size(tx: Tx) -> int: s = io.BytesIO() tx.stream(s) return len(s.getvalue())