Skip to content

Commit 3c1f9a7

Browse files
committed
review changes
1 parent eb6c227 commit 3c1f9a7

File tree

9 files changed

+63
-44
lines changed

9 files changed

+63
-44
lines changed

hathor/transaction/scripts/execute.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import struct
16+
from dataclasses import dataclass
1617
from typing import TYPE_CHECKING, NamedTuple, Optional, Union
1718

1819
from hathor.conf.get_settings import get_global_settings
@@ -23,11 +24,21 @@
2324
from hathor.transaction.scripts.script_context import ScriptContext
2425

2526

26-
class ScriptExtras(NamedTuple):
27+
@dataclass(slots=True, frozen=True, kw_only=True)
28+
class ScriptExtras:
29+
"""
30+
A simple container for auxiliary data that may be used during execution of scripts.
31+
"""
2732
tx: Transaction
28-
txin: TxInput
29-
spent_tx: BaseTransaction
3033
input_index: int
34+
spent_tx: BaseTransaction
35+
36+
@property
37+
def txin(self) -> TxInput:
38+
return self.tx.inputs[self.input_index]
39+
40+
def __post_init__(self) -> None:
41+
assert self.txin.tx_id == self.spent_tx.hash
3142

3243

3344
# XXX: Because the Stack is a heterogeneous list of bytes and int, and some OPs only work for when the stack has some
@@ -96,7 +107,7 @@ def evaluate_final_stack(stack: Stack, log: list[str]) -> None:
96107
raise FinalStackInvalid('\n'.join(log))
97108

98109

99-
def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction, *, input_index: int) -> 'ScriptContext':
110+
def script_eval(tx: Transaction, spent_tx: BaseTransaction, *, input_index: int) -> 'ScriptContext':
100111
"""Evaluates the output script and input data according to
101112
a very limited subset of Bitcoin's scripting language.
102113
@@ -111,10 +122,10 @@ def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction, *, in
111122
112123
:raises ScriptError: if script verification fails
113124
"""
114-
input_data = txin.data
115-
output_script = spent_tx.outputs[txin.index].script
125+
extras = ScriptExtras(tx=tx, input_index=input_index, spent_tx=spent_tx)
126+
input_data = extras.txin.data
127+
output_script = spent_tx.outputs[extras.txin.index].script
116128
log: list[str] = []
117-
extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx, input_index=input_index)
118129

119130
from hathor.transaction.scripts import MultiSig
120131
if MultiSig.re_match.search(output_script):
@@ -123,7 +134,7 @@ def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction, *, in
123134
# we can't use input_data + output_script because it will end with an invalid stack
124135
# i.e. the signatures will still be on the stack after ouput_script is executed
125136
redeem_script_pos = MultiSig.get_multisig_redeem_script_pos(input_data)
126-
full_data = txin.data[redeem_script_pos:] + output_script
137+
full_data = extras.txin.data[redeem_script_pos:] + output_script
127138
execute_eval(full_data, log, extras)
128139

129140
# Second, we need to validate that the signatures on the input_data solves the redeem_script

hathor/transaction/scripts/opcode.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import struct
1717
from enum import IntEnum
1818

19+
import pydantic
1920
from cryptography.exceptions import InvalidSignature
2021
from cryptography.hazmat.primitives import hashes
2122
from cryptography.hazmat.primitives.asymmetric import ec
@@ -643,7 +644,7 @@ def op_sighash_bitmask(context: ScriptContext) -> None:
643644
inputs=bytes_to_int(inputs),
644645
outputs=bytes_to_int(outputs)
645646
)
646-
except Exception as e:
647+
except pydantic.ValidationError as e:
647648
raise CustomSighashModelInvalid('Could not construct sighash bitmask.') from e
648649

649650
if context.extras.input_index not in sighash.get_input_indexes():
@@ -669,7 +670,7 @@ def op_max_inputs_outputs(context: ScriptContext) -> None:
669670
max_inputs=bytes_to_int(max_inputs),
670671
max_outputs=bytes_to_int(max_outputs)
671672
)
672-
except Exception as e:
673+
except pydantic.ValidationError as e:
673674
raise InputsOutputsLimitModelInvalid("Could not construct inputs and outputs limits.") from e
674675

675676
tx_inputs_len = len(context.extras.tx.inputs)

hathor/transaction/token_creation_tx.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ def get_sighash_all(self) -> bytes:
147147
:return: Serialization of the inputs, outputs and tokens
148148
:rtype: bytes
149149
"""
150-
if self._sighash_cache:
151-
return self._sighash_cache
150+
if self._sighash_all_cache:
151+
return self._sighash_all_cache
152152

153153
struct_bytes = pack(
154154
_SIGHASH_ALL_FORMAT_STRING,
@@ -169,7 +169,7 @@ def get_sighash_all(self) -> bytes:
169169
struct_bytes += b''.join(tx_outputs)
170170

171171
struct_bytes += self.serialize_token_info()
172-
self._sighash_cache = struct_bytes
172+
self._sighash_all_cache = struct_bytes
173173

174174
return struct_bytes
175175

hathor/transaction/transaction.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,17 @@
2424
from hathor.checkpoint import Checkpoint
2525
from hathor.exception import InvalidNewTransaction
2626
from hathor.reward_lock import iter_spent_rewards
27-
from hathor.transaction.exceptions import InvalidScriptError
2827
from hathor.transaction import TxInput, TxOutput, TxVersion
2928
from hathor.transaction.base_transaction import TX_HASH_SIZE, GenericVertex
30-
from hathor.transaction.exceptions import InvalidToken
29+
from hathor.transaction.exceptions import InvalidScriptError, InvalidToken
3130
from hathor.transaction.static_metadata import TransactionStaticMetadata
3231
from hathor.transaction.util import VerboseCallback, unpack, unpack_len
3332
from hathor.types import TokenUid, VertexId
3433
from hathor.util import not_none
3534

3635
if TYPE_CHECKING:
37-
from hathor.transaction.scripts.sighash import CustomSighash
3836
from hathor.conf.settings import HathorSettings
37+
from hathor.transaction.scripts.sighash import CustomSighash
3938
from hathor.transaction.storage import TransactionStorage # noqa: F401
4039

4140
# Signal bits (B), version (B), token uids len (B) and inputs len (B), outputs len (B).
@@ -93,7 +92,7 @@ def __init__(
9392
settings=settings
9493
)
9594
self.tokens = tokens or []
96-
self._sighash_cache: Optional[bytes] = None
95+
self._sighash_all_cache: Optional[bytes] = None
9796
self._sighash_data_cache: Optional[bytes] = None
9897

9998
@property
@@ -233,11 +232,11 @@ def get_sighash_all(self) -> bytes:
233232
# This method does not depend on the input itself, however we call it for each one to sign it.
234233
# For transactions that have many inputs there is a significant decrease on the verify time
235234
# when using this cache, so we call this method only once.
236-
if self._sighash_cache:
237-
return self._sighash_cache
235+
if self._sighash_all_cache:
236+
return self._sighash_all_cache
238237

239238
sighash = self._get_sighash(inputs=self.inputs, outputs=self.outputs)
240-
self._sighash_cache = sighash
239+
self._sighash_all_cache = sighash
241240

242241
return sighash
243242

hathor/verification/transaction_verifier.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
from hathor.daa import DifficultyAdjustmentAlgorithm
1717
from hathor.profiler import get_cpu_profiler
1818
from hathor.reward_lock import get_spent_reward_locked_info
19-
from hathor.transaction import BaseTransaction, Transaction, TxInput
19+
from hathor.reward_lock.reward_lock import get_minimum_best_height
20+
from hathor.transaction import BaseTransaction, Transaction
2021
from hathor.transaction.exceptions import (
2122
ConflictingInputs,
2223
DuplicatedParents,
@@ -154,8 +155,7 @@ def verify_script(
154155
"""
155156
from hathor.transaction.scripts import script_eval
156157
try:
157-
input_tx = tx.inputs[input_index]
158-
return script_eval(tx, input_tx, spent_tx, input_index=input_index)
158+
return script_eval(tx, spent_tx, input_index=input_index)
159159
except ScriptError as e:
160160
raise InvalidInputData(e) from e
161161

tests/tx/test_multisig.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def test_spend_multisig(self):
134134
expected_dict = {'type': 'MultiSig', 'address': self.multisig_address_b58, 'timelock': None}
135135
self.assertEqual(cls_script.to_human_readable(), expected_dict)
136136

137-
script_eval(tx, tx_input, tx1, input_index=0)
137+
script_eval(tx, tx1, input_index=0)
138138

139139
# Script error
140140
with self.assertRaises(ScriptError):

tests/tx/test_nano_contracts.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ def test_match_values(self):
3636
input_data = NanoContractMatchValues.create_input_data(
3737
base64.b64decode(oracle_data), base64.b64decode(oracle_signature), base64.b64decode(pubkey))
3838
txin = TxInput(b'aa', 0, input_data)
39-
spent_tx = Transaction(outputs=[TxOutput(20, script)])
40-
tx = Transaction(outputs=[TxOutput(20, P2PKH.create_output_script(address))])
41-
script_eval(tx, txin, spent_tx, input_index=0)
39+
spent_tx = Transaction(hash=b'aa', outputs=[TxOutput(20, script)])
40+
tx = Transaction(inputs=[txin], outputs=[TxOutput(20, P2PKH.create_output_script(address))])
41+
script_eval(tx, spent_tx, input_index=0)

tests/tx/test_scripts.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def test_checksig(self):
253253
signature = self.genesis_private_key.sign(hashed_data, ec.ECDSA(hashes.SHA256()))
254254
pubkey_bytes = get_public_key_bytes_compressed(self.genesis_public_key)
255255

256-
extras = ScriptExtras(tx=tx, txin=Mock(), spent_tx=Mock(), input_index=0)
256+
extras = ScriptExtras(tx=tx, spent_tx=block, input_index=0)
257257

258258
# wrong signature puts False (0) on stack
259259
stack = [b'aaaaaaaaa', pubkey_bytes]
@@ -278,7 +278,7 @@ def test_checksig_cache(self):
278278
signature = self.genesis_private_key.sign(hashed_data, ec.ECDSA(hashes.SHA256()))
279279
pubkey_bytes = get_public_key_bytes_compressed(self.genesis_public_key)
280280

281-
extras = ScriptExtras(tx=tx, txin=Mock(), spent_tx=Mock(), input_index=0)
281+
extras = ScriptExtras(tx=tx, spent_tx=block, input_index=0)
282282

283283
stack = [signature, pubkey_bytes]
284284
self.assertIsNone(tx._sighash_data_cache)
@@ -508,34 +508,40 @@ def test_find_p2pkh(self):
508508
out_genesis = P2PKH.create_output_script(genesis_address)
509509

510510
from hathor.transaction import Transaction, TxInput, TxOutput
511-
spent_tx = Transaction(outputs=[TxOutput(1, b'nano_contract_code')])
512-
txin = TxInput(b'dont_care', 0, b'data')
511+
spent_tx = Transaction(hash=b'some_hash', outputs=[TxOutput(1, b'nano_contract_code')])
512+
txin = TxInput(b'some_hash', 0, b'data')
513513

514514
# try with just 1 output
515515
stack = [genesis_address]
516-
tx = Transaction(outputs=[TxOutput(1, out_genesis)])
517-
extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx, input_index=0)
516+
tx = Transaction(inputs=[txin], outputs=[TxOutput(1, out_genesis)])
517+
extras = ScriptExtras(tx=tx, spent_tx=spent_tx, input_index=0)
518518
op_find_p2pkh(ScriptContext(stack=stack, logs=[], extras=extras, settings=Mock()))
519519
self.assertEqual(stack.pop(), 1)
520520

521521
# several outputs and correct output among them
522522
stack = [genesis_address]
523-
tx = Transaction(outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(1, out_genesis), TxOutput(1, out3)])
524-
extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx, input_index=0)
523+
tx = Transaction(
524+
inputs=[txin],
525+
outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(1, out_genesis), TxOutput(1, out3)]
526+
)
527+
extras = ScriptExtras(tx=tx, spent_tx=spent_tx, input_index=0)
525528
op_find_p2pkh(ScriptContext(stack=stack, logs=[], extras=extras, settings=Mock()))
526529
self.assertEqual(stack.pop(), 1)
527530

528531
# several outputs without correct amount output
529532
stack = [genesis_address]
530-
tx = Transaction(outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(2, out_genesis), TxOutput(1, out3)])
531-
extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx, input_index=0)
533+
tx = Transaction(
534+
inputs=[txin],
535+
outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(2, out_genesis), TxOutput(1, out3)]
536+
)
537+
extras = ScriptExtras(tx=tx, spent_tx=spent_tx, input_index=0)
532538
with self.assertRaises(VerifyFailed):
533539
op_find_p2pkh(ScriptContext(stack=stack, logs=[], extras=extras, settings=Mock()))
534540

535541
# several outputs without correct address output
536542
stack = [genesis_address]
537-
tx = Transaction(outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(1, out3)])
538-
extras = ScriptExtras(tx=tx, txin=txin, spent_tx=spent_tx, input_index=0)
543+
tx = Transaction(inputs=[txin], outputs=[TxOutput(1, out1), TxOutput(1, out2), TxOutput(1, out3)])
544+
extras = ScriptExtras(tx=tx, spent_tx=spent_tx, input_index=0)
539545
with self.assertRaises(VerifyFailed):
540546
op_find_p2pkh(ScriptContext(stack=stack, logs=[], extras=extras, settings=Mock()))
541547

@@ -545,11 +551,13 @@ def test_greaterthan_timestamp(self):
545551

546552
timestamp = 1234567
547553

548-
from hathor.transaction import Transaction
549-
tx = Transaction()
554+
from hathor.transaction import Transaction, TxInput
555+
spent_tx = Transaction(hash=b'some_hash')
556+
tx_input = TxInput(tx_id=b'some_hash', index=0, data=b'')
557+
tx = Transaction(inputs=[tx_input])
550558

551559
stack = [struct.pack('!I', timestamp)]
552-
extras = ScriptExtras(tx=tx, txin=Mock(), spent_tx=Mock(), input_index=0)
560+
extras = ScriptExtras(tx=tx, spent_tx=spent_tx, input_index=0)
553561

554562
with self.assertRaises(TimeLocked):
555563
tx.timestamp = timestamp - 1
@@ -575,7 +583,7 @@ def test_checkmultisig(self):
575583
tx = Transaction(inputs=[txin], outputs=[txout])
576584

577585
data_to_sign = tx.get_sighash_all()
578-
extras = ScriptExtras(tx=tx, txin=Mock(), spent_tx=Mock(), input_index=0)
586+
extras = ScriptExtras(tx=tx, spent_tx=block, input_index=0)
579587

580588
wallet = HDWallet()
581589
wallet._manually_initialize()

tests/tx/test_tx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ def test_wallet_index(self):
10511051
self.assertEqual(len(self.tx_storage.indexes.addresses.get_from_address(output3_address_b58)), 1)
10521052
self.assertEqual(len(self.tx_storage.indexes.addresses.get_from_address(new_address_b58)), 1)
10531053

1054-
def test_sighash_cache(self):
1054+
def test_sighash_all_cache(self):
10551055
from unittest import mock
10561056

10571057
address = get_address_from_public_key(self.genesis_public_key)

0 commit comments

Comments
 (0)