1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-18 05:28:40 +00:00

core/sign_tx: Reuse get_prevtx_output_value() in Decred.

This commit is contained in:
Andrew Kozlik 2020-04-08 20:55:18 +02:00 committed by Andrew Kozlik
parent 2b74513e49
commit 60dbec95ac
2 changed files with 54 additions and 60 deletions

View File

@ -4,11 +4,12 @@ from micropython import const
from trezor.crypto.hashlib import blake256 from trezor.crypto.hashlib import blake256
from trezor.messages import FailureType, InputScriptType from trezor.messages import FailureType, InputScriptType
from trezor.messages.SignTx import SignTx from trezor.messages.SignTx import SignTx
from trezor.messages.TransactionType import TransactionType
from trezor.messages.TxInputType import TxInputType from trezor.messages.TxInputType import TxInputType
from trezor.messages.TxOutputBinType import TxOutputBinType from trezor.messages.TxOutputBinType import TxOutputBinType
from trezor.messages.TxOutputType import TxOutputType from trezor.messages.TxOutputType import TxOutputType
from trezor.messages.TxRequestSerializedType import TxRequestSerializedType from trezor.messages.TxRequestSerializedType import TxRequestSerializedType
from trezor.utils import HashWriter from trezor.utils import HashWriter, ensure
from apps.common import coininfo, seed from apps.common import coininfo, seed
from apps.wallet.sign_tx import addresses, helpers, multisig, progress, scripts, writers from apps.wallet.sign_tx import addresses, helpers, multisig, progress, scripts, writers
@ -20,6 +21,9 @@ DECRED_SERIALIZE_WITNESS_SIGNING = const(3 << 16)
DECRED_SIGHASHALL = const(1) DECRED_SIGHASHALL = const(1)
if False:
from typing import Union
class DecredPrefixHasher: class DecredPrefixHasher:
""" """
@ -57,15 +61,19 @@ class Decred(Bitcoin):
def initialize( def initialize(
self, tx: SignTx, keychain: seed.Keychain, coin: coininfo.CoinInfo self, tx: SignTx, keychain: seed.Keychain, coin: coininfo.CoinInfo
) -> None: ) -> None:
ensure(coin.decred)
super().initialize(tx, keychain, coin) super().initialize(tx, keychain, coin)
# This is required because the last serialized output obtained in # This is required because the last serialized output obtained in
# `check_fee` will only be sent to the client in `sign_tx` # phase 1 will only be sent to the client in phase 2
self.last_output_bytes = None # type: bytearray self.last_output_bytes = None # type: bytearray
def init_hash143(self) -> None: def init_hash143(self) -> None:
self.hash143 = DecredPrefixHasher(self.tx) # pseudo BIP-0143 prefix hashing self.hash143 = DecredPrefixHasher(self.tx) # pseudo BIP-0143 prefix hashing
def create_hash_writer(self) -> HashWriter:
return HashWriter(blake256())
async def phase1(self) -> None: async def phase1(self) -> None:
await super().phase1() await super().phase1()
self.hash143.add_locktime_expiry(self.tx) self.hash143.add_locktime_expiry(self.tx)
@ -75,7 +83,7 @@ class Decred(Bitcoin):
w_txi = writers.empty_bytearray(8 if i == 0 else 0 + 9 + len(txi.prev_hash)) w_txi = writers.empty_bytearray(8 if i == 0 else 0 + 9 + len(txi.prev_hash))
if i == 0: # serializing first input => prepend headers if i == 0: # serializing first input => prepend headers
self.write_sign_tx_header(w_txi, False) self.write_sign_tx_header(w_txi, False)
writers.write_tx_input_decred(w_txi, txi) self.write_tx_input(w_txi, txi)
self.tx_req.serialized = TxRequestSerializedType(None, None, w_txi) self.tx_req.serialized = TxRequestSerializedType(None, None, w_txi)
async def phase1_confirm_output( async def phase1_confirm_output(
@ -127,7 +135,7 @@ class Decred(Bitcoin):
else: else:
raise SigningError("Unsupported input script type") raise SigningError("Unsupported input script type")
h_witness = HashWriter(blake256()) h_witness = self.create_hash_writer()
writers.write_uint32( writers.write_uint32(
h_witness, self.tx.version | DECRED_SERIALIZE_WITNESS_SIGNING h_witness, self.tx.version | DECRED_SERIALIZE_WITNESS_SIGNING
) )
@ -143,7 +151,7 @@ class Decred(Bitcoin):
h_witness, double=self.coin.sign_hash_double, reverse=False h_witness, double=self.coin.sign_hash_double, reverse=False
) )
h_sign = HashWriter(blake256()) h_sign = self.create_hash_writer()
writers.write_uint32(h_sign, DECRED_SIGHASHALL) writers.write_uint32(h_sign, DECRED_SIGHASHALL)
writers.write_bytes_fixed(h_sign, prefix_hash, writers.TX_HASH_SIZE) writers.write_bytes_fixed(h_sign, prefix_hash, writers.TX_HASH_SIZE)
writers.write_bytes_fixed(h_sign, witness_hash, writers.TX_HASH_SIZE) writers.write_bytes_fixed(h_sign, witness_hash, writers.TX_HASH_SIZE)
@ -175,36 +183,7 @@ class Decred(Bitcoin):
await helpers.request_tx_finish(self.tx_req) await helpers.request_tx_finish(self.tx_req)
async def get_prevtx_output_value(self, prev_hash: bytes, prev_index: int) -> int: def check_prevtx_output(self, txo_bin: TxOutputBinType) -> None:
total_out = 0 # sum of output amounts
# STAGE_REQUEST_2_PREV_META
tx = await helpers.request_tx_meta(self.tx_req, self.coin, prev_hash)
if tx.outputs_cnt <= prev_index:
raise SigningError(
FailureType.ProcessError, "Not enough outputs in previous transaction."
)
txh = HashWriter(blake256())
writers.write_uint32(txh, tx.version | DECRED_SERIALIZE_NO_WITNESS)
writers.write_varint(txh, tx.inputs_cnt)
for i in range(tx.inputs_cnt):
# STAGE_REQUEST_2_PREV_INPUT
txi = await helpers.request_tx_input(self.tx_req, i, self.coin, prev_hash)
writers.write_tx_input_decred(txh, txi)
writers.write_varint(txh, tx.outputs_cnt)
for o in range(tx.outputs_cnt):
# STAGE_REQUEST_2_PREV_OUTPUT
txo_bin = await helpers.request_tx_output(
self.tx_req, o, self.coin, prev_hash
)
writers.write_tx_output(txh, txo_bin)
if o == prev_index:
total_out += txo_bin.amount
if ( if (
txo_bin.decred_script_version is not None txo_bin.decred_script_version is not None
and txo_bin.decred_script_version != 0 and txo_bin.decred_script_version != 0
@ -214,15 +193,20 @@ class Decred(Bitcoin):
"Cannot use utxo that has script_version != 0", "Cannot use utxo that has script_version != 0",
) )
writers.write_uint32(txh, tx.lock_time) def write_tx_input(self, w: writers.Writer, i: TxInputType) -> None:
writers.write_uint32(txh, tx.expiry) writers.write_tx_input_decred(w, i)
if ( def write_sign_tx_header(self, w: writers.Writer, has_segwit: bool) -> None:
writers.get_tx_hash(txh, double=self.coin.sign_hash_double, reverse=True) writers.write_uint32(w, self.tx.version) # nVersion
!= prev_hash writers.write_varint(w, self.tx.inputs_count)
):
raise SigningError(
FailureType.ProcessError, "Encountered invalid prev_hash"
)
return total_out def write_tx_header(
self, w: writers.Writer, tx: Union[SignTx, TransactionType], has_segwit: bool
) -> None:
writers.write_uint32(w, tx.version | DECRED_SERIALIZE_NO_WITNESS)
async def write_prev_tx_footer(
self, w: writers.Writer, tx: TransactionType, prev_hash: bytes
) -> None:
writers.write_uint32(w, tx.lock_time)
writers.write_uint32(w, tx.expiry)

View File

@ -98,13 +98,16 @@ class Bitcoin:
# h_first is used to make sure the inputs and outputs streamed in Phase 1 # h_first is used to make sure the inputs and outputs streamed in Phase 1
# are the same as in Phase 2 when signing legacy inputs. it is thus not required to fully hash the # are the same as in Phase 2 when signing legacy inputs. it is thus not required to fully hash the
# tx, as the SignTx info is streamed only once # tx, as the SignTx info is streamed only once
self.h_first = utils.HashWriter(sha256()) # not a real tx hash self.h_first = self.create_hash_writer() # not a real tx hash
self.init_hash143() self.init_hash143()
def init_hash143(self) -> None: def init_hash143(self) -> None:
self.hash143 = segwit_bip143.Bip143() # BIP-0143 transaction hashing self.hash143 = segwit_bip143.Bip143() # BIP-0143 transaction hashing
def create_hash_writer(self) -> utils.HashWriter:
return utils.HashWriter(sha256())
async def phase1(self) -> None: async def phase1(self) -> None:
weight = tx_weight.TxWeightCalculator( weight = tx_weight.TxWeightCalculator(
self.tx.inputs_count, self.tx.outputs_count self.tx.inputs_count, self.tx.outputs_count
@ -267,7 +270,7 @@ class Bitcoin:
) )
if i_sign == 0: # serializing first input => prepend headers if i_sign == 0: # serializing first input => prepend headers
self.write_sign_tx_header(w_txi, True) self.write_sign_tx_header(w_txi, True)
writers.write_tx_input(w_txi, txi_sign) self.write_tx_input(w_txi, txi_sign)
self.tx_req.serialized = TxRequestSerializedType(serialized_tx=w_txi) self.tx_req.serialized = TxRequestSerializedType(serialized_tx=w_txi)
async def phase2_sign_segwit_input(self, i: int) -> Tuple[bytearray, bytes]: async def phase2_sign_segwit_input(self, i: int) -> Tuple[bytearray, bytes]:
@ -308,9 +311,9 @@ class Bitcoin:
async def phase2_sign_nonsegwit_input(self, i_sign: int) -> None: async def phase2_sign_nonsegwit_input(self, i_sign: int) -> None:
# hash of what we are signing with this input # hash of what we are signing with this input
h_sign = utils.HashWriter(sha256()) h_sign = self.create_hash_writer()
# same as h_first, checked before signing the digest # same as h_first, checked before signing the digest
h_second = utils.HashWriter(sha256()) h_second = self.create_hash_writer()
self.write_sign_tx_header(h_sign, has_segwit=False) self.write_sign_tx_header(h_sign, has_segwit=False)
@ -341,7 +344,7 @@ class Bitcoin:
) )
else: else:
txi.script_sig = bytes() txi.script_sig = bytes()
writers.write_tx_input(h_sign, txi) self.write_tx_input(h_sign, txi)
writers.write_varint(h_sign, self.tx.outputs_count) writers.write_varint(h_sign, self.tx.outputs_count)
@ -382,7 +385,7 @@ class Bitcoin:
) )
if i_sign == 0: # serializing first input => prepend headers if i_sign == 0: # serializing first input => prepend headers
self.write_sign_tx_header(w_txi_sign, True in self.segwit.values()) self.write_sign_tx_header(w_txi_sign, True in self.segwit.values())
writers.write_tx_input(w_txi_sign, txi_sign) self.write_tx_input(w_txi_sign, txi_sign)
self.tx_req.serialized = TxRequestSerializedType(i_sign, signature, w_txi_sign) self.tx_req.serialized = TxRequestSerializedType(i_sign, signature, w_txi_sign)
async def phase2_serialize_output(self, i: int) -> bytearray: async def phase2_serialize_output(self, i: int) -> bytearray:
@ -410,7 +413,7 @@ class Bitcoin:
FailureType.ProcessError, "Not enough outputs in previous transaction." FailureType.ProcessError, "Not enough outputs in previous transaction."
) )
txh = utils.HashWriter(sha256()) txh = self.create_hash_writer()
# TODO set has_segwit correctly # TODO set has_segwit correctly
self.write_tx_header(txh, tx, has_segwit=False) self.write_tx_header(txh, tx, has_segwit=False)
@ -419,7 +422,7 @@ class Bitcoin:
for i in range(tx.inputs_cnt): for i in range(tx.inputs_cnt):
# STAGE_REQUEST_2_PREV_INPUT # STAGE_REQUEST_2_PREV_INPUT
txi = await helpers.request_tx_input(self.tx_req, i, self.coin, prev_hash) txi = await helpers.request_tx_input(self.tx_req, i, self.coin, prev_hash)
writers.write_tx_input(txh, txi) self.write_tx_input(txh, txi)
writers.write_varint(txh, tx.outputs_cnt) writers.write_varint(txh, tx.outputs_cnt)
@ -431,6 +434,7 @@ class Bitcoin:
writers.write_tx_output(txh, txo_bin) writers.write_tx_output(txh, txo_bin)
if o == prev_index: if o == prev_index:
amount_out = txo_bin.amount amount_out = txo_bin.amount
self.check_prevtx_output(txo_bin)
await self.write_prev_tx_footer(txh, tx, prev_hash) await self.write_prev_tx_footer(txh, tx, prev_hash)
@ -444,6 +448,9 @@ class Bitcoin:
return amount_out return amount_out
def check_prevtx_output(self, txo_bin: TxOutputBinType) -> None:
pass
# TX Helpers # TX Helpers
# === # ===
@ -451,6 +458,9 @@ class Bitcoin:
SIGHASH_ALL = const(0x01) SIGHASH_ALL = const(0x01)
return SIGHASH_ALL return SIGHASH_ALL
def write_tx_input(self, w: writers.Writer, i: TxInputType) -> None:
writers.write_tx_input(w, i)
def write_sign_tx_header(self, w: writers.Writer, has_segwit: bool) -> None: def write_sign_tx_header(self, w: writers.Writer, has_segwit: bool) -> None:
self.write_tx_header(w, self.tx, has_segwit) self.write_tx_header(w, self.tx, has_segwit)
writers.write_varint(w, self.tx.inputs_count) writers.write_varint(w, self.tx.inputs_count)
@ -551,7 +561,7 @@ class Bitcoin:
if i.multisig: if i.multisig:
# p2wsh in p2sh # p2wsh in p2sh
pubkeys = multisig.multisig_get_pubkeys(i.multisig) pubkeys = multisig.multisig_get_pubkeys(i.multisig)
witness_script_hasher = utils.HashWriter(sha256()) witness_script_hasher = self.create_hash_writer()
scripts.write_output_script_multisig( scripts.write_output_script_multisig(
witness_script_hasher, pubkeys, i.multisig.m witness_script_hasher, pubkeys, i.multisig.m
) )