refactor(core): Rename Hash143 to SigHasher and replace preimage_hash() with two functions.

pull/1918/head
Andrew Kozlik 3 years ago committed by Andrew Kozlik
parent 85ff3682c9
commit ae35086bb9

@ -264,8 +264,6 @@ apps.bitcoin.sign_tx.bitcoinlike
import apps.bitcoin.sign_tx.bitcoinlike
apps.bitcoin.sign_tx.decred
import apps.bitcoin.sign_tx.decred
apps.bitcoin.sign_tx.hash143
import apps.bitcoin.sign_tx.hash143
apps.bitcoin.sign_tx.helpers
import apps.bitcoin.sign_tx.helpers
apps.bitcoin.sign_tx.layout
@ -276,6 +274,8 @@ apps.bitcoin.sign_tx.omni
import apps.bitcoin.sign_tx.omni
apps.bitcoin.sign_tx.progress
import apps.bitcoin.sign_tx.progress
apps.bitcoin.sign_tx.sig_hasher
import apps.bitcoin.sign_tx.sig_hasher
apps.bitcoin.sign_tx.tx_info
import apps.bitcoin.sign_tx.tx_info
apps.bitcoin.sign_tx.tx_weight

@ -20,7 +20,7 @@ from ..common import (
from ..ownership import verify_nonownership
from ..verification import SignatureVerifier
from . import approvers, helpers, progress
from .hash143 import Bip143Hash
from .sig_hasher import BitcoinSigHasher
from .tx_info import OriginalTxInfo, TxInfo
if False:
@ -40,7 +40,7 @@ if False:
from apps.common.coininfo import CoinInfo
from apps.common.keychain import Keychain
from .hash143 import Hash143
from .sig_hasher import SigHasher
# the number of bytes to preallocate for serialized transaction chunks
@ -50,13 +50,13 @@ _SERIALIZED_TX_BUFFER = empty_bytearray(_MAX_SERIALIZED_CHUNK_SIZE)
class Bitcoin:
async def signer(self) -> None:
# Add inputs to hash143 and h_tx_check and compute the sum of input amounts.
# Add inputs to sig_hasher and h_tx_check and compute the sum of input amounts.
await self.step1_process_inputs()
# Approve the original TXIDs in case of a replacement transaction.
await self.approver.approve_orig_txids(self.tx_info, self.orig_txs)
# Add outputs to hash143 and h_tx_check, approve outputs and compute
# Add outputs to sig_hasher and h_tx_check, approve outputs and compute
# sum of output amounts.
await self.step2_approve_outputs()
@ -131,8 +131,8 @@ class Bitcoin:
def create_hash_writer(self) -> HashWriter:
return HashWriter(sha256())
def create_hash143(self) -> Hash143:
return Bip143Hash()
def create_sig_hasher(self) -> SigHasher:
return BitcoinSigHasher()
async def step1_process_inputs(self) -> None:
h_external_inputs_check = HashWriter(sha256())
@ -443,15 +443,21 @@ class Bitcoin:
script_pubkey: bytes,
) -> bytes:
if txi.witness:
return tx_info.hash143.preimage_hash(
i,
txi,
public_keys,
threshold,
tx_info.tx,
self.coin,
self.get_sighash_type(txi),
)
if common.input_is_taproot(txi):
return tx_info.sig_hasher.hash341(
i,
tx_info.tx,
self.get_sighash_type(txi),
)
else:
return tx_info.sig_hasher.hash143(
txi,
public_keys,
threshold,
tx_info.tx,
self.coin,
self.get_sighash_type(txi),
)
else:
digest, _, _ = await self.get_legacy_tx_digest(i, tx_info, script_pubkey)
return digest
@ -523,8 +529,8 @@ class Bitcoin:
else:
public_keys = [public_key]
threshold = 1
hash143_hash = self.tx_info.hash143.preimage_hash(
0,
hash143_digest = self.tx_info.sig_hasher.hash143(
txi,
public_keys,
threshold,
@ -533,19 +539,15 @@ class Bitcoin:
self.get_sighash_type(txi),
)
signature = ecdsa_sign(node, hash143_hash)
signature = ecdsa_sign(node, hash143_digest)
return public_key, signature
def sign_taproot_input(self, i: int, txi: TxInput) -> bytes:
self.tx_info.check_input(txi)
sigmsg_digest = self.tx_info.hash143.preimage_hash(
sigmsg_digest = self.tx_info.sig_hasher.hash341(
i,
txi,
[],
1,
self.tx_info.tx,
self.coin,
self.get_sighash_type(txi),
)

@ -49,8 +49,7 @@ class Bitcoinlike(Bitcoin):
script_pubkey: bytes,
) -> bytes:
if self.coin.force_bip143:
return tx_info.hash143.preimage_hash(
i,
return tx_info.sig_hasher.hash143(
txi,
public_keys,
threshold,

@ -37,7 +37,7 @@ if False:
from apps.common.coininfo import CoinInfo
from apps.common.keychain import Keychain
from .hash143 import Hash143
from .sig_hasher import SigHasher
class DecredApprover(BasicApprover):
@ -50,7 +50,7 @@ class DecredApprover(BasicApprover):
await helpers.confirm_decred_sstx_submission(txo, self.coin, self.amount_unit)
class DecredHash:
class DecredSigHasher:
def __init__(self, h_prefix: HashWriter) -> None:
self.h_prefix = h_prefix
@ -60,9 +60,8 @@ class DecredHash:
def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
Decred.write_tx_output(self.h_prefix, txo, script_pubkey)
def preimage_hash(
def hash143(
self,
i: int,
txi: TxInput,
public_keys: Sequence[bytes | memoryview],
threshold: int,
@ -72,6 +71,14 @@ class DecredHash:
) -> bytes:
raise NotImplementedError
def hash341(
self,
i: int,
tx: SignTx | PrevTx,
sighash_type: int,
) -> bytes:
raise NotImplementedError
class Decred(Bitcoin):
def __init__(
@ -99,8 +106,8 @@ class Decred(Bitcoin):
def create_hash_writer(self) -> HashWriter:
return HashWriter(blake256())
def create_hash143(self) -> Hash143:
return DecredHash(self.h_prefix)
def create_sig_hasher(self) -> SigHasher:
return DecredSigHasher(self.h_prefix)
async def step2_approve_outputs(self) -> None:
write_bitcoin_varint(self.serialized_tx, self.tx_info.tx.outputs_count)

@ -5,21 +5,20 @@ from trezor.utils import HashWriter
from apps.common import coininfo
from .. import scripts, writers
from ..common import input_is_taproot, tagged_hashwriter
from ..common import tagged_hashwriter
if False:
from typing import Protocol, Sequence
class Hash143(Protocol):
class SigHasher(Protocol):
def add_input(self, txi: TxInput, script_pubkey: bytes) -> None:
...
def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
...
def preimage_hash(
def hash143(
self,
i: int,
txi: TxInput,
public_keys: Sequence[bytes | memoryview],
threshold: int,
@ -29,9 +28,17 @@ if False:
) -> bytes:
...
def hash341(
self,
i: int,
tx: SignTx | PrevTx,
sighash_type: int,
) -> bytes:
...
# BIP-0143 hash
class Bip143Hash:
class BitcoinSigHasher:
def __init__(self) -> None:
self.h_prevouts = HashWriter(sha256())
self.h_amounts = HashWriter(sha256())
@ -51,22 +58,7 @@ class Bip143Hash:
def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
writers.write_tx_output(self.h_outputs, txo, script_pubkey)
def preimage_hash(
self,
i: int,
txi: TxInput,
public_keys: Sequence[bytes | memoryview],
threshold: int,
tx: SignTx | PrevTx,
coin: coininfo.CoinInfo,
sighash_type: int,
) -> bytes:
if input_is_taproot(txi):
return self.bip341_hash(i, tx, sighash_type)
else:
return self.bip143_hash(txi, public_keys, threshold, tx, coin, sighash_type)
def bip143_hash(
def hash143(
self,
txi: TxInput,
public_keys: Sequence[bytes | memoryview],
@ -119,7 +111,7 @@ class Bip143Hash:
return writers.get_tx_hash(h_preimage, double=coin.sign_hash_double)
def bip341_hash(
def hash341(
self,
i: int,
tx: SignTx | PrevTx,

@ -16,7 +16,7 @@ if False:
TxInput,
TxOutput,
)
from .hash143 import Hash143
from .sig_hasher import SigHasher
from apps.common.coininfo import CoinInfo
@ -26,7 +26,7 @@ if False:
def create_hash_writer(self) -> HashWriter:
...
def create_hash143(self) -> Hash143:
def create_sig_hasher(self) -> SigHasher:
...
def write_tx_header(
@ -72,15 +72,14 @@ class TxInfoBase:
self.h_tx_check = HashWriter(sha256()) # not a real tx hash
# BIP-0143 transaction hashing.
self.hash143 = signer.create_hash143()
self.sig_hasher = signer.create_sig_hasher()
# The minimum nSequence of all inputs.
self.min_sequence = _SEQUENCE_FINAL
def add_input(self, txi: TxInput, script_pubkey: bytes) -> None:
self.hash143.add_input(
txi, script_pubkey
) # all inputs are included (non-segwit as well)
# all inputs are included (non-segwit as well)
self.sig_hasher.add_input(txi, script_pubkey)
writers.write_tx_input_check(self.h_tx_check, txi)
self.min_sequence = min(self.min_sequence, txi.sequence)
@ -89,7 +88,7 @@ class TxInfoBase:
self.multisig_fingerprint.add_input(txi)
def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
self.hash143.add_output(txo, script_pubkey)
self.sig_hasher.add_output(txo, script_pubkey)
writers.write_tx_output(self.h_tx_check, txo, script_pubkey)
def check_input(self, txi: TxInput) -> None:

@ -26,14 +26,14 @@ from .bitcoinlike import Bitcoinlike
if False:
from typing import Sequence
from apps.common import coininfo
from .hash143 import Hash143
from .sig_hasher import SigHasher
from .tx_info import OriginalTxInfo, TxInfo
from ..writers import Writer
OVERWINTERED = const(0x8000_0000)
class Zip243Hash:
class ZcashSigHasher:
def __init__(self) -> None:
self.h_prevouts = HashWriter(blake2b(outlen=32, personal=b"ZcashPrevoutHash"))
self.h_sequence = HashWriter(blake2b(outlen=32, personal=b"ZcashSequencHash"))
@ -47,9 +47,8 @@ class Zip243Hash:
def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
write_tx_output(self.h_outputs, txo, script_pubkey)
def preimage_hash(
def hash143(
self,
i: int,
txi: TxInput,
public_keys: Sequence[bytes | memoryview],
threshold: int,
@ -104,6 +103,14 @@ class Zip243Hash:
return get_tx_hash(h_preimage)
def hash341(
self,
i: int,
tx: SignTx | PrevTx,
sighash_type: int,
) -> bytes:
raise NotImplementedError
class Zcashlike(Bitcoinlike):
def __init__(
@ -119,8 +126,8 @@ class Zcashlike(Bitcoinlike):
if tx.version != 4:
raise wire.DataError("Unsupported transaction version.")
def create_hash143(self) -> Hash143:
return Zip243Hash()
def create_sig_hasher(self) -> SigHasher:
return ZcashSigHasher()
async def step7_finish(self) -> None:
self.write_tx_footer(self.serialized_tx, self.tx_info.tx)
@ -145,8 +152,7 @@ class Zcashlike(Bitcoinlike):
script_pubkey: bytes,
tx_hash: bytes | None = None,
) -> bytes:
return tx_info.hash143.preimage_hash(
0,
return tx_info.sig_hasher.hash143(
txi,
public_keys,
threshold,

@ -2,7 +2,7 @@ from common import *
from apps.bitcoin.common import SIGHASH_ALL
from apps.bitcoin.scripts import output_derive_script
from apps.bitcoin.sign_tx.bitcoin import Bip143Hash
from apps.bitcoin.sign_tx.bitcoin import BitcoinSigHasher
from apps.bitcoin.writers import get_tx_hash
from apps.common import coins
from apps.common.keychain import Keychain
@ -49,53 +49,53 @@ class TestSegwitBip143NativeP2WPKH(unittest.TestCase):
def test_prevouts(self):
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
bip143.add_input(self.inp1, b"")
bip143.add_input(self.inp2, b"")
prevouts_hash = get_tx_hash(bip143.h_prevouts, double=coin.sign_hash_double)
sig_hasher = BitcoinSigHasher()
sig_hasher.add_input(self.inp1, b"")
sig_hasher.add_input(self.inp2, b"")
prevouts_hash = get_tx_hash(sig_hasher.h_prevouts, double=coin.sign_hash_double)
self.assertEqual(hexlify(prevouts_hash), b'96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37')
def test_sequence(self):
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
bip143.add_input(self.inp1, b"")
bip143.add_input(self.inp2, b"")
sequence_hash = get_tx_hash(bip143.h_sequences, double=coin.sign_hash_double)
sig_hasher = BitcoinSigHasher()
sig_hasher.add_input(self.inp1, b"")
sig_hasher.add_input(self.inp2, b"")
sequence_hash = get_tx_hash(sig_hasher.h_sequences, double=coin.sign_hash_double)
self.assertEqual(hexlify(sequence_hash), b'52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b')
def test_outputs(self):
seed = bip39.seed('alcohol woman abuse must during monitor noble actual mixed trade anger aisle', '')
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
sig_hasher = BitcoinSigHasher()
for txo in [self.out1, self.out2]:
script_pubkey = output_derive_script(txo.address, coin)
txo_bin = PrevOutput(amount=txo.amount, script_pubkey=script_pubkey)
bip143.add_output(txo_bin, script_pubkey)
sig_hasher.add_output(txo_bin, script_pubkey)
outputs_hash = get_tx_hash(bip143.h_outputs, double=coin.sign_hash_double)
outputs_hash = get_tx_hash(sig_hasher.h_outputs, double=coin.sign_hash_double)
self.assertEqual(hexlify(outputs_hash), b'863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5')
def test_preimage_testdata(self):
seed = bip39.seed('alcohol woman abuse must during monitor noble actual mixed trade anger aisle', '')
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
bip143.add_input(self.inp1, b"")
bip143.add_input(self.inp2, b"")
sig_hasher = BitcoinSigHasher()
sig_hasher.add_input(self.inp1, b"")
sig_hasher.add_input(self.inp2, b"")
for txo in [self.out1, self.out2]:
script_pubkey = output_derive_script(txo.address, coin)
txo_bin = PrevOutput(amount=txo.amount, script_pubkey=script_pubkey)
bip143.add_output(txo_bin, script_pubkey)
sig_hasher.add_output(txo_bin, script_pubkey)
keychain = Keychain(seed, coin.curve_name, [AlwaysMatchingSchema])
node = keychain.derive(self.inp2.address_n)
# test data public key hash
# only for input 2 - input 1 is not segwit
result = bip143.preimage_hash(1, self.inp2, [node.public_key()], 1, self.tx, coin, SIGHASH_ALL)
result = sig_hasher.hash143(self.inp2, [node.public_key()], 1, self.tx, coin, SIGHASH_ALL)
self.assertEqual(hexlify(result), b'2fa3f1351618b2532228d7182d3221d95c21fd3d496e7e22e9ded873cf022a8b')

@ -2,7 +2,7 @@ from common import *
from apps.bitcoin.common import SIGHASH_ALL
from apps.bitcoin.scripts import output_derive_script
from apps.bitcoin.sign_tx.bitcoin import Bip143Hash
from apps.bitcoin.sign_tx.bitcoin import BitcoinSigHasher
from apps.bitcoin.writers import get_tx_hash
from apps.common import coins
from apps.common.keychain import Keychain
@ -41,46 +41,46 @@ class TestSegwitBip143(unittest.TestCase):
def test_bip143_prevouts(self):
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
bip143.add_input(self.inp1, b"")
prevouts_hash = get_tx_hash(bip143.h_prevouts, double=coin.sign_hash_double)
sig_hasher = BitcoinSigHasher()
sig_hasher.add_input(self.inp1, b"")
prevouts_hash = get_tx_hash(sig_hasher.h_prevouts, double=coin.sign_hash_double)
self.assertEqual(hexlify(prevouts_hash), b'b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a')
def test_bip143_sequence(self):
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
bip143.add_input(self.inp1, b"")
sequence_hash = get_tx_hash(bip143.h_sequences, double=coin.sign_hash_double)
sig_hasher = BitcoinSigHasher()
sig_hasher.add_input(self.inp1, b"")
sequence_hash = get_tx_hash(sig_hasher.h_sequences, double=coin.sign_hash_double)
self.assertEqual(hexlify(sequence_hash), b'18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198')
def test_bip143_outputs(self):
seed = bip39.seed('alcohol woman abuse must during monitor noble actual mixed trade anger aisle', '')
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
sig_hasher = BitcoinSigHasher()
for txo in [self.out1, self.out2]:
script_pubkey = output_derive_script(txo.address, coin)
txo_bin = PrevOutput(amount=txo.amount, script_pubkey=script_pubkey)
bip143.add_output(txo_bin, script_pubkey)
sig_hasher.add_output(txo_bin, script_pubkey)
outputs_hash = get_tx_hash(bip143.h_outputs, double=coin.sign_hash_double)
outputs_hash = get_tx_hash(sig_hasher.h_outputs, double=coin.sign_hash_double)
self.assertEqual(hexlify(outputs_hash), b'de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83')
def test_bip143_preimage_testdata(self):
seed = bip39.seed('alcohol woman abuse must during monitor noble actual mixed trade anger aisle', '')
coin = coins.by_name(self.tx.coin_name)
bip143 = Bip143Hash()
bip143.add_input(self.inp1, b"")
sig_hasher = BitcoinSigHasher()
sig_hasher.add_input(self.inp1, b"")
for txo in [self.out1, self.out2]:
script_pubkey = output_derive_script(txo.address, coin)
txo_bin = PrevOutput(amount=txo.amount, script_pubkey=script_pubkey)
bip143.add_output(txo_bin, script_pubkey)
sig_hasher.add_output(txo_bin, script_pubkey)
keychain = Keychain(seed, coin.curve_name, [AlwaysMatchingSchema])
node = keychain.derive(self.inp1.address_n)
# test data public key hash
result = bip143.preimage_hash(0, self.inp1, [node.public_key()], 1, self.tx, coin, SIGHASH_ALL)
result = sig_hasher.hash143(self.inp1, [node.public_key()], 1, self.tx, coin, SIGHASH_ALL)
self.assertEqual(hexlify(result), b'6e28aca7041720995d4acf59bbda64eef5d6f23723d23f2e994757546674bbd9')

@ -2,7 +2,7 @@ from common import *
from apps.bitcoin.common import SIGHASH_ALL, SIGHASH_ALL_TAPROOT
from apps.bitcoin.scripts import output_derive_script
from apps.bitcoin.sign_tx.bitcoin import Bip143Hash
from apps.bitcoin.sign_tx.bitcoin import BitcoinSigHasher
from apps.bitcoin.writers import get_tx_hash
from trezor.messages import SignTx
from trezor.messages import TxInput
@ -168,7 +168,7 @@ class TestSegwitBip341P2TR(unittest.TestCase):
def test_bip341(self):
for i, tx in enumerate(VECTORS):
hasher = Bip143Hash()
hasher = BitcoinSigHasher()
for txi in tx["inputs"]:
hasher.add_input(txi, txi.script_pubkey)
@ -185,7 +185,7 @@ class TestSegwitBip341P2TR(unittest.TestCase):
for sh in tx["signature_hashes"]:
txi = tx["inputs"][sh["index"]]
result = hasher.preimage_hash(sh["index"], txi, [b""], 1, tx["sign_tx"], coin, sh["hash_type"])
result = hasher.hash341(sh["index"], tx["sign_tx"], sh["hash_type"])
self.assertEqual(result, sh["result"], f"signature_hash tx {i} input {sh['index']}")

@ -9,7 +9,7 @@ from apps.bitcoin.common import SIGHASH_ALL
from apps.bitcoin.writers import get_tx_hash
if not utils.BITCOIN_ONLY:
from apps.bitcoin.sign_tx.zcash import Zip243Hash
from apps.bitcoin.sign_tx.zcash import ZcashSigHasher
# test vectors inspired from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/zip_0243.py
@ -191,7 +191,7 @@ class TestZcashZip243(unittest.TestCase):
branch_id=v["branch_id"],
)
zip243 = Zip243Hash()
zip243 = ZcashSigHasher()
for i in v["inputs"]:
txi = TxInput(
@ -213,7 +213,7 @@ class TestZcashZip243(unittest.TestCase):
self.assertEqual(hexlify(get_tx_hash(zip243.h_prevouts)), v["prevouts_hash"])
self.assertEqual(hexlify(get_tx_hash(zip243.h_sequence)), v["sequence_hash"])
self.assertEqual(hexlify(get_tx_hash(zip243.h_outputs)), v["outputs_hash"])
self.assertEqual(hexlify(zip243.preimage_hash(0, txi, [unhexlify(i["pubkey"])], 1, tx, coin, SIGHASH_ALL)), v["preimage_hash"])
self.assertEqual(hexlify(zip243.hash143(txi, [unhexlify(i["pubkey"])], 1, tx, coin, SIGHASH_ALL)), v["preimage_hash"])
if __name__ == "__main__":

Loading…
Cancel
Save