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