mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-03 12:00:59 +00:00
feat(core): Implement Taproot signing.
This commit is contained in:
parent
381e8bc85a
commit
a17cdb7cfa
1
core/.changelog.d/1656.added.1
Normal file
1
core/.changelog.d/1656.added.1
Normal file
@ -0,0 +1 @@
|
|||||||
|
Support spending from Taproot UTXOs.
|
@ -2,7 +2,7 @@ from micropython import const
|
|||||||
|
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.crypto import bech32, bip32, der
|
from trezor.crypto import bech32, bip32, der
|
||||||
from trezor.crypto.curve import secp256k1
|
from trezor.crypto.curve import bip340, secp256k1
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
from trezor.enums import InputScriptType, OutputScriptType
|
from trezor.enums import InputScriptType, OutputScriptType
|
||||||
from trezor.utils import HashWriter, ensure
|
from trezor.utils import HashWriter, ensure
|
||||||
@ -78,6 +78,12 @@ def ecdsa_sign(node: bip32.HDNode, digest: bytes) -> bytes:
|
|||||||
return sigder
|
return sigder
|
||||||
|
|
||||||
|
|
||||||
|
def bip340_sign(node: bip32.HDNode, digest: bytes) -> bytes:
|
||||||
|
internal_private_key = node.private_key()
|
||||||
|
output_private_key = bip340.tweak_secret_key(internal_private_key)
|
||||||
|
return bip340.sign(output_private_key, digest)
|
||||||
|
|
||||||
|
|
||||||
def ecdsa_hash_pubkey(pubkey: bytes, coin: CoinInfo) -> bytes:
|
def ecdsa_hash_pubkey(pubkey: bytes, coin: CoinInfo) -> bytes:
|
||||||
if pubkey[0] == 0x04:
|
if pubkey[0] == 0x04:
|
||||||
ensure(len(pubkey) == 65) # uncompressed format
|
ensure(len(pubkey) == 65) # uncompressed format
|
||||||
|
@ -9,7 +9,14 @@ from trezor.utils import HashWriter, empty_bytearray, ensure
|
|||||||
from apps.common.writers import write_bitcoin_varint
|
from apps.common.writers import write_bitcoin_varint
|
||||||
|
|
||||||
from .. import addresses, common, multisig, scripts, writers
|
from .. import addresses, common, multisig, scripts, writers
|
||||||
from ..common import SIGHASH_ALL, ecdsa_sign, input_is_external, input_is_segwit
|
from ..common import (
|
||||||
|
SIGHASH_ALL,
|
||||||
|
SIGHASH_ALL_TAPROOT,
|
||||||
|
bip340_sign,
|
||||||
|
ecdsa_sign,
|
||||||
|
input_is_external,
|
||||||
|
input_is_segwit,
|
||||||
|
)
|
||||||
from ..ownership import verify_nonownership
|
from ..ownership import verify_nonownership
|
||||||
from ..verification import SignatureVerifier
|
from ..verification import SignatureVerifier
|
||||||
from . import approvers, helpers, progress
|
from . import approvers, helpers, progress
|
||||||
@ -459,8 +466,13 @@ class Bitcoin:
|
|||||||
raise wire.ProcessError("Transaction has changed during signing")
|
raise wire.ProcessError("Transaction has changed during signing")
|
||||||
self.tx_info.check_input(txi)
|
self.tx_info.check_input(txi)
|
||||||
|
|
||||||
node = self.keychain.derive(txi.address_n)
|
if txi.script_type == InputScriptType.SPENDP2SHWITNESS:
|
||||||
key_sign_pub = node.public_key()
|
node = self.keychain.derive(txi.address_n)
|
||||||
|
key_sign_pub = node.public_key()
|
||||||
|
else:
|
||||||
|
# Native SegWit has an empty scriptSig. Public key is not needed.
|
||||||
|
key_sign_pub = b""
|
||||||
|
|
||||||
self.write_tx_input_derived(self.serialized_tx, txi, key_sign_pub, b"")
|
self.write_tx_input_derived(self.serialized_tx, txi, key_sign_pub, b"")
|
||||||
|
|
||||||
def sign_bip143_input(self, txi: TxInput) -> tuple[bytes, bytes]:
|
def sign_bip143_input(self, txi: TxInput) -> tuple[bytes, bytes]:
|
||||||
@ -489,29 +501,53 @@ class Bitcoin:
|
|||||||
|
|
||||||
return public_key, signature
|
return public_key, signature
|
||||||
|
|
||||||
|
def sign_taproot_input(self, i: int, txi: TxInput) -> bytes:
|
||||||
|
self.tx_info.check_input(txi)
|
||||||
|
sigmsg_digest = self.tx_info.hash143.preimage_hash(
|
||||||
|
i,
|
||||||
|
txi,
|
||||||
|
[],
|
||||||
|
1,
|
||||||
|
self.tx_info.tx,
|
||||||
|
self.coin,
|
||||||
|
self.get_sighash_type(txi),
|
||||||
|
)
|
||||||
|
|
||||||
|
node = self.keychain.derive(txi.address_n)
|
||||||
|
return bip340_sign(node, sigmsg_digest)
|
||||||
|
|
||||||
async def sign_segwit_input(self, i: int) -> None:
|
async def sign_segwit_input(self, i: int) -> None:
|
||||||
# STAGE_REQUEST_SEGWIT_WITNESS in legacy
|
# STAGE_REQUEST_SEGWIT_WITNESS in legacy
|
||||||
txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
|
txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
|
||||||
if txi.script_type not in common.SEGWIT_INPUT_SCRIPT_TYPES:
|
if txi.script_type not in common.SEGWIT_INPUT_SCRIPT_TYPES:
|
||||||
raise wire.ProcessError("Transaction has changed during signing")
|
raise wire.ProcessError("Transaction has changed during signing")
|
||||||
|
|
||||||
public_key, signature = self.sign_bip143_input(txi)
|
if txi.script_type == InputScriptType.SPENDTAPROOT:
|
||||||
|
signature = self.sign_taproot_input(i, txi)
|
||||||
self.set_serialized_signature(i, signature)
|
scripts.write_witness_p2tr(
|
||||||
if txi.multisig:
|
self.serialized_tx, signature, self.get_hash_type(txi)
|
||||||
# find out place of our signature based on the pubkey
|
|
||||||
signature_index = multisig.multisig_pubkey_index(txi.multisig, public_key)
|
|
||||||
scripts.write_witness_multisig(
|
|
||||||
self.serialized_tx,
|
|
||||||
txi.multisig,
|
|
||||||
signature,
|
|
||||||
signature_index,
|
|
||||||
self.get_hash_type(txi),
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
scripts.write_witness_p2wpkh(
|
public_key, signature = self.sign_bip143_input(txi)
|
||||||
self.serialized_tx, signature, public_key, self.get_hash_type(txi)
|
|
||||||
)
|
if txi.multisig:
|
||||||
|
# find out place of our signature based on the pubkey
|
||||||
|
signature_index = multisig.multisig_pubkey_index(
|
||||||
|
txi.multisig, public_key
|
||||||
|
)
|
||||||
|
scripts.write_witness_multisig(
|
||||||
|
self.serialized_tx,
|
||||||
|
txi.multisig,
|
||||||
|
signature,
|
||||||
|
signature_index,
|
||||||
|
self.get_hash_type(txi),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
scripts.write_witness_p2wpkh(
|
||||||
|
self.serialized_tx, signature, public_key, self.get_hash_type(txi)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.set_serialized_signature(i, signature)
|
||||||
|
|
||||||
async def get_legacy_tx_digest(
|
async def get_legacy_tx_digest(
|
||||||
self,
|
self,
|
||||||
@ -654,7 +690,10 @@ class Bitcoin:
|
|||||||
# ===
|
# ===
|
||||||
|
|
||||||
def get_sighash_type(self, txi: TxInput) -> int:
|
def get_sighash_type(self, txi: TxInput) -> int:
|
||||||
return SIGHASH_ALL
|
if common.input_is_taproot(txi):
|
||||||
|
return SIGHASH_ALL_TAPROOT
|
||||||
|
else:
|
||||||
|
return SIGHASH_ALL
|
||||||
|
|
||||||
def get_hash_type(self, txi: TxInput) -> int:
|
def get_hash_type(self, txi: TxInput) -> int:
|
||||||
""" Return the nHashType flags."""
|
""" Return the nHashType flags."""
|
||||||
|
Loading…
Reference in New Issue
Block a user