def test_sign_p2sh(self): tx_out_script = h2b( "76a91491b24bf9f5288532960ac687abb035127b1d28a588ac") script = script_for_address("1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm") self.assertEqual(tx_out_script, script) tx_out = TxOut(100, tx_out_script) tx = Tx(1, [TxIn(b'\1' * 32, 1)], [TxOut(100, tx_out_script)]) tx.set_unspents([tx_out]) hl = build_hash160_lookup([1], [secp256k1_generator]) self.assertEqual(tx.bad_signature_count(), 1) tx.sign(hash160_lookup=hl) self.assertEqual(tx.bad_signature_count(), 0)
def test_p2sh_multisig_sequential_signing(self): raw_scripts = [ h2b("52210234abcffd2e80ad01c2ec0276ad02682808169c6fafdd25ebfb60703df272b461" "2102e5baaafff8094e4d77ce8b009d5ebc3de9110085ebd3d96e50cc7ce70faf175221" "0316ee25e80eb6e6fc734d9c86fa580cbb9c4bfd94a19f0373a22353ececd4db6853ae" ) ] spendable = { 'script_hex': 'a914c4ed4de526461e3efbb79c8b688a6f9282c0464687', 'does_seem_spent': 0, 'block_index_spent': 0, 'coin_value': 10000, 'block_index_available': 0, 'tx_out_index': 0, 'tx_hash_hex': '0ca152ba6b88db87a7ef1afd24554102aca1ab86cf2c10ccbc374472145dc943' } key_1 = 'Kz6pytJCigYHeMsGLmfHQPJhN5og2wpeSVrU43xWwgHLCAvpsprh' key_2 = 'Kz7NHgX7MBySA3RSKj9GexUSN6NepEDoPNugSPr5absRDoKgn2dT' for ordered_keys in [(key_1, key_2), (key_2, key_1)]: txs_in = [ TxIn(previous_hash=h2b( '43c95d14724437bccc102ccf86aba1ac02415524fd1aefa787db886bba52a10c' ), previous_index=0) ] txs_out = [ TxOut(10000, script_for_address('3KeGeLFmsbmbVdeMLrWp7WYKcA3tdsB4AR')) ] unspents = [Spendable.from_dict(spendable)] tx = Tx(version=DEFAULT_VERSION, txs_in=txs_in, txs_out=txs_out, unspents=unspents) for key in ordered_keys: self.assertEqual(tx.bad_signature_count(), 1) p2sh_lookup = build_p2sh_lookup(raw_scripts) tx.sign(LazySecretExponentDB([key], {}, [secp256k1_generator]), p2sh_lookup=p2sh_lookup) self.assertEqual(tx.bad_signature_count(), 0)
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)