1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 23:48:12 +00:00

chore(core): In apps.bitcoin allow get_tx_digest() to be used for original transactions.

This commit is contained in:
Andrew Kozlik 2020-09-09 17:45:55 +02:00 committed by Andrew Kozlik
parent b636e959f5
commit 7c2d690e45
4 changed files with 71 additions and 40 deletions

View File

@ -239,21 +239,22 @@ class Bitcoin:
self, self,
i: int, i: int,
txi: TxInput, txi: TxInput,
tx: Union[SignTx, PrevTx],
hash143: Hash143,
h_approved: HashWriter,
public_keys: List[bytes], public_keys: List[bytes],
threshold: int, threshold: int,
script_pubkey: bytes, script_pubkey: bytes,
tx_hash: Optional[bytes] = None,
) -> bytes: ) -> bytes:
if txi.witness: if txi.witness:
return self.hash143.preimage_hash( return hash143.preimage_hash(
txi, txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi),
public_keys,
threshold,
self.tx,
self.coin,
self.get_sighash_type(txi),
) )
else: else:
digest, _, _ = await self.get_legacy_tx_digest(i, script_pubkey) digest, _, _ = await self.get_legacy_tx_digest(
i, tx, h_approved, script_pubkey, tx_hash
)
return digest return digest
async def verify_external_input( async def verify_external_input(
@ -276,7 +277,14 @@ class Bitcoin:
verifier.ensure_hash_type(self.get_hash_type(txi)) verifier.ensure_hash_type(self.get_hash_type(txi))
tx_digest = await self.get_tx_digest( tx_digest = await self.get_tx_digest(
i, txi, verifier.public_keys, verifier.threshold, script_pubkey i,
txi,
self.tx,
self.hash143,
self.h_approved,
verifier.public_keys,
verifier.threshold,
script_pubkey,
) )
verifier.verify(tx_digest) verifier.verify(tx_digest)
@ -347,27 +355,34 @@ class Bitcoin:
) )
async def get_legacy_tx_digest( async def get_legacy_tx_digest(
self, index: int, script_pubkey: Optional[bytes] = None self,
index: int,
tx: Union[SignTx, PrevTx],
h_approved: HashWriter,
script_pubkey: Optional[bytes] = None,
tx_hash: Optional[bytes] = None,
) -> Tuple[bytes, TxInput, Optional[bip32.HDNode]]: ) -> Tuple[bytes, TxInput, Optional[bip32.HDNode]]:
# the transaction digest which gets signed for this input # the transaction digest which gets signed for this input
h_sign = self.create_hash_writer() h_sign = self.create_hash_writer()
# should come out the same as h_approved, checked before signing the digest # should come out the same as h_approved, checked before signing the digest
h_check = self.create_hash_writer() h_check = self.create_hash_writer()
self.write_tx_header(h_sign, self.tx, witness_marker=False) self.write_tx_header(h_sign, tx, witness_marker=False)
write_bitcoin_varint(h_sign, self.tx.inputs_count) write_bitcoin_varint(h_sign, tx.inputs_count)
for i in range(self.tx.inputs_count): for i in range(tx.inputs_count):
# STAGE_REQUEST_4_INPUT in legacy # STAGE_REQUEST_4_INPUT 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, tx_hash)
writers.write_tx_input_check(h_check, txi) writers.write_tx_input_check(h_check, txi)
# Only the previous UTXO's scriptPubKey is included in h_sign. # Only the previous UTXO's scriptPubKey is included in h_sign.
if i == index: if i == index:
txi_sign = txi txi_sign = txi
node = None node = None
if not script_pubkey: if not script_pubkey:
self.wallet_path.check_input(txi) if isinstance(tx, SignTx):
self.multisig_fingerprint.check_input(txi) self.wallet_path.check_input(txi)
self.multisig_fingerprint.check_input(txi)
node = self.keychain.derive(txi.address_n) node = self.keychain.derive(txi.address_n)
key_sign_pub = node.public_key() key_sign_pub = node.public_key()
if txi.multisig: if txi.multisig:
@ -390,27 +405,29 @@ class Bitcoin:
else: else:
self.write_tx_input(h_sign, txi, bytes()) self.write_tx_input(h_sign, txi, bytes())
write_bitcoin_varint(h_sign, self.tx.outputs_count) write_bitcoin_varint(h_sign, tx.outputs_count)
for i in range(self.tx.outputs_count): for i in range(tx.outputs_count):
# STAGE_REQUEST_4_OUTPUT in legacy # STAGE_REQUEST_4_OUTPUT in legacy
txo = await helpers.request_tx_output(self.tx_req, i, self.coin) txo = await helpers.request_tx_output(self.tx_req, i, self.coin, tx_hash)
script_pubkey = self.output_derive_script(txo) script_pubkey = self.output_derive_script(txo)
self.write_tx_output(h_check, txo, script_pubkey) self.write_tx_output(h_check, txo, script_pubkey)
self.write_tx_output(h_sign, txo, script_pubkey) self.write_tx_output(h_sign, txo, script_pubkey)
writers.write_uint32(h_sign, self.tx.lock_time) writers.write_uint32(h_sign, tx.lock_time)
writers.write_uint32(h_sign, self.get_sighash_type(txi_sign)) writers.write_uint32(h_sign, self.get_sighash_type(txi_sign))
# check that the inputs were the same as those streamed for approval # check that the inputs were the same as those streamed for approval
if self.h_approved.get_digest() != h_check.get_digest(): if h_approved.get_digest() != h_check.get_digest():
raise wire.ProcessError("Transaction has changed during signing") raise wire.ProcessError("Transaction has changed during signing")
tx_digest = writers.get_tx_hash(h_sign, double=self.coin.sign_hash_double) tx_digest = writers.get_tx_hash(h_sign, double=self.coin.sign_hash_double)
return tx_digest, txi_sign, node return tx_digest, txi_sign, node
async def sign_nonsegwit_input(self, i: int) -> None: async def sign_nonsegwit_input(self, i: int) -> None:
tx_digest, txi, node = await self.get_legacy_tx_digest(i) tx_digest, txi, node = await self.get_legacy_tx_digest(
i, self.tx, self.h_approved
)
assert node is not None assert node is not None
# compute the signature from the tx digest # compute the signature from the tx digest

View File

@ -9,10 +9,11 @@ from apps.common.writers import write_bitcoin_varint
from .. import multisig, writers from .. import multisig, writers
from . import helpers from . import helpers
from .bitcoin import Bitcoin, input_is_nonsegwit from .bitcoin import Bitcoin, Hash143, input_is_nonsegwit
if False: if False:
from typing import List, Union from typing import List, Optional, Union
from trezor.utils import HashWriter
_SIGHASH_FORKID = const(0x40) _SIGHASH_FORKID = const(0x40)
@ -44,22 +45,21 @@ class Bitcoinlike(Bitcoin):
self, self,
i: int, i: int,
txi: TxInput, txi: TxInput,
tx: Union[SignTx, PrevTx],
hash143: Hash143,
h_approved: HashWriter,
public_keys: List[bytes], public_keys: List[bytes],
threshold: int, threshold: int,
script_pubkey: bytes, script_pubkey: bytes,
tx_hash: Optional[bytes] = None,
) -> bytes: ) -> bytes:
if self.coin.force_bip143: if self.coin.force_bip143:
return self.hash143.preimage_hash( return hash143.preimage_hash(
txi, txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi),
public_keys,
threshold,
self.tx,
self.coin,
self.get_sighash_type(txi),
) )
else: else:
return await super().get_tx_digest( return await super().get_tx_digest(
i, txi, public_keys, threshold, script_pubkey i, txi, tx, hash143, h_approved, public_keys, threshold, script_pubkey
) )
def get_sighash_type(self, txi: TxInput) -> int: def get_sighash_type(self, txi: TxInput) -> int:

View File

@ -8,6 +8,8 @@ from trezor.messages.RequestType import (
TXFINISHED, TXFINISHED,
TXINPUT, TXINPUT,
TXMETA, TXMETA,
TXORIGINPUT,
TXORIGOUTPUT,
TXOUTPUT, TXOUTPUT,
) )
from trezor.messages.SignTx import SignTx from trezor.messages.SignTx import SignTx
@ -29,7 +31,7 @@ from ..writers import TX_HASH_SIZE
from . import layout from . import layout
if False: if False:
from typing import Any, Awaitable from typing import Any, Awaitable, Optional
# Machine instructions # Machine instructions
@ -170,9 +172,13 @@ def request_tx_extra_data( # type: ignore
return ack.tx.extra_data_chunk return ack.tx.extra_data_chunk
def request_tx_input(tx_req: TxRequest, i: int, coin: CoinInfo) -> Awaitable[TxInput]: # type: ignore def request_tx_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: Optional[bytes] = None) -> Awaitable[TxInput]: # type: ignore
assert tx_req.details is not None assert tx_req.details is not None
tx_req.request_type = TXINPUT if tx_hash:
tx_req.request_type = TXORIGINPUT
tx_req.details.tx_hash = tx_hash
else:
tx_req.request_type = TXINPUT
tx_req.details.request_index = i tx_req.details.request_index = i
ack = yield TxAckInput, tx_req ack = yield TxAckInput, tx_req
_clear_tx_request(tx_req) _clear_tx_request(tx_req)
@ -189,9 +195,13 @@ def request_tx_prev_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: by
return sanitize_tx_prev_input(ack.tx.input, coin) return sanitize_tx_prev_input(ack.tx.input, coin)
def request_tx_output(tx_req: TxRequest, i: int, coin: CoinInfo) -> Awaitable[TxOutput]: # type: ignore def request_tx_output(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: Optional[bytes] = None) -> Awaitable[TxOutput]: # type: ignore
assert tx_req.details is not None assert tx_req.details is not None
tx_req.request_type = TXOUTPUT if tx_hash:
tx_req.request_type = TXORIGOUTPUT
tx_req.details.tx_hash = tx_hash
else:
tx_req.request_type = TXOUTPUT
tx_req.details.request_index = i tx_req.details.request_index = i
ack = yield TxAckOutput, tx_req ack = yield TxAckOutput, tx_req
_clear_tx_request(tx_req) _clear_tx_request(tx_req)

View File

@ -30,7 +30,7 @@ from .hash143 import Hash143
if False: if False:
from apps.common import coininfo from apps.common import coininfo
from typing import List, Union from typing import List, Optional, Union
from ..writers import Writer from ..writers import Writer
OVERWINTERED = const(0x80000000) OVERWINTERED = const(0x80000000)
@ -134,12 +134,16 @@ class Zcashlike(Bitcoinlike):
self, self,
i: int, i: int,
txi: TxInput, txi: TxInput,
tx: Union[SignTx, PrevTx],
hash143: Hash143,
h_approved: HashWriter,
public_keys: List[bytes], public_keys: List[bytes],
threshold: int, threshold: int,
script_pubkey: bytes, script_pubkey: bytes,
tx_hash: Optional[bytes] = None,
) -> bytes: ) -> bytes:
return self.hash143.preimage_hash( return hash143.preimage_hash(
txi, public_keys, threshold, self.tx, self.coin, self.get_sighash_type(txi) txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi)
) )
def write_tx_header( def write_tx_header(