1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-03-12 14:16:06 +00:00

multisig: compute dummy chaincode based on pubkeys

Compute a dummy xpub to use as the internal pubkey
for taproot multisig. Sort and remove duplicates of all
xpub pubkeys, concatenate and compute sha256 hash.
The result is the chaincode, and the pubkey is the NUMS
point.
This commit is contained in:
Andrew Toth 2024-09-17 10:52:52 -04:00
parent 0bddc028c5
commit d06623218f
No known key found for this signature in database
GPG Key ID: 60007AFC8938B018

View File

@ -59,14 +59,10 @@ def validate_multisig(multisig: MultisigRedeemScriptType) -> None:
def multisig_pubkey_index(multisig: MultisigRedeemScriptType, pubkey: bytes) -> int:
validate_multisig(multisig)
if multisig.nodes:
for i, hd_node in enumerate(multisig.nodes):
if multisig_get_pubkey(hd_node, multisig.address_n) == pubkey:
return i
else:
for i, hd in enumerate(multisig.pubkeys):
if multisig_get_pubkey(hd.node, hd.address_n) == pubkey:
return i
pubkeys = multisig_get_pubkeys(multisig)
for i, derived_pubkey in enumerate(pubkeys):
if derived_pubkey == pubkey:
return i
raise DataError("Pubkey not found in multisig script")
@ -85,19 +81,44 @@ def multisig_get_pubkey(n: HDNodeType, p: paths.Bip32Path) -> bytes:
return node.public_key()
def compute_taproot_dummy_chaincode(multisig: MultisigRedeemScriptType) -> bytes:
from trezor.crypto.hashlib import sha256
from trezor.utils import HashWriter
from .writers import write_bytes_fixed
if len(multisig.address_n) != 2:
raise DataError("Taproot multisig must use xpub derivation depth of 2")
if multisig.nodes:
pubkeys = [hd.public_key for hd in multisig.nodes]
else:
pubkeys = [hd.public_key for hd in multisig.pubkeys]
pubkeys.sort()
h = HashWriter(sha256())
prev = None
for pubkey in pubkeys:
if prev == pubkey:
continue
prev = pubkey
write_bytes_fixed(h, pubkey, 33)
return h.get_digest()
def multisig_get_dummy_pubkey(multisig: MultisigRedeemScriptType) -> bytes:
from trezor.crypto import bip32
# The following encodes this xpub into an HDNode. It is the NUMS point suggested
# in BIP341, with a chaincode of 32 0 bytes. Deriving a pubkey from this node
# results in a provably unspendable pubkey.
# in BIP341, with a chaincode derived from the sha256 of the sorted public keys with duplicates removed.
# Deriving a pubkey from this node results in a provably unspendable pubkey.
# https://delvingbitcoin.org/t/unspendable-keys-in-descriptors/304
# xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6QgnecKFpJFPpdzxKrwoaZoV44qAJewsc4kX9vGaCaBExuvJH57
node = bip32.HDNode(
depth=0,
fingerprint=2084970077,
child_num=0,
chain_code=b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
chain_code=compute_taproot_dummy_chaincode(multisig),
public_key=b"\x02P\x92\x9bt\xc1\xa0IT\xb7\x8bK`5\xe9z^\x07\x8aZ\x0f(\xec\x96\xd5G\xbf\xee\x9a\xce\x80:\xc0",
)