def tx_from_json_dict(r): version = r.get("version") lock_time = r.get("locktime") txs_in = [] for vin in r.get("vin"): if "coinbase" in vin: previous_hash = b'\0' * 32 script = h2b(vin.get("coinbase")) previous_index = 4294967295 else: previous_hash = h2b_rev(vin.get("txid")) scriptSig = vin.get("scriptSig") if "hex" in scriptSig: script = h2b(scriptSig.get("hex")) else: script = tools.compile(scriptSig.get("asm")) previous_index = vin.get("vout") sequence = vin.get("sequence") txs_in.append(TxIn(previous_hash, previous_index, script, sequence)) txs_out = [] for vout in r.get("vout"): coin_value = btc_to_satoshi(decimal.Decimal(vout.get("value"))) script = tools.compile(vout.get("scriptPubKey").get("asm")) txs_out.append(TxOut(coin_value, script)) tx = Tx(version, txs_in, txs_out, lock_time) bh = r.get("blockhash") if bh: bh = h2b_rev(bh) tx.confirmation_block_hash = bh return tx
def store_nulldata(service, testnet, nulldatatxout, keys, changeaddress=None, txouts=None, fee=10000, locktime=0, publish=True): # get required satoshis txouts = txouts if txouts else [] required = sum(list(map(lambda txout: txout.coin_value, txouts))) + fee # get txins addresses = list(map(lambda key: key.address(), keys)) txins, total = find_txins(service, addresses, required) if total < required: raise exceptions.InsufficientFunds(required, total) # setup txouts changeaddress = changeaddress if changeaddress else addresses[0] changeout = deserialize.txout(testnet, changeaddress, total - required) txouts = txouts + [nulldatatxout, changeout] # create, sign and publish tx tx = Tx(1, txins, txouts, locktime) tx = sign_tx(service, testnet, tx, keys) if publish: service.send_tx(tx) return tx.hash()
def check_bip143_tx(self, tx_u_hex, tx_s_hex, txs_out_value_scripthex_pair, tx_in_count, tx_out_count, version, lock_time): tx_u = Tx.from_hex(tx_u_hex) tx_s = Tx.from_hex(tx_s_hex) txs_out = [ TxOut(int(coin_value * 1e8), h2b(script_hex)) for coin_value, script_hex in txs_out_value_scripthex_pair ] for tx in (tx_u, tx_s): self.assertEqual(len(tx.txs_in), tx_in_count) self.assertEqual(len(tx.txs_out), tx_out_count) self.assertEqual(tx.version, version) self.assertEqual(tx.lock_time, lock_time) tx.set_unspents(txs_out) self.check_unsigned(tx_u) self.check_signed(tx_s) tx_hex = tx_u.as_hex() self.assertEqual(tx_hex, tx_u_hex) tx_hex = tx_s.as_hex() self.assertEqual(tx_hex, tx_s_hex) tx_u_prime = self.unsigned_copy(tx_s) tx_hex = tx_u_prime.as_hex() self.assertEqual(tx_hex, tx_u_hex) self.assertEqual(b2h_rev(double_sha256(h2b(tx_s_hex))), tx_s.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.id()) return tx_u, tx_s
def parse_tx(arg, parser, tx_db, network): # hex transaction id tx = None if TX_ID_RE.match(arg): if tx_db is None: tx_db = create_tx_db(network) tx = tx_db.get(h2b_rev(arg)) if not tx: parser.error("can't find Tx with id %s" % arg) return tx, tx_db # hex transaction data try: return Tx.from_hex(arg), tx_db except Exception: pass if os.path.exists(arg): try: with open(arg, "rb") as f: if f.name.endswith("hex"): f = io.BytesIO(codecs.getreader("hex_codec")(f).read()) tx = Tx.parse(f) tx.parse_unspents(f) except Exception: pass return tx, tx_db
def multisig_M_of_N_individually(self, M, N): keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) for partial_key_list in itertools.permutations(keys[:N], M): tx2 = create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) for key in partial_key_list: self.assertEqual(tx2.bad_signature_count(), 1) hash160_lookup = build_hash160_lookup([key.secret_exponent()]) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.bad_signature_count(), 0)
def multisig_M_of_N(self, M, N, unsigned_id, signed_id): keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) self.assertEqual(tx2.id(), unsigned_id) self.assertEqual(tx2.bad_signature_count(), 1) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.id(), signed_id) self.assertEqual(tx2.bad_signature_count(), 0)
def standard_tx(coins_from, coins_to): txs_in = [] unspents = [] for h, idx, tx_out in coins_from: txs_in.append(TxIn(h, idx)) unspents.append(tx_out) txs_out = [] for coin_value, bitcoin_address in coins_to: txs_out.append(TxOut(coin_value, standard_tx_out_script(bitcoin_address))) version, lock_time = 1, 0 tx = Tx(version, txs_in, txs_out, lock_time) tx.set_unspents(unspents) return tx
def test_sign_pay_to_script_multisig(self): M, N = 3, 3 keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') underlying_script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() address = address_for_pay_to_script(underlying_script) self.assertEqual(address, "39qEwuwyb2cAX38MFtrNzvq3KV9hSNov3q") script = standard_tx_out_script(address) tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [address]) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys[:N]) p2sh_lookup = build_p2sh_lookup([underlying_script]) tx2.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) self.assertEqual(tx2.bad_signature_count(), 0)
def main(): if len(sys.argv) != 2: print("usage: %s address" % sys.argv[0]) sys.exit(-1) # validate the address address = sys.argv[1] assert is_address_valid(address) print("creating coinbase transaction to %s" % address) tx_in = TxIn.coinbase_tx_in(script=b'') tx_out = TxOut(50*1e8, script_for_address(address)) tx = Tx(1, [tx_in], [tx_out]) print("Here is the tx as hex:\n%s" % tx.as_hex())
def test_tx_api(self): tx = Tx.tx_from_hex(TX_E1A18B843FC420734DEEB68FF6DF041A2585E1A0D7DBF3B82AAB98291A6D9952_HEX) # this transaction is a pay-to-hash transaction self.assertEqual(tx.id(), "e1a18b843fc420734deeb68ff6df041a2585e1a0d7dbf3b82aab98291a6d9952") self.assertEqual(tx.txs_out[0].bitcoin_address(), "19LemzJ3XPdUxp113uynqCAivDbXZBdBy3") # TODO: fix this when pay-to-hash properly parsed self.assertEqual(tx.txs_out[1].bitcoin_address(), "3KmkA7hvqG2wKkWUGz1BySioUywvcmdPLR")
def main(): if len(sys.argv) != 4: print("usage: %s tx-hex-file-path wif-file-path p2sh-file-path" % sys.argv[0]) sys.exit(-1) # get the tx with open(sys.argv[1], "r") as f: tx_hex = f.readline().strip() tx = Tx.from_hex(tx_hex) # get the WIF with open(sys.argv[2], "r") as f: wif = f.readline().strip() assert is_wif_valid(wif) # create the p2sh_lookup with open(sys.argv[3], "r") as f: p2sh_script_hex = f.readline().strip() p2sh_script = h2b(p2sh_script_hex) # build a dictionary of script hashes to scripts p2sh_lookup = build_p2sh_lookup([p2sh_script]) # sign the transaction with the given WIF sign_tx(tx, wifs=[wif], p2sh_lookup=p2sh_lookup) bad_signature_count = tx.bad_signature_count() print("tx %s now has %d bad signature(s)" % (tx.id(), bad_signature_count)) include_unspents = (bad_signature_count > 0) print("Here is the tx as hex:\n%s" % tx.as_hex(include_unspents=include_unspents))
def test_blanked_hash(self): tx = Tx.tx_from_hex( TX_E1A18B843FC420734DEEB68FF6DF041A2585E1A0D7DBF3B82AAB98291A6D9952_HEX ) self.assertEqual( tx.id(), "e1a18b843fc420734deeb68ff6df041a2585e1a0d7dbf3b82aab98291a6d9952") self.assertEqual( b2h(tx.blanked_hash()), "909579526c4c2c441687c7478d3f96249724d2ff071d2272b44500d6cf70d5d6") tx.txs_in[0].script = b"foo" self.assertEqual( b2h(tx.blanked_hash()), "909579526c4c2c441687c7478d3f96249724d2ff071d2272b44500d6cf70d5d6") tx.txs_out[0].coin_value += 1 self.assertEqual( b2h(tx.blanked_hash()), "10d4e87f7bf35f2949e7693e7a4a84189aad8631f0b2b0999e88f7261066cbe5") tx.txs_in[0].script = b"bar" self.assertEqual( b2h(tx.blanked_hash()), "10d4e87f7bf35f2949e7693e7a4a84189aad8631f0b2b0999e88f7261066cbe5") tx.txs_in[0].script = b"" self.assertEqual( b2h(tx.hash()), "10d4e87f7bf35f2949e7693e7a4a84189aad8631f0b2b0999e88f7261066cbe5") tx.txs_in[0].script = b"foo" self.assertEqual( b2h(tx.hash()), "c91910058722f1c0f52fc5c734939053c9b87882a9c72b609f21632e0bd13751")
def test_pay_to_script_file(self): the_dir = self.set_cache_dir() p2sh_file = tempfile.NamedTemporaryFile() p2sh_file.write( "52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817982102c60" "47f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52102f9308a019258" "c31049344f85f89d5229b531c845836f99b08601f113bce036f953ae\n".encode("utf8")) p2sh_file.write( "53210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817982102c60" "47f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52102f9308a019258" "c31049344f85f89d5229b531c845836f99b08601f113bce036f953ae\n".encode("utf8")) p2sh_file.flush() tx_source_hex = ( "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0" "0ffffffff0200f902950000000017a91415fc0754e73eb85d1cbce08786fadb7320ecb8dc8700f90295" "0000000017a914594f349df0bac3084ffea8a477bba5f03dcd45078700000000") self.launch_tool("tx -C %s" % tx_source_hex) tx_to_sign = ( "01000000020a316ea8980ef9ba02f4e6637c88229bf059f39b06238d48d06a8e" "f672aea2bb0000000000ffffffff0a316ea8980ef9ba02f4e6637c88229bf059" "f39b06238d48d06a8ef672aea2bb0100000000ffffffff01f0ca052a01000000" "1976a914751e76e8199196d454941c45d1b3a323f1433bd688ac0000000000f9" "02950000000017a91415fc0754e73eb85d1cbce08786fadb7320ecb8dc8700f9" "02950000000017a914594f349df0bac3084ffea8a477bba5f03dcd450787") wifs = ' '.join(Key(_).wif() for _ in (1, 2, 3)) signed = tempfile.mktemp(suffix=".hex") self.launch_tool("tx -a -P %s --db %s %s %s -o %s" % ( p2sh_file.name, tx_source_hex, tx_to_sign, wifs, signed), env=dict(PYCOIN_CACHE_DIR=the_dir)) tx = Tx.from_hex(open(signed).read()) self.assertEqual(tx.id(), "9d991ddccf77e33cb4584e4fc061a36da0da43589232b2e78a1aa0748ac3254b")
def test_sign_pay_to_script_multisig(self): M, N = 3, 3 keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') underlying_script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() address = address_for_pay_to_script(underlying_script) self.assertEqual(address, "39qEwuwyb2cAX38MFtrNzvq3KV9hSNov3q") script = standard_tx_out_script(address) tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [address]) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys[:N]) p2sh_lookup = build_p2sh_lookup([underlying_script]) tx2.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) self.assertEqual(tx2.bad_signature_count(), 0) self.assertRaises(who_signed.NoAddressesForScriptTypeError, who_signed.who_signed_tx, tx2, 0)
def doit(accept=True, in_psbt=None, finalize=False): if accept != None: need_keypress('y' if accept else 'x') if accept == False: with pytest.raises(CCUserRefused): done = None while done == None: time.sleep(0.050) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) return else: done = None while done == None: time.sleep(0.050) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) assert len(done) == 2 resp_len, chk = done psbt_out = dev.download_file(resp_len, chk) if not finalize: if in_psbt: assert BasicPSBT().parse(in_psbt) == BasicPSBT().parse(psbt_out) else: from pycoin.tx.Tx import Tx # parse it res = psbt_out assert res[0:4] != b'psbt' t = Tx.from_bin(res) assert t.version in [1, 2] return psbt_out
def test_change_fraud_addr(start_sign, end_sign, check_against_bitcoind, cap_story): # fraud: BIP32 path of output doesn't match TXO address from pycoin.tx.Tx import Tx from pycoin.tx.TxOut import TxOut # NOTE: out#1 is change: #chg_addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo' psbt = open('data/example-change.psbt', 'rb').read() b4 = BasicPSBT().parse(psbt) # tweak output addr to garbage t = Tx.parse(BytesIO(b4.txn)) chg = t.txs_out[1] # pycoin.tx.TxOut.TxOut b = bytearray(chg.script) b[-5] ^= 0x55 chg.script = bytes(b) b4.txn = t.as_bin() with BytesIO() as fd: b4.serialize(fd) mod_psbt = fd.getvalue() open('debug/mod-addr.psbt', 'wb').write(mod_psbt) start_sign(mod_psbt) with pytest.raises(CCProtoError) as ee: signed = end_sign(True) assert 'change output is fraud' in str(ee)
def test_sign_bitcoind_partially_signed_2_of_2(self): # Finish signing a 2 of 2 transaction, that already has one signature signed by bitcoind # This tx can be found on testnet3 blockchain # txid: 9618820d7037d2f32db798c92665231cd4599326f5bd99cb59d0b723be2a13a2 raw_script = ( "522103e33b41f5ed67a77d4c4c54b3e946bd30d15b8f66e42cb29fde059c168851165521" "02b92cb20a9fb1eb9656a74eeb7387636cf64cdf502ff50511830328c1b479986452ae" ) p2sh_lookup = build_p2sh_lookup([h2b(raw_script)]) partially_signed_raw_tx = ( "010000000196238f11a5fd3ceef4efd5a186a7e6b9217d900418e72aca917cd6a6e634" "e74100000000910047304402201b41b471d9dd93cf97eed7cfc39a5767a546f6bfbf3e" "0c91ff9ad23ab9770f1f02205ce565666271d055be1f25a7e52e34cbf659f6c70770ff" "59bd783a6fcd1be3dd0147522103e33b41f5ed67a77d4c4c54b3e946bd30d15b8f66e4" "2cb29fde059c16885116552102b92cb20a9fb1eb9656a74eeb7387636cf64cdf502ff5" "0511830328c1b479986452aeffffffff01a0bb0d00000000001976a9143b3beefd6f78" "02fa8706983a76a51467bfa36f8b88ac00000000") tx = Tx.from_hex(partially_signed_raw_tx) tx_out = TxOut(1000000, h2b("a914a10dfa21ee8c33b028b92562f6fe04e60563d3c087")) tx.set_unspents([tx_out]) key = key_from_text( "cThRBRu2jAeshWL3sH3qbqdq9f4jDiDbd1SVz4qjTZD2xL1pdbsx") hash160_lookup = build_hash160_lookup([key.secret_exponent()], [secp256k1_generator]) self.assertEqual(tx.bad_signature_count(), 1) tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) self.assertEqual(tx.bad_signature_count(), 0) self.assertEqual( tx.id(), "9618820d7037d2f32db798c92665231cd4599326f5bd99cb59d0b723be2a13a2")
def test_txid_calc(num_ins, fake_txn, try_sign, dev, segwit, decode_with_bitcoind, cap_story): # verify correct txid for transactions is being calculated xp = dev.master_xpub psbt = fake_txn(num_ins, 1, xp, segwit_in=segwit) _, txn = try_sign(psbt, accept=True, finalize=True) #print('Signed; ' + B2A(txn)) time.sleep(.1) title, story = cap_story() assert '0' in story assert 'TXID' in title, story txid = story.strip() if 1: # compare to PyCoin from pycoin.tx.Tx import Tx t = Tx.from_bin(txn) assert t.id() == txid if 1: # compare to bitcoin core decoded = decode_with_bitcoind(txn) pprint(decoded) assert len(decoded['vin']) == num_ins if segwit: assert all(x['txinwitness'] for x in decoded['vin']) assert decoded['txid'] == txid
async def _get_utxos(self, address: str) -> List[Spendable]: """ Coroutine. Returns a list of pycoin.tx.Spendable objects for all UTXOS associated with the given address :param address: an address string to retrieve a balance for :returns: Future, a list of pycoin Spendable objects. """ logging.debug("Retrieving utxos for address %s", address) result = await self.connection.listen_rpc(self.methods["listunspent"], [address]) # type: Dict pos_map = { unspent["tx_hash"]: unspent["tx_pos"] for unspent in result } # type: Dict[str, int] futures = [ self.connection.listen_rpc(self.methods["get"], [unspent["tx_hash"]]) for unspent in result ] # type: List[asyncio.Future] txs = await asyncio.gather(*futures, loop=self.loop) # type: List[str] utxos = [] # type: List[Spendable] for tx_hex in txs: tx = Tx.from_hex(tx_hex) # type: Tx vout = pos_map[tx.id()] # type: int spendable = tx.tx_outs_as_spendable()[vout] # type: Spendable utxos.append(spendable) logging.debug("Retrieved utxo: %s", spendable) return utxos
def multisig_M_of_N(self, M, N, unsigned_id, signed_id): keys = [Key(secret_exponent=i) for i in range(1, N + 2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) self.assertEqual(tx2.id(), unsigned_id) self.assertEqual(tx2.bad_signature_count(), 1) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.id(), signed_id) self.assertEqual(tx2.bad_signature_count(), 0)
[docs]def main(): if len(sys.argv) != 4: print("usage: %s tx-hex-file-path wif-file-path p2sh-file-path" % sys.argv[0]) sys.exit(-1) # get the tx with open(sys.argv[1], "r") as f: tx_hex = f.readline().strip() tx = Tx.from_hex(tx_hex) # get the WIF with open(sys.argv[2], "r") as f: wif = f.readline().strip() assert is_wif_valid(wif) # create the p2sh_lookup with open(sys.argv[3], "r") as f: p2sh_script_hex = f.readline().strip() p2sh_script = h2b(p2sh_script_hex) # build a dictionary of script hashes to scripts p2sh_lookup = build_p2sh_lookup([p2sh_script]) # sign the transaction with the given WIF sign_tx(tx, wifs=[wif], p2sh_lookup=p2sh_lookup) bad_signature_count = tx.bad_signature_count() print("tx %s now has %d bad signature(s)" % (tx.id(), bad_signature_count)) include_unspents = (bad_signature_count > 0) print("Here is the tx as hex:\n%s" % tx.as_hex(include_unspents=include_unspents))
def test_signature_hash(self): compressed = False exponent_2 = int( "137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent_2), compressed=compressed) exponent = wif_to_secret_exponent( "5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") public_key_sec = public_pair_to_sec( ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, exponent), compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) coins_from = [(the_coinbase_tx.hash(), 0, the_coinbase_tx.txs_out[0])] coins_to = [(int(50 * 1e8), bitcoin_address_2)] unsigned_coinbase_spend_tx = standard_tx(coins_from, coins_to) tx_out_script_to_check = the_coinbase_tx.txs_out[0].script idx = 0 actual_hash = unsigned_coinbase_spend_tx.signature_hash( tx_out_script_to_check, idx, hash_type=SIGHASH_ALL) self.assertEqual( actual_hash, 29819170155392455064899446505816569230970401928540834591675173488544269166940 )
def standard_tx(coins_from, coins_to): txs_in = [] unspents = [] for h, idx, tx_out in coins_from: txs_in.append(TxIn(h, idx)) unspents.append(tx_out) txs_out = [] for coin_value, bitcoin_address in coins_to: txs_out.append( TxOut(coin_value, standard_tx_out_script(bitcoin_address))) version, lock_time = 1, 0 tx = Tx(version, txs_in, txs_out, lock_time) tx.set_unspents(unspents) return tx
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 build_spending_tx(script_in_bin, credit_tx): txs_in = [TxIn(credit_tx.hash(), 0, script_in_bin, sequence=4294967295)] txs_out = [TxOut(credit_tx.txs_out[0].coin_value, b'')] spend_tx = Tx(1, txs_in, txs_out, unspents=credit_tx.tx_outs_as_spendable()) return spend_tx
def test_validate_multisig(self): # this is a transaction in the block chain # the unspents are included too, so it can be validated f = io.BytesIO(h2b("01000000025718fb915fb8b3a802bb699ddf04dd91261ef6715f5f2820a2b1b9b7e38b4f27000000004a004830450221008c2107ed4e026ab4319a591e8d9ec37719cdea053951c660566e3a3399428af502202ecd823d5f74a77cc2159d8af2d3ea5d36a702fef9a7edaaf562aef22ac35da401ffffffff038f52231b994efb980382e4d804efeadaee13cfe01abe0d969038ccb45ec17000000000490047304402200487cd787fde9b337ab87f9fe54b9fd46d5d1692aa58e97147a4fe757f6f944202203cbcfb9c0fc4e3c453938bbea9e5ae64030cf7a97fafaf460ea2cb54ed5651b501ffffffff0100093d00000000001976a9144dc39248253538b93d3a0eb122d16882b998145888ac0000000002000000000000004751210351efb6e91a31221652105d032a2508275f374cea63939ad72f1b1e02f477da782100f2b7816db49d55d24df7bdffdbc1e203b424e8cd39f5651ab938e5e4a193569e52ae404b4c00000000004751210351efb6e91a31221652105d032a2508275f374cea63939ad72f1b1e02f477da7821004f0331742bbc917ba2056a3b8a857ea47ec088dd10475ea311302112c9d24a7152ae")) tx = Tx.parse(f) tx.parse_unspents(f) self.assertEqual(tx.id(), "70c4e749f2b8b907875d1483ae43e8a6790b0c8397bbb33682e3602617f9a77a") self.assertEqual(tx.bad_signature_count(), 0)
def test_tx_api(self): tx = Tx.from_hex(TX_E1A18B843FC420734DEEB68FF6DF041A2585E1A0D7DBF3B82AAB98291A6D9952_HEX) # this transaction is a pay-to-hash transaction self.assertEqual(tx.id(), "e1a18b843fc420734deeb68ff6df041a2585e1a0d7dbf3b82aab98291a6d9952") address = address_for_script(tx.txs_out[0].puzzle_script()) self.assertEqual(address, "19LemzJ3XPdUxp113uynqCAivDbXZBdBy3") address = address_for_script(tx.txs_out[1].puzzle_script()) self.assertEqual(address, "3KmkA7hvqG2wKkWUGz1BySioUywvcmdPLR")
def test_bip143_attack_data_capture(num_utxo, segwit_in, try_sign, fake_txn, settings_set, settings_get, cap_story, sim_exec, hist_count): # cleanup prev runs, if very first time thru sim_exec('import history; history.OutptValueCache.clear()') hist_b4 = hist_count() assert hist_b4 == 0 # make a txn, capture the outputs of that as inputs for another txn psbt = fake_txn(1, num_utxo+3, segwit_in=segwit_in, change_outputs=range(num_utxo+2), outstyles=(['p2wpkh']*num_utxo) + ['p2wpkh-p2sh', 'p2pkh']) _, txn = try_sign(psbt, accept=True, finalize=True) open('debug/funding.psbt', 'wb').write(psbt) num_inp_utxo = (1 if segwit_in else 0) time.sleep(.1) title, story = cap_story() assert 'TXID' in title, story txid = story.strip() assert hist_count() in {128, hist_b4+num_utxo+num_inp_utxo} # compare to PyCoin from pycoin.tx.Tx import Tx t = Tx.from_bin(txn) assert t.id() == txid # expect all of new "change outputs" to be recorded (none of the non-segwit change tho) # plus the one input we "revealed" after1 = settings_get('ovc') assert len(after1) == min(30, num_utxo + num_inp_utxo) all_utxo = hist_count() assert all_utxo == hist_b4+num_utxo+num_inp_utxo # build a new PSBT based on those change outputs psbt2, raw = spend_outputs(psbt, txn) # try to sign that ... should work fine try_sign(raw, accept=True, finalize=True) time.sleep(.1) # should not affect stored data, because those values already cached assert settings_get('ovc') == after1 # any tweaks to input side's values should fail. for amt in [int(1E6), 1]: def value_tweak(spendables): assert len(spendables) > 2 spendables[0][1].coin_value += amt psbt3, raw = spend_outputs(psbt, txn, tweaker=value_tweak) with pytest.raises(CCProtoError) as ee: orig, result = try_sign(raw, accept=True, finalize=True) assert 'but PSBT claims' in str(ee), ee
def test_recognize_multisig(self): h = '010000000139c92b102879eb95f14e7344e4dd7d481e1238b1bfb1fa0f735068d2927b231400000000910047304402208fc06d216ebb4b6a3a3e0f906e1512c372fa8a9c2a92505d04e9b451ea7acd0c0220764303bb7e514ddd77855949d941c934e9cbda8e3c3827bfdb5777477e73885b014730440220569ec6d2e81625dd18c73920e0079cdb4c1d67d3d7616759eb0c18cf566b3d3402201c60318f0a62e3ba85ca0f158d4dfe63c0779269eb6765b6fc939fc51e7a8ea901ffffffff0140787d01000000001976a914641ad5051edd97029a003fe9efb29359fcee409d88ac0000000040787d0100000000c952410496ec45f878b62c46c4be8e336dff7cc58df9b502178cc240eb3d31b1266f69f5767071aa3e017d1b82a0bb28dab5e27d4d8e9725b3e68ed5f8a2d45c730621e34104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af53ae' f = io.BytesIO(h2b(h)) tx = Tx.parse(f) tx.parse_unspents(f) self.assertEqual(tx.id(), "10c61e258e0a2b19b245a96a2d0a1538fe81cd4ecd547e0a3df7ed6fd3761ada") the_script = tx.unspents[0].script s = script_obj_from_script(tx.unspents[0].script) self.assertEqual(s.script(), the_script)
def tx_to_b64(tx_hex): # use this to dump raw transactions in the data above import io tx = Tx.tx_from_hex(tx_hex) f = io.BytesIO() tx.stream(f) d = f.getvalue() for idx in range(0, len(d), 45): print('"%s"' % binascii.b2a_base64(d[idx:idx+45]).decode("utf8")[:-1])
async def do_broadcast(self, obj): tx_hex, chg_vout = obj.get("tx_hex"), obj.get("vout") if not tx_hex or not chg_vout: self.print_json({"error": "Command parameters are not correct"}) return chg_out = Tx.from_hex(tx_hex).txs_out[chg_vout] txid = await self.wallet.broadcast(tx_hex, chg_out) self.print_json({"txid": txid})
def test_coinbase_tx(self): tx = Tx.coinbase_tx(COINBASE_PUB_KEY_FROM_80971, int(50 * 1e8), COINBASE_BYTES_FROM_80971) s = io.BytesIO() tx.stream(s) tx1 = s.getvalue() s = io.BytesIO() block_80971.txs[0].stream(s) tx2 = s.getvalue() self.assertEqual(tx1, tx2)
def test_tx_with_gpg(self): gpg_wif = tempfile.NamedTemporaryFile(suffix=".gpg") gpg_wif.write(WIF_1_GPG) gpg_wif.flush() output_file = tempfile.NamedTemporaryFile(suffix=".hex") self.launch_tool(args=["tx.py", "5564224b6c01dbc2bfad89bfd8038bc2f4ca6c55eb660511d7151d71e4b94b6d/0/210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac/5000000000", "1KissFDVu2wAYWPRm4UGh5ZCDU9sE9an8T", "-f", gpg_wif.name, "-g", "--batch --passphrase=foo", "-o", output_file.name]) d = open(output_file.name).read() tx = Tx.from_hex(d) self.assertEqual(tx.id(), "c52b0c66cff6147b99acb29389343f6eae68c29faf2186fa8c1613d615b217e8")
def test_cache_tx(self): the_dir = self.set_cache_dir() tx = Tx.from_hex( "01000000010000000000000000000000000000000000000000000000000000000000000000" "ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a" "2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781" "e62294721166bf621e73a82cbf2342c858eeac00000000") self.launch_tool("tx -C %s --db %s" % (tx.id(), tx.as_hex()), env=dict(PYCOIN_CACHE_DIR=the_dir)) self.assertTrue(os.path.exists(os.path.join(the_dir, "txs", "%s_tx.bin" % tx.id())))
def prepare_tx_for_signing(hex_tx, tx_inputs): logging.info('Preparing tx for signing') transaction = Tx.from_hex(hex_tx) unspents = [ TxOut(coin_value=tx_input.coin_value, script=tx_input.script) for tx_input in tx_inputs ] transaction.set_unspents(unspents) return transaction
def make_tx(i): txs_in = [ TxIn(make_hash(i * 10000 + idx), (i + idx) % 2) for idx in range(3) ] txs_out = [ TxOut(i * 40000, make_hash(i * 20000 + idx)) for idx in range(2) ] tx = Tx(1, txs_in, txs_out) return tx
def make_tx(i): key = Key(12345 * (i + 29)) script = standard_tx_out_script(key.address()) txs_in = [ TxIn(make_hash(i * 10000 + idx), (i + idx) % 2) for idx in range(3) ] txs_out = [TxOut(i * 40000, script) for idx in range(2)] tx = Tx(1, txs_in, txs_out) return tx
def test_mempool_observer(self): connection = Mock() from pycoin.tx.Tx import Tx tx = Tx.from_hex( '01000000000101112a649fd72656cf572259cb7cb61bd31ccdbdf0944070e73401565affbe629d0100000000ffffffff02608' 'de2110000000017a914d52b516c1a094462959ed6facebb94429d2cebf487d3135b0b00000000220020701a8d401c84fb13e6' 'baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d0400473044022006b149e0cf031f57fd443bd1210b381e9b1b15094' '57ba1f49e48b803696f56e802203d66bd974ad3ac5b7591cc84e706b78d139c61e2bf1995a89c4dc0758984a2b70148304502' '2100fe7275d601080e1870517774a3ad6accaa7f8ad144addec3251e98685d4fefad02207792c2b0ed6ab42ed2ba6d12e6bd3' '4db8c6f4ac6f15e604f70ea85a735c450b1016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd' '92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e' '046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000') self.loop.run_until_complete( self.sut.on_transaction(connection, {'tx': tx})) self.assertEqual(tx.w_id(), [x for x in self.mempool_repository.get_txids()][0]) self.repository.blockchain.get_transactions_by_block_hash.return_value = [], None block = Block(1, b'0' * 32, merkle_root=merkle([tx.hash()]), timestamp=123456789, difficulty=3000000, nonce=1 * 137) block.txs.append(tx) as_bin = block.as_bin() Block.from_bin(as_bin) block_header = {'block_hash': block.id()} self.batcher_factory.add_peer.return_value = async_coro(True) self.batcher_factory.inv_item_to_future.return_value = block.as_bin() mempool_response = self.mempool_repository.get_raw_mempool(True) self.assertEqual( { '41867301a6cff5c47951aa1a4eef0be910db0cb5f154eaeb469732e1f9b54548': { 'size': 381, 'fee': 0, 'modifiedfee': 0, 'time': ANY, 'height': 0, 'descendantcount': 0, 'descendantsize': 0, 'descendantfees': 0, 'ancestorcount': 0, 'ancestorsize': 0, 'ancestorfees': 0, 'depends': [] } }, mempool_response) self.repository.blockchain.save_block.side_effect = lambda a: { 'block_object': Block.from_bin(a['block_bytes']) } self.repository.blockchain.get_transactions_by_block_hash.return_value = [], None self.loop.run_until_complete(self.sut.on_block_header(block_header)) self.assertEqual(self.mempool_repository.get_raw_mempool(True), {}) self.assertEqual([x for x in self.mempool_repository.get_txids()], [])
def load_tx(self, txid): p = subprocess.Popen(['electrum', 'gettransaction', txid], stdout=subprocess.PIPE, stderr=subprocess.PIPE) raw_data = p.communicate()[0] data = json.loads(raw_data) txhex = data['hex'] tx = Tx.from_hex(txhex) return tx
def tx_to_b64(tx_hex): # use this to dump raw transactions in the data above import io tx = Tx.from_hex(tx_hex) f = io.BytesIO() tx.stream(f) d = f.getvalue() for idx in range(0, len(d), 45): print('"%s"' % binascii.b2a_base64(d[idx:idx+45]).decode("utf8")[:-1])
def multisig_M_of_N_individually(self, M, N): keys = [ Key(secret_exponent=i, generator=secp256k1_generator) for i in range(1, N + 2) ] tx_in = Tx.TxIn.coinbase_tx_in(script=b'') script = script_for_multisig(m=M, sec_keys=[key.sec() for key in keys[:N]]) tx_out = Tx.TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) for partial_key_list in itertools.permutations(keys[:N], M): tx2 = create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) for key in partial_key_list: self.assertEqual(tx2.bad_signature_count(), 1) hash160_lookup = build_hash160_lookup([key.secret_exponent()], [secp256k1_generator]) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.bad_signature_count(), 0)
def tether_tx(tx_ins, private_key, send_amount, receiver): """ simple usdt transaction here assume utxo comes from the sender and is used for mine fee bitcoin change will be sent back to sender address of course different address's utxo can be used for mine fee, but should be aware sender is determined by the first input in the tx bitcoin change can also be sent back to different address, but should be aware receiver is indicated by the last output address that is not the sender for full customization, use btc sample p2pkh_tx :param tx_ins: utxo from the sender :param private_key: private key of the same sender :param send_amount: (display amount) * (10 ** 8) :param receiver: address to receive usdt """ _txs_in = [] _un_spent = [] total_bal = 0 for tx_id, idx, balance, address in tx_ins: total_bal += balance # must h2b_rev NOT h2b tx_id_b = h2b_rev(tx_id) _txs_in.append(TxIn(tx_id_b, idx)) _un_spent.append(Spendable(balance, standard_tx_out_script(address), tx_id_b, idx)) txn_fee = estimate_p2pkh_tx_bytes(len(tx_ins), 3) * recommend_satoshi_per_byte() _txs_out = [TxOut(total_bal - txn_fee - 546, standard_tx_out_script(tx_ins[0][3])), TxOut(0, binascii.unhexlify(omni_tether_script(send_amount))), TxOut(546, standard_tx_out_script(receiver))] version, lock_time = 1, 0 tx = Tx(version, _txs_in, _txs_out, lock_time) tx.set_unspents(_un_spent) solver = build_hash160_lookup([int(private_key, 16)] * len(tx_ins)) signed_tx = tx.sign(solver, hash_type=SIGHASH_ALL) return signed_tx.as_hex(), signed_tx.id()
def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind, cap_story, case): # not fraud: output address encoded in various equiv forms from pycoin.tx.Tx import Tx from pycoin.tx.TxOut import TxOut # NOTE: out#1 is change: #chg_addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo' psbt = open('data/example-change.psbt', 'rb').read() b4 = BasicPSBT().parse(psbt) t = Tx.parse(BytesIO(b4.txn)) pkh = t.txs_out[1].hash160() if case == 'p2wpkh': t.txs_out[1].script = bytes([0, 20]) + bytes(pkh) from bech32 import encode expect_addr = encode('tb', 0, pkh) elif case == 'p2sh': spk = bytes([0xa9, 0x14]) + pkh + bytes([0x87]) b4.outputs[1].redeem_script = bytes([0, 20]) + bytes(pkh) t.txs_out[1].script = spk expect_addr = t.txs_out[1].address('XTN') b4.txn = t.as_bin() with BytesIO() as fd: b4.serialize(fd) mod_psbt = fd.getvalue() open('debug/mod-%s.psbt' % case, 'wb').write(mod_psbt) start_sign(mod_psbt) time.sleep(.1) _, story = cap_story() check_against_bitcoind(B2A(b4.txn), Decimal('0.00000294'), change_outs=[ 1, ], dests=[(1, expect_addr)]) #print(story) assert expect_addr in story assert parse_change_back(story) == (Decimal('1.09997082'), [expect_addr]) signed = end_sign(True)
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 tx_for_tx_hash(self, tx_hash): """ Get a Tx by its hash. """ url = "%s/rawtx/%s" % (self.url, b2h_rev(tx_hash)) d = urlopen(url).read() j = json.loads(d.decode("utf8")) tx = Tx.from_hex(j.get("rawtx", "")) if tx.hash() == tx_hash: return tx
def get_raw_unsigned(self, fee_satoshi): """Return raw transaction ready for signing May return a transaction with amount=0 if the input amount is not enough to cover fees """ txout = self.tx.txs_out[self.vout] amount_satoshi = txout.coin_value if fee_satoshi >= amount_satoshi: logging.warning('Insufficient funds to cover fee') logging.warning('txout has value of {}, fee = {}'.format( amount_satoshi, fee_satoshi)) # Calculate adjusted amount = input amount - fee adjusted_amount_satoshi = max(0, amount_satoshi - fee_satoshi) logging.debug('tx amount = amount - fee = {} - {} = {}'.format( amount_satoshi, fee_satoshi, adjusted_amount_satoshi)) assert adjusted_amount_satoshi >= 0 adjusted_amount_btc = decimal.Decimal( adjusted_amount_satoshi) / satoshi_per_btc logging.debug("Create tx: {} sat -> {}".format(adjusted_amount_satoshi, self.dest_address)) logging.info("Input tx id = {}, vout={}".format( self.tx.id().encode("ascii"), self.vout)) txin = TxIn(self.tx.hash(), self.vout, sequence=MAX_BIP125_RBF_SEQUENCE) scriptPubKey = pycoin.ui.script_obj_from_address(self.dest_address) txout = TxOut(adjusted_amount_satoshi, scriptPubKey.script()) # Set nlocktime to the current blockheight to discourage 'fee sniping', as per the core # wallet implementation nlocktime = util.get_current_blockcount() or 0 version = 1 tx = Tx(version, [ txin, ], [ txout, ], nlocktime) return tx.as_hex()
def test_multisig_one_at_a_time(self): M = 3 N = 3 keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) ids = ["403e5bfc59e097bb197bf77a692d158dd3a4f7affb4a1fa41072dafe7bec7058", "5931d9995e83721243dca24772d7012afcd4378996a8b953c458175f15a544db", "9bb4421088190bbbb5b42a9eaa9baed7ec7574a407c25f71992ba56ca43d9c44", "03a1dc2a63f93a5cf5a7cb668658eb3fc2eda88c06dc287b85ba3e6aff751771"] for i in range(1, N+1): self.assertEqual(tx2.bad_signature_count(), 1) self.assertEqual(tx2.id(), ids[i-1]) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys[i-1:i]) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.id(), ids[i]) self.assertEqual(tx2.bad_signature_count(), 0)
def tx_for_tx_hash(self, tx_hash): """ returns the pycoin.tx object for tx_hash """ try: url_append = "?token=%s&includeHex=true" % self.api_key url = self.base_url("txs/%s%s" % (b2h_rev(tx_hash), url_append)) result = json.loads(urlopen(url).read().decode("utf8")) tx = Tx.parse(io.BytesIO(h2b(result.get("hex")))) return tx except: raise Exception
def do_test_tx(self, sighash, index_, flags): txhash, seq, script, witness_script = b'0' * 32, 0xffffffff, b'\x51', b'000000' out_script, spend_script, locktime = b'\x00\x00\x51', b'\x00\x51', 999999 txs_in = [TxIn(txhash, 0, script, seq), TxIn(txhash, 1, script+b'\x51', seq-1), TxIn(txhash, 2, script+b'\x51\x51', seq-2), TxIn(txhash, 3, script+b'\x51\x51\x51', seq-3)] txs_out = [TxOut(55, out_script), TxOut(54, out_script+b'\x51'), TxOut(53, out_script+b'\x51\x51')] pytx = Tx(2, txs_in, txs_out, lock_time=locktime) pytx.unspents = {0: TxOut(5000, spend_script), # FIXME: Make script unique 1: TxOut(5001, spend_script), 2: TxOut(5002, spend_script), 3: TxOut(5003, spend_script)} unspent = pytx.unspents[index_] pytx_hex = pytx.as_hex() if flags & USE_WITNESS: pytx_hash = pytx.signature_for_hash_type_segwit(unspent.script, index_, sighash) else: pytx_hash = pytx.signature_hash(spend_script, index_, sighash) pytx_hash = hex_from_bytes(to_bytes_32(pytx_hash)) tx = tx_init(2, locktime, 3, 3) tx_add_input(tx, tx_input_init(txhash, 0, seq, script, None)) tx_add_raw_input(tx, txhash, 1, seq-1, script+b'\x51', None, 0) tx_add_raw_input(tx, txhash, 2, seq-2, script+b'\x51\x51', None, 0) tx_add_raw_input(tx, txhash, 3, seq-3, script+b'\x51\x51\x51', None, 0) tx_add_raw_output(tx, 55, out_script, 0) tx_add_raw_output(tx, 54, out_script+b'\x51', 0) tx_add_raw_output(tx, 53, out_script+b'\x51\x51', 0) tx_hex = tx_to_hex(tx, 0) amount = (index_ + 1) * 5000 tx_hash = tx_get_btc_signature_hash(tx, index_, unspent.script, unspent.coin_value, sighash, flags) tx_hash = hex_from_bytes(tx_hash) self.assertEqual(pytx_hex, tx_hex) self.assertEqual(pytx_hash, tx_hash)
def test_build_spends(self): # first, here is the tx database TX_DB = {} # create a coinbase Tx where we know the public & private key exponent = wif_to_secret_exponent("5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") compressed = False public_key_sec = public_pair_to_sec(exponent * secp256k1_generator, compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) TX_DB[the_coinbase_tx.hash()] = the_coinbase_tx # now create a Tx that spends the coinbase compressed = False exponent_2 = int("137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address(exponent_2 * secp256k1_generator, compressed=compressed) self.assertEqual("12WivmEn8AUth6x6U8HuJuXHaJzDw3gHNZ", bitcoin_address_2) coins_from = [(the_coinbase_tx.hash(), 0, the_coinbase_tx.txs_out[0])] coins_to = [(int(50 * 1e8), bitcoin_address_2)] unsigned_coinbase_spend_tx = standard_tx(coins_from, coins_to) solver = build_hash160_lookup([exponent]) coinbase_spend_tx = unsigned_coinbase_spend_tx.sign(solver) # now check that it validates self.assertEqual(coinbase_spend_tx.bad_signature_count(), 0) TX_DB[coinbase_spend_tx.hash()] = coinbase_spend_tx # now try to respend from priv_key_2 to priv_key_3 compressed = True exponent_3 = int("f8d39b8ecd0e1b6fee5a340519f239097569d7a403a50bb14fb2f04eff8db0ff", 16) bitcoin_address_3 = public_pair_to_bitcoin_address(exponent_3 * secp256k1_generator, compressed=compressed) self.assertEqual("13zzEHPCH2WUZJzANymow3ZrxcZ8iFBrY5", bitcoin_address_3) coins_from = [(coinbase_spend_tx.hash(), 0, coinbase_spend_tx.txs_out[0])] unsigned_spend_tx = standard_tx(coins_from, [(int(50 * 1e8), bitcoin_address_3)]) solver.update(build_hash160_lookup([exponent_2])) spend_tx = unsigned_spend_tx.sign(solver) # now check that it validates self.assertEqual(spend_tx.bad_signature_count(), 0)
def test_blanked_hash(self): tx = Tx.tx_from_hex(TX_E1A18B843FC420734DEEB68FF6DF041A2585E1A0D7DBF3B82AAB98291A6D9952_HEX) self.assertEqual(tx.id(), "e1a18b843fc420734deeb68ff6df041a2585e1a0d7dbf3b82aab98291a6d9952") self.assertEqual(b2h(tx.blanked_hash()), "909579526c4c2c441687c7478d3f96249724d2ff071d2272b44500d6cf70d5d6") tx.txs_in[0].script = b"foo" self.assertEqual(b2h(tx.blanked_hash()), "909579526c4c2c441687c7478d3f96249724d2ff071d2272b44500d6cf70d5d6") tx.txs_out[0].coin_value += 1 self.assertEqual(b2h(tx.blanked_hash()), "10d4e87f7bf35f2949e7693e7a4a84189aad8631f0b2b0999e88f7261066cbe5") tx.txs_in[0].script = b"bar" self.assertEqual(b2h(tx.blanked_hash()), "10d4e87f7bf35f2949e7693e7a4a84189aad8631f0b2b0999e88f7261066cbe5") tx.txs_in[0].script = b"" self.assertEqual(b2h(tx.hash()), "10d4e87f7bf35f2949e7693e7a4a84189aad8631f0b2b0999e88f7261066cbe5") tx.txs_in[0].script = b"foo" self.assertEqual(b2h(tx.hash()), "c91910058722f1c0f52fc5c734939053c9b87882a9c72b609f21632e0bd13751")
def get_tx(tx_hash): """ Get a Tx by its hash. """ # TODO: fix this j = get_json_for_hash(tx_hash) txs_in = [] for j_in in j.get("in"): if j_in.get("coinbase"): txs_in.append(TxIn.coinbase_tx_in(binascii.unhexlify(j_in["coinbase"]))) else: 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(btc_to_satoshi(j_out["value"])), tools.compile(j_out["scriptPubKey"]))) tx = Tx(int(j["ver"]), txs_in, txs_out, int(j["lock_time"])) assert tx.hash() == tx_hash return tx
def get(self, key): for path in self.paths_for_hash(key): try: tx = Tx.parse(open(path, "rb")) if tx and tx.hash() == key: return tx except IOError: pass for method in self.lookup_methods: try: tx = method(key) if tx and tx.hash() == key: self.put(tx) return tx except Exception: pass return None
def test_signature_hash(self): compressed = False exponent_2 = int("137f3276686959c82b454eea6eefc9ab1b9e45bd4636fb9320262e114e321da1", 16) bitcoin_address_2 = public_pair_to_bitcoin_address(exponent_2 * secp256k1_generator, compressed=compressed) exponent = wif_to_secret_exponent("5JMys7YfK72cRVTrbwkq5paxU7vgkMypB55KyXEtN5uSnjV7K8Y") public_key_sec = public_pair_to_sec(exponent * secp256k1_generator, compressed=compressed) the_coinbase_tx = Tx.coinbase_tx(public_key_sec, int(50 * 1e8), COINBASE_BYTES_FROM_80971) coins_from = [(the_coinbase_tx.hash(), 0, the_coinbase_tx.txs_out[0])] coins_to = [(int(50 * 1e8), bitcoin_address_2)] unsigned_coinbase_spend_tx = standard_tx(coins_from, coins_to) tx_out_script_to_check = the_coinbase_tx.txs_out[0].script idx = 0 actual_hash = unsigned_coinbase_spend_tx.signature_hash(tx_out_script_to_check, idx, hash_type=SIGHASH_ALL) self.assertEqual(actual_hash, 29819170155392455064899446505816569230970401928540834591675173488544269166940)
def test_compare_cost(self): """ Compare our size estimation with a known transaction. The transaction contains 1 input, 6 outputs, and 1 OP_RETURN Note that the estimation may be off +/- the number of inputs, which is why the estimate was off by 1 in this case. :return: """ tx = Tx.from_hex( '0100000001ae17c5db3174b46ae2bdc911c25df6bc3ce88092256b6f6e564989693ecf67fc030000006b483045022100b0cfd576dd30bbdf6fd11e0d6118c59b6c6f8e7bf6513d323c7f9f5f8296bef102200174a28e28c792425b71155df99ea6110cdb67d3567792f1696e61424c1f67400121037175dfbeecd8b5a54eb5ad9a696f15b7b39da2ea7d67b4cd7a3299bb95e28884ffffffff07be0a0000000000001976a91481c706f7e6b2d9546169c1e76f50a3ee18e1e1d788acbe0a0000000000001976a914c2b9a62457e35bef48ef350a00622b1e63394d4588acbe0a0000000000001976a91481c706f7e6b2d9546169c1e76f50a3ee18e1e1d788acbe0a0000000000001976a914c2b9a62457e35bef48ef350a00622b1e63394d4588acbe0a0000000000001976a914cc0a909c4c83068be8b45d69b60a6f09c2be0fda88ac5627cb1d000000001976a9144103222e7c72b869c5e47bfe86702684531f2c9088ac0000000000000000226a206f308c70afcfcb0311ad0de989b80904fb54d9131fd3ab2187b89ca9601adab000000000') s = io.BytesIO() tx.stream(s) tx_byte_count = len(s.getvalue()) estimated_byte_count = tx_utils.calculate_raw_tx_size_with_op_return(num_inputs=1, num_outputs=6) self.assertEquals(estimated_byte_count, tx_byte_count + 1)
def txs_from_json(path): """ Read tests from ./data/tx_??valid.json Format is an array of arrays Inner arrays are either [ "comment" ] or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...], serializedTransaction, verifyFlags] ... where all scripts are stringified scripts. verifyFlags is a comma separated list of script verification flags to apply, or "NONE" """ comments = None with open(path, 'r') as f: for tvec in json.load(f): if len(tvec) == 1: comments = tvec[0] continue assert len(tvec) == 3 prevouts = tvec[0] for prevout in prevouts: assert len(prevout) in (3, 4) tx_hex = tvec[1] flag_mask = parse_flags(tvec[2]) try: tx = Tx.from_hex(tx_hex) except: print("Cannot parse tx_hex: %s" % tx_hex) raise spendable_db = {} blank_spendable = Spendable(0, b'', b'\0' * 32, 0) for prevout in prevouts: coin_value = 1000000 if len(prevout) == 4: coin_value = prevout[3] spendable = Spendable(coin_value=coin_value, script=compile(prevout[2]), tx_hash=h2b_rev(prevout[0]), tx_out_index=prevout[1]) spendable_db[(spendable.tx_hash, spendable.tx_out_index)] = spendable unspents = [ spendable_db.get((tx_in.previous_hash, tx_in.previous_index), blank_spendable) for tx_in in tx.txs_in] tx.set_unspents(unspents) yield (tx, flag_mask, comments)
def test_is_invalid(self): for (prevouts, tx_hex, flags) in txs_from_json(TX_INVALID_JSON): try: tx = Tx.from_hex(tx_hex) if not check_transaction(tx): continue unspents = [Spendable(coin_value=1000000, script=compile_script(prevout[2]), tx_hash=prevout[0], tx_out_index=prevout[1]) for prevout in prevouts] tx.set_unspents(unspents) bs = tx.bad_signature_count() self.assertEqual(bs, 0) except: continue self.fail("Invalid transaction: " + tx.id() + " appears to be valid.")
def test_tx_with_gpg(self): # binary data with GPG-encrypted WIF KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn for secret exponent 1 WIF_1_GPG = h2b( "8c0d040303026c3030b7518a94eb60c950bc87ab26f0604a37f247f74f88deda10b180bb807" "2879b728b8f056808baea0c8e511e7cf2eba77cce937d2f69a67a79e163bf70b57113d27cb6" "a1c2390a1e8069b447c34a7c9b5ba268c2beedd85b50") gpg_wif = tempfile.NamedTemporaryFile(suffix=".gpg") gpg_wif.write(WIF_1_GPG) gpg_wif.flush() output_file = tempfile.NamedTemporaryFile(suffix=".hex") self.launch_tool( args=["tx", "5564224b6c01dbc2bfad89bfd8038bc2f4ca6c55eb660511d7151d71e4b94b6d/0/" "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac/5000000000", "1KissFDVu2wAYWPRm4UGh5ZCDU9sE9an8T", "-f", gpg_wif.name, "-g", "--batch --passphrase=foo", "-o", output_file.name]) d = open(output_file.name).read() tx = Tx.from_hex(d) self.assertEqual(tx.id(), "c52b0c66cff6147b99acb29389343f6eae68c29faf2186fa8c1613d615b217e8")