core/sign_tx: Refactor BIP-143 signing.

pull/985/head
Andrew Kozlik 4 years ago committed by Andrew Kozlik
parent d58cd3987b
commit 6ad3baeab2

@ -28,7 +28,7 @@ from apps.wallet.sign_tx.common import SigningError, ecdsa_sign
from apps.wallet.sign_tx.matchcheck import MultisigFingerprintChecker, WalletPathChecker from apps.wallet.sign_tx.matchcheck import MultisigFingerprintChecker, WalletPathChecker
if False: if False:
from typing import Set, Union from typing import Set, Tuple, Union
# Default signature hash type in Bitcoin, which signs the entire transaction except scripts. # Default signature hash type in Bitcoin, which signs the entire transaction except scripts.
_SIGHASH_ALL = const(0x01) _SIGHASH_ALL = const(0x01)
@ -208,16 +208,19 @@ class Bitcoin:
raise SigningError(FailureType.DataError, "Wrong input script type") raise SigningError(FailureType.DataError, "Wrong input script type")
async def process_segwit_input(self, i: int, txi: TxInputType) -> None: async def process_segwit_input(self, i: int, txi: TxInputType) -> None:
if not txi.amount: await self.process_bip143_input(i, txi)
raise SigningError(FailureType.DataError, "Segwit input without amount")
self.bip143_in += txi.amount
self.total_in += txi.amount
async def process_nonsegwit_input(self, i: int, txi: TxInputType) -> None: async def process_nonsegwit_input(self, i: int, txi: TxInputType) -> None:
self.total_in += await self.get_prevtx_output_value( self.total_in += await self.get_prevtx_output_value(
txi.prev_hash, txi.prev_index txi.prev_hash, txi.prev_index
) )
async def process_bip143_input(self, i: int, txi: TxInputType) -> None:
if not txi.amount:
raise SigningError(FailureType.DataError, "Expected input with amount")
self.bip143_in += txi.amount
self.total_in += txi.amount
async def confirm_output( async def confirm_output(
self, i: int, txo: TxOutputType, txo_bin: TxOutputBinType self, i: int, txo: TxOutputType, txo_bin: TxOutputBinType
) -> None: ) -> None:
@ -252,34 +255,45 @@ class Bitcoin:
self.write_tx_input(self.serialized_tx, txi) self.write_tx_input(self.serialized_tx, txi)
async def sign_segwit_input(self, i: int) -> None: def sign_bip143_input(self, txi: TxInputType) -> Tuple[bytes, bytes]:
# STAGE_REQUEST_SEGWIT_WITNESS
txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
self.wallet_path.check_input(txi) self.wallet_path.check_input(txi)
self.multisig_fingerprint.check_input(txi) self.multisig_fingerprint.check_input(txi)
if not input_is_segwit(txi) or txi.amount > self.bip143_in: if txi.amount > self.bip143_in:
raise SigningError( raise SigningError(
FailureType.ProcessError, "Transaction has changed during signing" FailureType.ProcessError, "Transaction has changed during signing"
) )
self.bip143_in -= txi.amount self.bip143_in -= txi.amount
node = self.keychain.derive(txi.address_n, self.coin.curve_name) node = self.keychain.derive(txi.address_n, self.coin.curve_name)
key_sign_pub = node.public_key() public_key = node.public_key()
hash143_hash = self.hash143.preimage_hash( hash143_hash = self.hash143.preimage_hash(
self.coin, self.coin,
self.tx, self.tx,
txi, txi,
addresses.ecdsa_hash_pubkey(key_sign_pub, self.coin), addresses.ecdsa_hash_pubkey(public_key, self.coin),
self.get_hash_type(), self.get_hash_type(),
) )
signature = ecdsa_sign(node, hash143_hash) signature = ecdsa_sign(node, hash143_hash)
return public_key, signature
async def sign_segwit_input(self, i: int) -> None:
# STAGE_REQUEST_SEGWIT_WITNESS
txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
if not input_is_segwit(txi):
raise SigningError(
FailureType.ProcessError, "Transaction has changed during signing"
)
public_key, signature = self.sign_bip143_input(txi)
self.set_serialized_signature(i, signature) self.set_serialized_signature(i, signature)
if txi.multisig: if txi.multisig:
# find out place of our signature based on the pubkey # find out place of our signature based on the pubkey
signature_index = multisig.multisig_pubkey_index(txi.multisig, key_sign_pub) signature_index = multisig.multisig_pubkey_index(txi.multisig, public_key)
self.serialized_tx.extend( self.serialized_tx.extend(
scripts.witness_p2wsh( scripts.witness_p2wsh(
txi.multisig, signature, signature_index, self.get_hash_type() txi.multisig, signature, signature_index, self.get_hash_type()
@ -287,7 +301,7 @@ class Bitcoin:
) )
else: else:
self.serialized_tx.extend( self.serialized_tx.extend(
scripts.witness_p2wpkh(signature, key_sign_pub, self.get_hash_type()) scripts.witness_p2wpkh(signature, public_key, self.get_hash_type())
) )
async def sign_nonsegwit_input(self, i_sign: int) -> None: async def sign_nonsegwit_input(self, i_sign: int) -> None:

@ -1,14 +1,14 @@
import gc import gc
from micropython import const from micropython import const
from trezor.messages import FailureType, InputScriptType from trezor.messages import FailureType
from trezor.messages.SignTx import SignTx from trezor.messages.SignTx import SignTx
from trezor.messages.TransactionType import TransactionType from trezor.messages.TransactionType import TransactionType
from trezor.messages.TxInputType import TxInputType from trezor.messages.TxInputType import TxInputType
from apps.wallet.sign_tx import addresses, helpers, multisig, writers from apps.wallet.sign_tx import helpers, multisig, writers
from apps.wallet.sign_tx.bitcoin import Bitcoin from apps.wallet.sign_tx.bitcoin import Bitcoin, input_is_nonsegwit
from apps.wallet.sign_tx.common import SigningError, ecdsa_sign from apps.wallet.sign_tx.common import SigningError
if False: if False:
from typing import Union from typing import Union
@ -28,58 +28,31 @@ class Bitcoinlike(Bitcoin):
else: else:
await super().process_nonsegwit_input(i, txi) await super().process_nonsegwit_input(i, txi)
async def process_bip143_input(self, i: int, txi: TxInputType) -> None: async def sign_nonsegwit_bip143_input(self, i_sign: int) -> None:
if not txi.amount: txi = await helpers.request_tx_input(self.tx_req, i_sign, self.coin)
raise SigningError(FailureType.DataError, "Expected input with amount")
self.bip143_in += txi.amount
self.total_in += txi.amount
async def sign_nonsegwit_input(self, i_sign: int) -> None: if not input_is_nonsegwit(txi):
if self.coin.force_bip143:
await self.sign_bip143_input(i_sign)
else:
await super().sign_nonsegwit_input(i_sign)
async def sign_bip143_input(self, i_sign: int) -> None:
# STAGE_REQUEST_SEGWIT_INPUT
txi_sign = await helpers.request_tx_input(self.tx_req, i_sign, self.coin)
self.wallet_path.check_input(txi_sign)
self.multisig_fingerprint.check_input(txi_sign)
is_bip143 = (
txi_sign.script_type == InputScriptType.SPENDADDRESS
or txi_sign.script_type == InputScriptType.SPENDMULTISIG
)
if not is_bip143 or txi_sign.amount > self.bip143_in:
raise SigningError( raise SigningError(
FailureType.ProcessError, "Transaction has changed during signing" FailureType.ProcessError, "Transaction has changed during signing"
) )
self.bip143_in -= txi_sign.amount public_key, signature = self.sign_bip143_input(txi)
key_sign = self.keychain.derive(txi_sign.address_n, self.coin.curve_name)
key_sign_pub = key_sign.public_key()
hash143_hash = self.hash143.preimage_hash(
self.coin,
self.tx,
txi_sign,
addresses.ecdsa_hash_pubkey(key_sign_pub, self.coin),
self.get_hash_type(),
)
# if multisig, do a sanity check to ensure we are signing with a key that is included in the multisig # if multisig, do a sanity check to ensure we are signing with a key that is included in the multisig
if txi_sign.multisig: if txi.multisig:
multisig.multisig_pubkey_index(txi_sign.multisig, key_sign_pub) multisig.multisig_pubkey_index(txi.multisig, public_key)
signature = ecdsa_sign(key_sign, hash143_hash)
# serialize input with correct signature # serialize input with correct signature
gc.collect() gc.collect()
txi_sign.script_sig = self.input_derive_script( txi.script_sig = self.input_derive_script(txi, public_key, signature)
txi_sign, key_sign_pub, signature writers.write_tx_input(self.serialized_tx, txi)
)
writers.write_tx_input(self.serialized_tx, txi_sign)
self.set_serialized_signature(i_sign, signature) self.set_serialized_signature(i_sign, signature)
async def sign_nonsegwit_input(self, i_sign: int) -> None:
if self.coin.force_bip143:
await self.sign_nonsegwit_bip143_input(i_sign)
else:
await super().sign_nonsegwit_input(i_sign)
def on_negative_fee(self) -> None: def on_negative_fee(self) -> None:
# some coins require negative fees for reward TX # some coins require negative fees for reward TX
if not self.coin.negative_fee: if not self.coin.negative_fee:

@ -219,7 +219,7 @@ class Overwintered(Bitcoinlike):
await self.process_bip143_input(i, txi) await self.process_bip143_input(i, txi)
async def sign_nonsegwit_input(self, i_sign: int) -> None: async def sign_nonsegwit_input(self, i_sign: int) -> None:
await self.sign_bip143_input(i_sign) await self.sign_nonsegwit_bip143_input(i_sign)
def write_tx_header( def write_tx_header(
self, w: Writer, tx: Union[SignTx, TransactionType], has_segwit: bool self, w: Writer, tx: Union[SignTx, TransactionType], has_segwit: bool

Loading…
Cancel
Save