1

How is the vout address derived for a P2PK transaction? I am using the Python API that ships with the bitcoin source code, in directory bitcoin/test/functional. I'm using regtest.

I have written a unit test. It creates a transaction, writes the CTransaction object to the log file, then appends a CTxOut object to the vout array, and writes the CTransaction object to the log again. The second log entry shows that the item in the vout array includes an array of addresses containing a single value, 'muXtVbAXxTdAdaS9HQLnexRvsfgVteZcv8'. How was that value derived?

Here is the relevant code fragment:

    addr1 = w0_rpc.getnewaddress()
    self.log.info(f"\n    DEBUG addr1={addr1}")
    a1 = w0_rpc.getaddressinfo(addr1)
    pubkey1 = hex_str_to_bytes(a1['pubkey'])
    self.log.info(f"\n    DEBUG pubkey1={pubkey1}")
    p2pk1 = CScript([pubkey1, OP_CHECKSIG])
    self.log.info(f"\n    DEBUG bytes_to_hex_str(p2pk1)={bytes_to_hex_str(p2pk1)}")
    tx1 = CTransaction()
    tx1.vin.append(CTxIn(COutPoint(int(txid0, 16), v), b""))
    s = pprint.pformat(w0_rpc.decoderawtransaction(ToHex(tx1)))
    self.log.info(f"\n    DEBUG tx={s}")
    tx1.vout.append(CTxOut(int(9 * COIN), p2pk1))
    s = pprint.pformat(w0_rpc.decoderawtransaction(ToHex(tx1)))
    self.log.info(f"\n    DEBUG tx={s}")

Below is the log output from that code. You can see at the end that the vout array contains an item which includes an array of addresses containing a single value, 'muXtVbAXxTdAdaS9HQLnexRvsfgVteZcv8'. I did not explicitly provide that value, at least not knowingly. It seems to me that the value was derived somewhere, under the hood. Where did this happen? How was the value calculated?

    DEBUG addr1=2NGYQt1CxwSCdGbj8mpxMtLaTZtxHsmbqWM
2019-10-10T12:13:06.176000Z TestFramework (INFO): 
    DEBUG pubkey1=b'\x02#\x9c\xabCVRL\xc1\xd1o8}\x0e\x85Z myQ-&\x06\xb2\xc2\xea \xfa\xf0(\xd3e\x98'
2019-10-10T12:13:06.176000Z TestFramework (INFO): 
    DEBUG bytes_to_hex_str(p2pk1)=2102239cab4356524cc1d16f387d0e855a206d79512d2606b2c2ea20faf028d36598ac
2019-10-10T12:13:06.177000Z TestFramework (INFO): 
    DEBUG tx={'hash': '0abb1fa572d89323429fc231fcede74af58f77469a3b383ee856db60ecee41a4',
 'locktime': 0,
 'size': 51,
 'txid': '0abb1fa572d89323429fc231fcede74af58f77469a3b383ee856db60ecee41a4',
 'version': 1,
 'vin': [{'scriptSig': {'asm': '', 'hex': ''},
          'sequence': 0,
          'txid': '9d177bb43b76dcbbf52fad079c4fd98986b32034d91ea12bcb4e67d32dc20f9b',
          'vout': 0}],
 'vout': [],
 'vsize': 51,
 'weight': 204}
2019-10-10T12:13:06.179000Z TestFramework (INFO): 
    DEBUG tx={'hash': 'd86153fc539e699289dc8f7f4e20d10f6378fa4f10dad131f5c8b2d4bc7e5c76',
 'locktime': 0,
 'size': 95,
 'txid': 'd86153fc539e699289dc8f7f4e20d10f6378fa4f10dad131f5c8b2d4bc7e5c76',
 'version': 1,
 'vin': [{'scriptSig': {'asm': '', 'hex': ''},
          'sequence': 0,
          'txid': '9d177bb43b76dcbbf52fad079c4fd98986b32034d91ea12bcb4e67d32dc20f9b',
          'vout': 0}],
 'vout': [{'n': 0,
           'scriptPubKey': {'addresses': ['muXtVbAXxTdAdaS9HQLnexRvsfgVteZcv8'],
                            'asm': '02239cab4356524cc1d16f387d0e855a206d79512d2606b2c2ea20faf028d36598 '
                                   'OP_CHECKSIG',
                            'hex': '2102239cab4356524cc1d16f387d0e855a206d79512d2606b2c2ea20faf028d36598ac',
                            'reqSigs': 1,
                            'type': 'pubkey'},
           'value': Decimal('9.00000000')}],
 'vsize': 95,
 'weight': 380}

Edit: Thanks to the response below, I was able to work out how the value of the address is derived. This code does the trick:

    from test_framework.script import hash160
    from test_framework.address import byte_to_base58
    addr1_hash160 = hash160(pubkey1)
    self.log.info(f"\n    DEBUG addr1_hash160={addr1_hash160}")
    addr1_base58 = byte_to_base58(addr1_hash160, 111)
    self.log.info(f"\n    DEBUG addr1_base58={addr1_base58}")

So for the given example, this code prints out 'muXtVbAXxTdAdaS9HQLnexRvsfgVteZcv8', the value of the address that appears in the output from decoderawtransaction(). As mentioned below, this value is not actually relevant for a P2PK transaction.

  • The script inside the transaction output contains the address because this is a unique method for check the owner of the transaction. ps: not only the script P2PKH contains the address but also P2SH, P2WPKH, P2WSH. Only the script P2PK does not contain the address but the real public key. – vincenzopalazzo Oct 10 at 12:40
  • How was the value derived? – eric Oct 10 at 13:02
  • The address derived to the parsing and apply the correct codify to the hex propriety inside the ScriptPubKey. so if you parsering this 2102239cab4356524cc1d16f387d0e855a206d79512d2606b2c2ea20faf028d36598ac it contains the address – vincenzopalazzo Oct 10 at 16:42
2

P2PK scripts don't actually have addresses, it is really a bug that an address is shown for P2PK outputs that has been fixed for Bitcoin Core 0.19 (to be released soon) and later.


Given a public key, you can derive an address from it by hashing the public key first with SHA256, then that hash with RIPEMD160. This result is known as the hash160. The hash160 is then encoded with Base58Check Encoding with a version number of 0 for mainnet, and 111 (hex 0x6f) for testnet.

You can also derive other address types from a public key, i.e. bech32 addresses for a P2WPKH script. However, either way, it is incorrect to say that a P2PK output has an address.

  • I got it, many thanks. Based on the info that you provided, I was able to work out the code to derive, from the public key, the address that appears in the output of decoderawtransaction(). I edited my question to include the code. I understand that the address value is not relevant for a P2PK transaction. I take it that the address is derived in bitcoin core - in the call to decoderawtransaction(), I suppose - and not in the Python layer. – eric Oct 11 at 15:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.