Post
Topic
Board Development & Technical Discussion
Topic OP
Need help reversing bitcoinutils "tagged_hash" to generate Taproot addresses
by
BTCW
on 15/01/2024, 21:33:16 UTC
Let's begin with this private key

Code:
0000000000000000000000000000000000000000000000000000000000000001

Using any good old tool, bitcoinutils includes, it gives us

Code:
Private key compressed (WIF):                 KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn
Public key compressed (HEX):                  0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798

HASH160 uncompressed:                         91b24bf9f5288532960ac687abb035127b1d28a5
HASH160 compressed:                           751e76e8199196d454941c45d1b3a323f1433bd6

Legacy uncompressed public address (P2PKH):   1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm
Legacy compressed public address (P2PKH):     1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH
Nested segwit public address (P2SH-P2WPKH):   3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN
Native segwit public address (P2WPKH):        bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

I won't go into legacy or nested segwit here, but I would like to mention how we easily derive the native segwit public address using the HASH160 compressed

Code:
bitcoinutils.bech32.encode('bc',0,bytes.fromhex('751e76e8199196d454941c45d1b3a323f1433bd6'))
#bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

(Likewise, we can produce the segwit bech32m (P2WSH) address by using the single SHA256 of the compressed public key to get bc1qpac4ht6afshdx2tctnhjnetz7u6g3j9zhwwmc4cqkdsa2jumq42qd3drf7; another thread.)

However, here is where I need your help.

I know that the P2TR address corresponding to this private key and its witness program are

Code:
Tweaked Taproot public address (P2TR):        bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9
Witness program (HEX):                        da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21

since I can quickly arrive at them using bitcoinutils

I can furthermore reproduce and verify it with:

Code:
bitcoinutils.bech32.encode('bc',1,bytes.fromhex('da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21'))
#bc1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sspknck9

However, I fail to reverse how bitcoilutils does it. Specifically, this function from here that generates the tweaked public key...

Code:
def tagged_hash(data: bytes, tag: str) -> bytes:
    """
    Tagged hashes ensure that hashes used in one context can not be used in another.
    It is used extensively in Taproot

    A tagged hash is: SHA256( SHA256("TapTweak") ||
                              SHA256("TapTweak") ||
                              data
                            )
    """

    tag_digest = hashlib.sha256(tag.encode()).digest()
    return hashlib.sha256(tag_digest + tag_digest + data).digest()

... gives me a headache.

I don't see it. How do you go from the 32-byte x-only public address to that witness program?

In other words, how do I reproduce this transformation in Python (without relying on bitcoinutils as a mystery box library)?

Code:
Input:                                        79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
to
Output:                                       da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21

Need your help! I'm not sure what the library uses as its standard "TapTweak", and I get confused by bitcoinutil's nested (pun intended) code.

How do we use hashlib only and arrive at the same tweaked witness program, and thus the identical P2TR public address? I am very well aware you are allowed to use whatever as "TapTweak". Can we, however, simply reproduce the "library recipe"? What is its standard "TapTweak," and how to arrive at this public address, given the data above, using simple Python?

Many thanks.