taproot – Unclear on learn how to assemble witness stack and controlblock in P2TR Scrip Path outputs utilizing python

on

|

views

and

comments


I’ve been documenting learn how to create various kinds of Tx outputs right here, leveraging the Bitcoin Core Take a look at Framework. Notably for Taproot, I am engaged on a situation of a P2TR Script Path with a tree of PK scripts like this:

                   taptweak(Inside P|ABC)
                             |
                             |
                      TH("TapBranch") ABC
                             |
                             |
                   ----------------------
                  |                      |
           TH("TapBranch") AB.    TH("TapLeaf") C    
                  |
           ---------------
          |              |
   TH("TapLeaf) A TH("TapLeaf) B

Reviewing the Bips 340, 341, and 342, in addition to the Opthech Taproot Worksop movies, documentation, and code, it’s unclear learn how to assemble the code for the witness and the controlblock, when a “Script Inclusion Proof” must be offered.

Bip-341 states:

To spend this output utilizing script D, the management block would include the next information on this order:
<management byte with leaf model and parity bit> <inner key p> <C> <E> <AB>

In my instance I am spending script_B, I assume I’ve to supply the Tag Hashes for script_A and script_C within the controlblock as inclusion proof, additionally, It’s not clear if a compact dimension of the entire controlblock is required, however I did present it utilizing the ser_string() perform. I’ve tried to implement it in Python (see code beneath), however I am getting this error:
'non-mandatory-script-verify-flag (Invalid Taproot management block dimension)'

Can anybody recommend what I is perhaps lacking or misinterpreting?

Right here is how I am developing the witness and the controlblock:

        control_block = [TAPSCRIPT_VERSION, internal_pubkey, TH_Leaf_A, TH_Leaf_C]
        control_block_bstr = ser_string(b''.be part of(component for component in control_block)
        witness_elements = [signature, script_A, control_block_bstr ]

        # Add witness parts, script and management block 
        tx2.wit.vtxinwit.append(CTxInWitness())
        tx2.wit.vtxinwit[0].scriptWitness.stack = witness_elements

and right here is my full code:

from io import BytesIO
from test_framework.tackle import program_to_witness
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
                                     CTransaction,
                                     COutPoint,
                                     CTxIn,
                                     CTxOut,
                                     CTxInWitness,
                                     ser_string
                             )
from test_framework.script import (
                                   CScript,
                                   SIGHASH_DEFAULT,
                                   TaprootSignatureHash,
                                   OP_CHECKSIG,
                                   OP_1
                            )

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.key import ( ECKey, 
                               compute_xonly_pubkey, 
                               sign_schnorr,
                               verify_schnorr,
                               TaggedHash,
                               tweak_add_pubkey
                        )
TAPSCRIPT_VERSION = bytes([0xc0])

# Facilitate key pair technology
def generate_bip340_key_pair():
    # Key pair technology
    privkey = ECKey()
    privkey.generate()
    # Compute x solely pubKey, Bip340 32 bytes Public Key
    pubkey, negated = compute_xonly_pubkey(privkey.get_bytes())
    assert_equal(len(pubkey), 32)

    return privkey.get_bytes(), pubkey

# Create TapBranch sorting lexicographically
def tapbranch_hash(left, proper):
    return TaggedHash("TapBranch", b''.be part of(sorted([left, right])))

class P2TR_Script_Path(BitcoinTestFramework):

    def set_test_params(self):
        """This methodology must be overwritten to specify take a look at parameters"""
        self.setup_clean_chain = True
        self.num_nodes = 1
        self.extra_args = [[]]

    def skip_test_if_missing_module(self):
        """
           Be aware: this perform, in addition to to skip the take a look at if no pockets was compiled, creates 
           a default pockets.
           NOTE: should you take away it, you HAVE to create the pockets, in any other case RPCs calls will fail
        """
        self.skip_if_no_wallet()


    def run_test(self):
        """Principal take a look at logic"""

        self.log.information("Begin take a look at!")
        self.log.information("Producing some Blocks to create UTXOs")        
        self.generate(self.nodes[0], COINBASE_MATURITY + 1)
        
        # After producing 101 blocks there in a UTXO for 50BTC
        utxos = self.nodes[0].listunspent()
        assert len(utxos) == 1
        assert_equal(utxos[-1]["amount"], 50) 

        # Create enter to spend from UTXO
        unspent_txid = self.nodes[0].listunspent()[-1]["txid"]
        enter = [{"txid": unspent_txid, "vout": 0}]
        self.log.information("Chosen UTXO as enter: {}".format(enter))        

        # Generate key pairs
        internal_privkey, internal_pubkey = generate_bip340_key_pair()

        privkey_A, pubkey_A = generate_bip340_key_pair()
        privkey_B, pubkey_B = generate_bip340_key_pair()
        privkey_C, pubkey_C = generate_bip340_key_pair()

        # create PK scripts
        script_A = CScript([pubkey_A, OP_CHECKSIG])
        script_B = CScript([pubkey_B, OP_CHECKSIG])
        script_C = CScript([pubkey_C, OP_CHECKSIG])

        # Hash TapLeaves with model, size and script (ser_string appends compac dimension size)
        hash_A = TAPSCRIPT_VERSION + ser_string(script_A)
        hash_B = TAPSCRIPT_VERSION + ser_string(script_B)
        hash_C = TAPSCRIPT_VERSION + ser_string(script_C)
        TH_Leaf_A = TaggedHash("TapLeaf", hash_A)
        TH_Leaf_B = TaggedHash("TapLeaf", hash_B)
        TH_Leaf_C = TaggedHash("TapLeaf", hash_C)

        # Compute branches
        branch_AB = tapbranch_hash(TH_Leaf_A, TH_Leaf_B)
        branch_ABC = tapbranch_hash(branch_AB, TH_Leaf_C)
        
        # Compute TapTweak
        tap_tweak = TaggedHash("TapTweak", internal_pubkey + branch_ABC)
        self.log.information("TapTweak: {}".format(tap_tweak.hex()))

        # Derive bech32m tackle
        taproot_PK_bytes, negated = tweak_add_pubkey(internal_pubkey, tap_tweak)
        bech32m_address = program_to_witness(1, taproot_PK_bytes)
        self.log.information("Tackle (bech32m): {}".format(bech32m_address))

        # Create Tx1 utilizing the tweaked public key
        tx1_amount = 1
        tx1_hex = self.nodes[0].createrawtransaction(inputs=enter, outputs=[{bech32m_address: tx1_amount}])
        res = self.nodes[0].signrawtransactionwithwallet(hexstring=tx1_hex)
        self.log.debug("Tx1 consequence: {}".format(res))

        tx1_hex = res["hex"]
        assert res["complete"]
        assert 'errors' not in res

        # Ship the uncooked transaction. We've not created a change output,
        # so maxfeerate have to be set to 0 to permit any price fee.
        tx1_id = self.nodes[0].sendrawtransaction(hexstring=tx1_hex, maxfeerate=0)
        decrawtx = self.nodes[0].decoderawtransaction(tx1_hex, True)
        self.log.debug("Tx1 decoded: {}".format(decrawtx))


        # Reconstruct transaction from hex 
        tx1 = CTransaction()
        tx1.deserialize(BytesIO(bytes.fromhex(tx1_hex)))
        tx1.rehash()

        # Assert the output we created is a P2TR witness_v1_taproot
        assert_equal(decrawtx['vout'][0]['scriptPubKey']['type'], 'witness_v1_taproot')
        self.log.information("Transaction {}, output 0".format(tx1_id))       
        self.log.information("despatched to {}".format(bech32m_address))       
        self.log.information("Quantity {}".format(decrawtx['vout'][0]['value']))       


        # Generate a P2TR scriptPubKey 01(segwit v1) 20(32 bytes in hex) <pubkey>
        script_pubkey = CScript([OP_1, internal_pubkey])

        # Manually assemble the Tx2, utilizing Tx1 P2TR output as enter.
        tx2 = CTransaction()
        tx2.nVersion = 2
        tx2.nLockTime = 0
        outpoint = COutPoint(int(tx1_id,16), 0)
        # No scriptSig, the signature can be on the witness stack
        tx2.vin.append(CTxIn(outpoint, b""))
        # scriptPubKey is witness v1: 0 and 32 byte public key
        dest_output = CTxOut(nValue=((tx1.vout[0].nValue)- 1000), scriptPubKey=script_pubkey)
        tx2.vout.append(dest_output)

        # Generate the taproot signature hash for signing
        # SIGHASH_ALL_TAPROOT is 0x00
        sighash = TaprootSignatureHash(  tx2, 
                                                [tx1.vout[0]], 
                                                SIGHASH_DEFAULT, 
                                                input_index = 0, 
                                                scriptpath = True,
                                                script = script_B 
                                             )
         
        # All schnorr sighashes besides SIGHASH_DEFAULT require
        # the hash_type appended to the top of signature
        signature = sign_schnorr(privkey_B, sighash)

        witness_elements = [signature, script_A, TAPSCRIPT_VERSION, internal_pubkey, TH_Leaf_A, TH_Leaf_C]

        # Add witness parts, script and management block 
        tx2.wit.vtxinwit.append(CTxInWitness())
        tx2.wit.vtxinwit[0].scriptWitness.stack = witness_elements
        tx2.rehash()

        tx2_hex = tx2.serialize().hex()
        decrawtx = self.nodes[0].decoderawtransaction(tx2_hex, True)
        descriptor = decrawtx['vout'][0]['scriptPubKey']['desc']
        assert self.nodes[0].testmempoolaccept(rawtxs=[tx2_hex], maxfeerate=0)[0]['allowed']
        tx2_id = self.nodes[0].sendrawtransaction(hexstring=tx2_hex)
        tackle = decrawtx['vout'][0]['scriptPubKey']['address']
        self.log.information("P2TR Script Path Transaction {}".format(tx2_id))       
        self.log.information("despatched to {}".format(tackle))       
        self.log.information("Descriptor {}".format(descriptor))       

if __name__ == '__main__':
    P2TR_Script_Path().essential()

That is the uncooked tx decomposed.

model:            02000000 
marker and flag:    0001
variety of inputs:   01
  txid:             2dc0a18c1b9bd09070380bc3a4c5cc4dda81649462397aa529b7234194a924c5
  vout:             00000000
  scriptsig:        00
  sequence:         00000000
variety of outputs:  01
  quantity:           18ddf50500000000
  scriptpubkey:     225120e661a607f63da586616de255ecb27ce2b835b5c266015b1c04b9c57a83697316
objects witness stack:06
  signature:        40 9a5e6dbfc92135a6ec18c1c87b401e58eb38da51f79207002461961decf6782f8bdceffd3b751eae9bb3e0877190790def5bd9076aabac6ae3bc14a1ef0adac4 
  script:           22 20814c44e4784059f451e40f8d8da0dd3b79ed11f8788b02f9aa312d7d219012a3ac
Tapscrit model:   01 c0
Inside PK:        20 e661a607f63da586616de255ecb27ce2b835b5c266015b1c04b9c57a83697316
TH_Leaf_A:          20 28ddf0751e454d7c930449733c959a8e0b8bb996930f9333739141c0d43ca593
TH_Leaf_C           20 f48d3a446abe298f73ad7075b6f11a410f5defe6708f41832cd4044c9cf78998
locktime:           00000000

Share this
Tags

Must-read

Common Motors names new CEO of troubled self-driving subsidiary Cruise | GM

Common Motors on Tuesday named a veteran know-how government with roots within the online game business to steer its troubled robotaxi service Cruise...

Meet Mercy and Anita – the African employees driving the AI revolution, for simply over a greenback an hour | Synthetic intelligence (AI)

Mercy craned ahead, took a deep breath and loaded one other process on her pc. One after one other, disturbing photographs and movies...

Tesla’s worth drops $60bn after traders fail to hail self-driving ‘Cybercab’ | Automotive business

Tesla shares fell practically 9% on Friday, wiping about $60bn (£45bn) from the corporate’s worth, after the long-awaited unveiling of its so-called robotaxi...

Recent articles

More like this

LEAVE A REPLY

Please enter your comment!
Please enter your name here