For this particular error, the standard suspects are double length-prefixing or by chance together with the annex. Double-prefixing is the one I see most frequently.
The management block must be one witness stack factor, uncooked bytes. Structure:
<1 byte: leaf_version | parity>
<32 bytes: inner key>
<32 bytes × depth: merkle path hashes>
(parity comes from the tweaked pubkey’s y-coordinate odd/even—you may have it when you derived Q appropriately.) No compactSize inside. No splitting.
Your ser_string name — that is the issue. Witness serialization already length-prefixes every stack merchandise. So once you do ser_string(control_block), you are including an additional prefix. The node checks (len - 33) % 32 == 0; one further byte and that examine fails, therefore Invalid Taproot management block measurement. Simply concatenate:
control_block = b''.be part of([
bytes([leaf_version | parity]),
internal_pubkey,
merkle_hash_1,
merkle_hash_2
])
witness_stack = [preimage_or_sig, script, control_block]
Splitting into separate components — additionally fallacious. The node takes the final factor because the management block. In the event you break up, it solely sees hash2 (32 bytes) or no matter is final, so the dimensions examine fails. It’s also possible to hit bad-witness-nonstandard earlier than script validation, relying on coverage.
Helpful approach to consider it: layer 1 is BIP 341’s management block format (33+32m bytes, validated by that (len-33)%32==0 examine). Layer 2 is witness encoding (size prefix per factor). You solely outline layer 1; layer 2 is computerized. Do not combine them.
If dropping ser_string and utilizing a single blob fixes it, that was the problem. I’ve reproduced this on regtest—toggle solely the management block construct and the habits matches.
