mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 12:28:09 +00:00
feat(core): Implement sending to taproot addresses.
This commit is contained in:
parent
ea0fb08fed
commit
9d03112846
1
core/.changelog.d/1656.added
Normal file
1
core/.changelog.d/1656.added
Normal file
@ -0,0 +1 @@
|
||||
Support sending to Taproot addresses.
|
@ -10,7 +10,7 @@ from apps.common.coininfo import CoinInfo
|
||||
|
||||
from .common import ecdsa_hash_pubkey, encode_bech32_address
|
||||
from .multisig import multisig_get_pubkeys, multisig_pubkey_index
|
||||
from .scripts import output_script_native_p2wpkh_or_p2wsh, write_output_script_multisig
|
||||
from .scripts import output_script_native_segwit, write_output_script_multisig
|
||||
|
||||
if False:
|
||||
from trezor.crypto import bip32
|
||||
@ -109,13 +109,13 @@ def address_p2sh(redeem_script_hash: bytes, coin: CoinInfo) -> str:
|
||||
|
||||
def address_p2wpkh_in_p2sh(pubkey: bytes, coin: CoinInfo) -> str:
|
||||
pubkey_hash = ecdsa_hash_pubkey(pubkey, coin)
|
||||
redeem_script = output_script_native_p2wpkh_or_p2wsh(pubkey_hash)
|
||||
redeem_script = output_script_native_segwit(0, pubkey_hash)
|
||||
redeem_script_hash = coin.script_hash(redeem_script).digest()
|
||||
return address_p2sh(redeem_script_hash, coin)
|
||||
|
||||
|
||||
def address_p2wsh_in_p2sh(witness_script_hash: bytes, coin: CoinInfo) -> str:
|
||||
redeem_script = output_script_native_p2wpkh_or_p2wsh(witness_script_hash)
|
||||
redeem_script = output_script_native_segwit(0, witness_script_hash)
|
||||
redeem_script_hash = coin.script_hash(redeem_script).digest()
|
||||
return address_p2sh(redeem_script_hash, coin)
|
||||
|
||||
|
@ -7,6 +7,7 @@ from trezor.enums import InputScriptType, OutputScriptType
|
||||
from trezor.utils import ensure
|
||||
|
||||
if False:
|
||||
from typing import Tuple
|
||||
from apps.common.coininfo import CoinInfo
|
||||
from trezor.messages import TxInput
|
||||
|
||||
@ -84,12 +85,12 @@ def encode_bech32_address(prefix: str, witver: int, script: bytes) -> str:
|
||||
return address
|
||||
|
||||
|
||||
def decode_bech32_address(prefix: str, address: str) -> bytes:
|
||||
def decode_bech32_address(prefix: str, address: str) -> Tuple[int, bytes]:
|
||||
witver, raw = bech32.decode(prefix, address)
|
||||
if witver not in _BECH32_WITVERS:
|
||||
raise wire.ProcessError("Invalid address witness program")
|
||||
assert raw is not None
|
||||
return bytes(raw)
|
||||
return witver, bytes(raw)
|
||||
|
||||
|
||||
def input_is_segwit(txi: TxInput) -> bool:
|
||||
|
@ -60,9 +60,9 @@ def write_input_script_prefixed(
|
||||
write_input_script_p2wpkh_in_p2sh(
|
||||
w, common.ecdsa_hash_pubkey(pubkey, coin), prefixed=True
|
||||
)
|
||||
elif script_type == InputScriptType.SPENDWITNESS:
|
||||
# native p2wpkh or p2wsh
|
||||
script_sig = input_script_native_p2wpkh_or_p2wsh()
|
||||
elif script_type in (InputScriptType.SPENDWITNESS, InputScriptType.SPENDTAPROOT):
|
||||
# native p2wpkh or p2wsh or p2tr
|
||||
script_sig = input_script_native_segwit()
|
||||
write_bytes_prefixed(w, script_sig)
|
||||
elif script_type == InputScriptType.SPENDMULTISIG:
|
||||
# p2sh multisig
|
||||
@ -77,9 +77,9 @@ def write_input_script_prefixed(
|
||||
|
||||
def output_derive_script(address: str, coin: CoinInfo) -> bytes:
|
||||
if coin.bech32_prefix and address.startswith(coin.bech32_prefix):
|
||||
# p2wpkh or p2wsh
|
||||
witprog = common.decode_bech32_address(coin.bech32_prefix, address)
|
||||
return output_script_native_p2wpkh_or_p2wsh(witprog)
|
||||
# p2wpkh or p2wsh or p2tr
|
||||
witver, witprog = common.decode_bech32_address(coin.bech32_prefix, address)
|
||||
return output_script_native_segwit(witver, witprog)
|
||||
|
||||
if (
|
||||
not utils.BITCOIN_ONLY
|
||||
@ -205,34 +205,39 @@ def output_script_p2sh(scripthash: bytes) -> bytearray:
|
||||
return s
|
||||
|
||||
|
||||
# SegWit: Native P2WPKH or P2WSH
|
||||
# SegWit: Native P2WPKH or P2WSH or P2TR
|
||||
# ===
|
||||
#
|
||||
# P2WPKH (Pay-to-Witness-Public-Key-Hash) is native SegWit version 0 P2PKH.
|
||||
# Not backwards compatible.
|
||||
# https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh
|
||||
#
|
||||
# P2WSH (Pay-to-Witness-Script-Hash) is native SegWit version 0 P2SH.
|
||||
# Not backwards compatible.
|
||||
# https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh
|
||||
#
|
||||
# P2WPKH (Pay-to-Witness-Public-Key-Hash) is the segwit native P2PKH.
|
||||
# Not backwards compatible.
|
||||
#
|
||||
# P2WSH (Pay-to-Witness-Script-Hash) is segwit native P2SH.
|
||||
# P2TR (Pay-to-Taproot) is native SegWit version 1.
|
||||
# Not backwards compatible.
|
||||
# https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#script-validation-rules
|
||||
|
||||
|
||||
def input_script_native_p2wpkh_or_p2wsh() -> bytearray:
|
||||
def input_script_native_segwit() -> bytearray:
|
||||
# Completely replaced by the witness and therefore empty.
|
||||
return bytearray(0)
|
||||
|
||||
|
||||
def output_script_native_p2wpkh_or_p2wsh(witprog: bytes) -> bytearray:
|
||||
def output_script_native_segwit(witver: int, witprog: bytes) -> bytearray:
|
||||
# Either:
|
||||
# 00 14 <20-byte-key-hash>
|
||||
# 00 20 <32-byte-script-hash>
|
||||
# 51 20 <32-byte-taproot-output-key>
|
||||
length = len(witprog)
|
||||
utils.ensure(length == 20 or length == 32)
|
||||
utils.ensure((length == 20 and witver == 0) or length == 32)
|
||||
|
||||
w = utils.empty_bytearray(3 + length)
|
||||
w.append(0x00) # witness version byte
|
||||
w.append(length) # pub key hash length is 20 (P2WPKH) or 32 (P2WSH) bytes
|
||||
write_bytes_fixed(w, witprog, length) # pub key hash
|
||||
w = utils.empty_bytearray(2 + length)
|
||||
w.append(witver + 0x50 if witver else 0) # witness version byte (OP_witver)
|
||||
w.append(length) # witness program length is 20 (P2WPKH) or 32 (P2WSH, P2TR) bytes
|
||||
write_bytes_fixed(w, witprog, length)
|
||||
return w
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@ from trezor.crypto.hashlib import sha256
|
||||
|
||||
from .common import ecdsa_hash_pubkey
|
||||
from .scripts import (
|
||||
output_script_native_p2wpkh_or_p2wsh,
|
||||
output_script_native_segwit,
|
||||
output_script_p2pkh,
|
||||
output_script_p2sh,
|
||||
parse_input_script_multisig,
|
||||
@ -40,14 +40,14 @@ class SignatureVerifier:
|
||||
if len(script_pubkey) == 22: # P2WPKH
|
||||
public_key, signature, hash_type = parse_witness_p2wpkh(witness)
|
||||
pubkey_hash = ecdsa_hash_pubkey(public_key, coin)
|
||||
if output_script_native_p2wpkh_or_p2wsh(pubkey_hash) != script_pubkey:
|
||||
if output_script_native_segwit(0, pubkey_hash) != script_pubkey:
|
||||
raise wire.DataError("Invalid public key hash")
|
||||
self.public_keys = [public_key]
|
||||
self.signatures = [(signature, hash_type)]
|
||||
elif len(script_pubkey) == 34: # P2WSH
|
||||
script, self.signatures = parse_witness_multisig(witness)
|
||||
script_hash = sha256(script).digest()
|
||||
if output_script_native_p2wpkh_or_p2wsh(script_hash) != script_pubkey:
|
||||
if output_script_native_segwit(0, script_hash) != script_pubkey:
|
||||
raise wire.DataError("Invalid script hash")
|
||||
self.public_keys, self.threshold = parse_output_script_multisig(script)
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user