1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 15:38:11 +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,
i: int,
txi: TxInput,
tx: Union[SignTx, PrevTx],
hash143: Hash143,
h_approved: HashWriter,
public_keys: List[bytes],
threshold: int,
script_pubkey: bytes,
tx_hash: Optional[bytes] = None,
) -> bytes:
if txi.witness:
return self.hash143.preimage_hash(
txi,
public_keys,
threshold,
self.tx,
self.coin,
self.get_sighash_type(txi),
return hash143.preimage_hash(
txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi),
)
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
async def verify_external_input(
@ -276,7 +277,14 @@ class Bitcoin:
verifier.ensure_hash_type(self.get_hash_type(txi))
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)
@ -347,27 +355,34 @@ class Bitcoin:
)
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]]:
# the transaction digest which gets signed for this input
h_sign = self.create_hash_writer()
# should come out the same as h_approved, checked before signing the digest
h_check = self.create_hash_writer()
self.write_tx_header(h_sign, self.tx, witness_marker=False)
write_bitcoin_varint(h_sign, self.tx.inputs_count)
self.write_tx_header(h_sign, tx, witness_marker=False)
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
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)
# Only the previous UTXO's scriptPubKey is included in h_sign.
if i == index:
txi_sign = txi
node = None
if not script_pubkey:
self.wallet_path.check_input(txi)
self.multisig_fingerprint.check_input(txi)
if isinstance(tx, SignTx):
self.wallet_path.check_input(txi)
self.multisig_fingerprint.check_input(txi)
node = self.keychain.derive(txi.address_n)
key_sign_pub = node.public_key()
if txi.multisig:
@ -390,27 +405,29 @@ class Bitcoin:
else:
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
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)
self.write_tx_output(h_check, 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))
# 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")
tx_digest = writers.get_tx_hash(h_sign, double=self.coin.sign_hash_double)
return tx_digest, txi_sign, node
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
# 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 helpers
from .bitcoin import Bitcoin, input_is_nonsegwit
from .bitcoin import Bitcoin, Hash143, input_is_nonsegwit
if False:
from typing import List, Union
from typing import List, Optional, Union
from trezor.utils import HashWriter
_SIGHASH_FORKID = const(0x40)
@ -44,22 +45,21 @@ class Bitcoinlike(Bitcoin):
self,
i: int,
txi: TxInput,
tx: Union[SignTx, PrevTx],
hash143: Hash143,
h_approved: HashWriter,
public_keys: List[bytes],
threshold: int,
script_pubkey: bytes,
tx_hash: Optional[bytes] = None,
) -> bytes:
if self.coin.force_bip143:
return self.hash143.preimage_hash(
txi,
public_keys,
threshold,
self.tx,
self.coin,
self.get_sighash_type(txi),
return hash143.preimage_hash(
txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi),
)
else:
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:

View File

@ -8,6 +8,8 @@ from trezor.messages.RequestType import (
TXFINISHED,
TXINPUT,
TXMETA,
TXORIGINPUT,
TXORIGOUTPUT,
TXOUTPUT,
)
from trezor.messages.SignTx import SignTx
@ -29,7 +31,7 @@ from ..writers import TX_HASH_SIZE
from . import layout
if False:
from typing import Any, Awaitable
from typing import Any, Awaitable, Optional
# Machine instructions
@ -170,9 +172,13 @@ def request_tx_extra_data( # type: ignore
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
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
ack = yield TxAckInput, 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)
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
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
ack = yield TxAckOutput, tx_req
_clear_tx_request(tx_req)

View File

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