You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
3.6 KiB
96 lines
3.6 KiB
4 years ago
|
from micropython import const
|
||
|
|
||
4 years ago
|
from trezor import wire
|
||
4 years ago
|
from trezor.messages.TxInputType import TxInputType
|
||
|
from trezor.messages.TxOutputType import TxOutputType
|
||
|
from trezor.utils import ensure
|
||
|
|
||
4 years ago
|
from .. import multisig
|
||
4 years ago
|
|
||
|
if False:
|
||
4 years ago
|
from typing import Any, Union
|
||
4 years ago
|
|
||
|
# the number of bip32 levels used in a wallet (chain and address)
|
||
|
_BIP32_WALLET_DEPTH = const(2)
|
||
|
|
||
|
|
||
|
class MatchChecker:
|
||
|
"""
|
||
4 years ago
|
MatchCheckers are used to identify the change-output in a transaction. An output is
|
||
|
a change-output if it has a certain matching attribute with all inputs.
|
||
|
1. When inputs are first processed, add_input() is called on each one to determine
|
||
|
if they all match.
|
||
|
2. Outputs are tested using output_matches() to tell whether they are admissible as
|
||
|
a change-output.
|
||
|
3. Before signing each input, check_input() is used to ensure that the attribute has
|
||
|
not changed.
|
||
|
|
||
|
There are two possible paths:
|
||
|
|
||
|
(a) If all inputs match on the attribute, the matching value is stored. Every output
|
||
|
that matches the stored value is admissible as a change-output.
|
||
|
|
||
|
(b) If some inputs do not match, a special value MISMATCH is stored. When the
|
||
|
matcher is in this state, _no outputs_ are admissible as change-outputs.
|
||
|
check_input() is a no-op in this case: if there is no matching attribute to
|
||
|
check against, we cannot detect modifications.
|
||
4 years ago
|
"""
|
||
|
|
||
|
MISMATCH = object()
|
||
|
UNDEFINED = object()
|
||
|
|
||
|
def __init__(self) -> None:
|
||
4 years ago
|
self.attribute = self.UNDEFINED # type: Any
|
||
4 years ago
|
self.read_only = False # Failsafe to ensure that add_input() is not accidentally called after output_matches().
|
||
|
|
||
4 years ago
|
def attribute_from_tx(self, txio: Union[TxInputType, TxOutputType]) -> Any:
|
||
4 years ago
|
# Return the attribute from the txio, which is to be used for matching.
|
||
|
# If the txio is invalid for matching, then return an object which
|
||
|
# evaluates as a boolean False.
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def add_input(self, txi: TxInputType) -> None:
|
||
|
ensure(not self.read_only)
|
||
|
|
||
|
if self.attribute is self.MISMATCH:
|
||
|
return # There was a mismatch in previous inputs.
|
||
|
|
||
|
added_attribute = self.attribute_from_tx(txi)
|
||
|
if not added_attribute:
|
||
|
self.attribute = self.MISMATCH # The added input is invalid for matching.
|
||
|
elif self.attribute is self.UNDEFINED:
|
||
|
self.attribute = added_attribute # This is the first input.
|
||
|
elif self.attribute != added_attribute:
|
||
|
self.attribute = self.MISMATCH
|
||
|
|
||
|
def check_input(self, txi: TxInputType) -> None:
|
||
|
if self.attribute is self.MISMATCH:
|
||
|
return # There was already a mismatch when adding inputs, ignore it now.
|
||
|
|
||
|
# All added inputs had a matching attribute, allowing a change-output.
|
||
|
# Ensure that this input still has the same attribute.
|
||
|
if self.attribute != self.attribute_from_tx(txi):
|
||
4 years ago
|
raise wire.ProcessError("Transaction has changed during signing")
|
||
4 years ago
|
|
||
|
def output_matches(self, txo: TxOutputType) -> bool:
|
||
|
self.read_only = True
|
||
|
|
||
|
if self.attribute is self.MISMATCH:
|
||
|
return False
|
||
|
|
||
|
return self.attribute_from_tx(txo) == self.attribute
|
||
|
|
||
|
|
||
|
class WalletPathChecker(MatchChecker):
|
||
4 years ago
|
def attribute_from_tx(self, txio: Union[TxInputType, TxOutputType]) -> Any:
|
||
4 years ago
|
if not txio.address_n:
|
||
|
return None
|
||
|
return txio.address_n[:-_BIP32_WALLET_DEPTH]
|
||
|
|
||
|
|
||
|
class MultisigFingerprintChecker(MatchChecker):
|
||
4 years ago
|
def attribute_from_tx(self, txio: Union[TxInputType, TxOutputType]) -> Any:
|
||
4 years ago
|
if not txio.multisig:
|
||
|
return None
|
||
|
return multisig.multisig_fingerprint(txio.multisig)
|