feat(core): Support Taproot in SignatureVerifier.

pull/1918/head
Andrew Kozlik 3 years ago committed by Andrew Kozlik
parent a17cdb7cfa
commit 84e31310a1

@ -0,0 +1 @@
Support replacement transactions with Taproot inputs in Bitcoin.

@ -0,0 +1 @@
Support pre-signed external Taproot inputs in Bitcoin.

@ -374,7 +374,7 @@ class Bitcoin:
verifier = SignatureVerifier( verifier = SignatureVerifier(
script_pubkey, txi.script_sig, txi.witness, self.coin script_pubkey, txi.script_sig, txi.witness, self.coin
) )
verifier.ensure_hash_type(SIGHASH_ALL) verifier.ensure_hash_type((SIGHASH_ALL_TAPROOT, self.get_hash_type(txi)))
tx_digest = await self.get_tx_digest( tx_digest = await self.get_tx_digest(
orig.verification_index, orig.verification_index,
txi, txi,
@ -439,7 +439,7 @@ class Bitcoin:
script_pubkey, txi.script_sig, txi.witness, self.coin script_pubkey, txi.script_sig, txi.witness, self.coin
) )
verifier.ensure_hash_type(self.get_hash_type(txi)) verifier.ensure_hash_type((SIGHASH_ALL_TAPROOT, self.get_hash_type(txi)))
tx_digest = await self.get_tx_digest( tx_digest = await self.get_tx_digest(
i, i,

@ -1,9 +1,9 @@
from trezor import utils, wire from trezor import utils, wire
from trezor.crypto import der from trezor.crypto import 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 .common import ecdsa_hash_pubkey from .common import OP_0, OP_1, ecdsa_hash_pubkey
from .scripts import ( from .scripts import (
output_script_native_segwit, output_script_native_segwit,
output_script_p2pkh, output_script_p2pkh,
@ -11,13 +11,16 @@ from .scripts import (
parse_input_script_multisig, parse_input_script_multisig,
parse_input_script_p2pkh, parse_input_script_p2pkh,
parse_output_script_multisig, parse_output_script_multisig,
parse_output_script_p2tr,
parse_witness_multisig, parse_witness_multisig,
parse_witness_p2tr,
parse_witness_p2wpkh, parse_witness_p2wpkh,
write_input_script_p2wpkh_in_p2sh, write_input_script_p2wpkh_in_p2sh,
write_input_script_p2wsh_in_p2sh, write_input_script_p2wsh_in_p2sh,
) )
if False: if False:
from typing import Sequence
from apps.common.coininfo import CoinInfo from apps.common.coininfo import CoinInfo
@ -32,6 +35,7 @@ class SignatureVerifier:
self.threshold = 1 self.threshold = 1
self.public_keys: list[memoryview] = [] self.public_keys: list[memoryview] = []
self.signatures: list[tuple[memoryview, int]] = [] self.signatures: list[tuple[memoryview, int]] = []
self.is_taproot = False
if not script_sig: if not script_sig:
if not witness: if not witness:
@ -44,12 +48,16 @@ class SignatureVerifier:
raise wire.DataError("Invalid public key hash") raise wire.DataError("Invalid public key hash")
self.public_keys = [public_key] self.public_keys = [public_key]
self.signatures = [(signature, hash_type)] self.signatures = [(signature, hash_type)]
elif len(script_pubkey) == 34: # P2WSH elif len(script_pubkey) == 34 and script_pubkey[0] == OP_0: # P2WSH
script, self.signatures = parse_witness_multisig(witness) script, self.signatures = parse_witness_multisig(witness)
script_hash = sha256(script).digest() script_hash = sha256(script).digest()
if output_script_native_segwit(0, script_hash) != script_pubkey: if output_script_native_segwit(0, script_hash) != script_pubkey:
raise wire.DataError("Invalid script hash") raise wire.DataError("Invalid script hash")
self.public_keys, self.threshold = parse_output_script_multisig(script) self.public_keys, self.threshold = parse_output_script_multisig(script)
elif len(script_pubkey) == 34 and script_pubkey[0] == OP_1: # P2TR
self.is_taproot = True
self.public_keys = [parse_output_script_p2tr(script_pubkey)]
self.signatures = [parse_witness_p2tr(witness)]
else: else:
raise wire.DataError("Unsupported signature script") raise wire.DataError("Unsupported signature script")
elif witness and witness != b"\x00": elif witness and witness != b"\x00":
@ -98,8 +106,8 @@ class SignatureVerifier:
if self.threshold != len(self.signatures): if self.threshold != len(self.signatures):
raise wire.DataError("Invalid signature") raise wire.DataError("Invalid signature")
def ensure_hash_type(self, hash_type: int) -> None: def ensure_hash_type(self, hash_types: Sequence[int]) -> None:
if any(h != hash_type for _, h in self.signatures): if any(h not in hash_types for _, h in self.signatures):
raise wire.DataError("Unsupported sighash type") raise wire.DataError("Unsupported sighash type")
def verify(self, digest: bytes) -> None: def verify(self, digest: bytes) -> None:
@ -108,6 +116,17 @@ class SignatureVerifier:
# different hash type than expected, then verification will fail. To # different hash type than expected, then verification will fail. To
# return the proper error message, the caller can optionally check the # return the proper error message, the caller can optionally check the
# hash type by using ensure_hash_type() before calling verify. # hash type by using ensure_hash_type() before calling verify.
if self.is_taproot:
self.verify_bip340(digest)
else:
self.verify_ecdsa(digest)
def verify_bip340(self, digest: bytes) -> None:
if not bip340.verify(self.public_keys[0], self.signatures[0][0], digest):
raise wire.DataError("Invalid signature")
def verify_ecdsa(self, digest: bytes) -> None:
try: try:
i = 0 i = 0
for der_signature, _ in self.signatures: for der_signature, _ in self.signatures:

Loading…
Cancel
Save