13
13
# limitations under the License.
14
14
15
15
from hathor .profiler import get_cpu_profiler
16
- from hathor .transaction import Transaction
16
+ from hathor .transaction import BaseTransaction , Transaction , TxInput
17
+ from hathor .transaction .transaction import TokenInfo
18
+ from hathor .types import TokenUid
17
19
from hathor .verification .vertex_verifier import VertexVerifier
18
20
19
21
cpu = get_cpu_profiler ()
@@ -27,9 +29,9 @@ def verify_basic(self, tx: Transaction) -> None:
27
29
if tx .is_genesis :
28
30
# TODO do genesis validation?
29
31
return
30
- tx .verify_parents_basic ()
31
- tx .verify_weight ()
32
- tx .verify_without_storage ()
32
+ self .verify_parents_basic (tx )
33
+ self .verify_weight (tx )
34
+ self .verify_without_storage (tx )
33
35
34
36
@cpu .profiler (key = lambda _ , tx : 'tx-verify!{}' .format (tx .hash .hex ()))
35
37
def verify (self , tx : Transaction , * , reject_locked_reward : bool = True ) -> None :
@@ -47,10 +49,80 @@ def verify(self, tx: Transaction, *, reject_locked_reward: bool = True) -> None:
47
49
if tx .is_genesis :
48
50
# TODO do genesis validation
49
51
return
52
+ self .verify_without_storage (tx )
53
+ self .verify_sigops_input (tx )
54
+ self .verify_inputs (tx ) # need to run verify_inputs first to check if all inputs exist
55
+ self .verify_parents (tx )
56
+ self .verify_sum (tx )
57
+ if reject_locked_reward :
58
+ self .verify_reward_locked (tx )
59
+
60
+ def verify_unsigned_skip_pow (self , tx : Transaction ) -> None :
61
+ """ Same as .verify but skipping pow and signature verification."""
62
+ tx .verify_unsigned_skip_pow ()
63
+
64
+ def verify_parents_basic (self , tx : Transaction ) -> None :
65
+ """Verify number and non-duplicity of parents."""
66
+ tx .verify_parents_basic ()
67
+
68
+ def verify_weight (self , tx : Transaction ) -> None :
69
+ """Validate minimum tx difficulty."""
70
+ tx .verify_weight ()
71
+
72
+ def verify_without_storage (self , tx : Transaction ) -> None :
73
+ """ Run all verifications that do not need a storage.
74
+ """
50
75
tx .verify_without_storage ()
76
+
77
+ def verify_sigops_input (self , tx : Transaction ) -> None :
78
+ """ Count sig operations on all inputs and verify that the total sum is below the limit
79
+ """
51
80
tx .verify_sigops_input ()
52
- tx .verify_inputs () # need to run verify_inputs first to check if all inputs exist
53
- tx .verify_parents ()
81
+
82
+ def verify_inputs (self , tx : Transaction , * , skip_script : bool = False ) -> None :
83
+ """Verify inputs signatures and ownership and all inputs actually exist"""
84
+ tx .verify_inputs (skip_script = skip_script )
85
+
86
+ def verify_script (self , * , tx : Transaction , input_tx : TxInput , spent_tx : BaseTransaction ) -> None :
87
+ """
88
+ :type tx: Transaction
89
+ :type input_tx: TxInput
90
+ :type spent_tx: Transaction
91
+ """
92
+ tx .verify_script (input_tx , spent_tx )
93
+
94
+ def verify_sum (self , tx : Transaction ) -> None :
95
+ """Verify that the sum of outputs is equal of the sum of inputs, for each token.
96
+
97
+ If there are authority UTXOs involved, tokens can be minted or melted, so the above rule may
98
+ not be respected.
99
+
100
+ :raises InvalidToken: when there's an error in token operations
101
+ :raises InputOutputMismatch: if sum of inputs is not equal to outputs and there's no mint/melt
102
+ """
54
103
tx .verify_sum ()
55
- if reject_locked_reward :
56
- tx .verify_reward_locked ()
104
+
105
+ def verify_reward_locked (self , tx : Transaction ) -> None :
106
+ """Will raise `RewardLocked` if any reward is spent before the best block height is enough, considering only
107
+ the block rewards spent by this tx itself, and not the inherited `min_height`."""
108
+ tx .verify_reward_locked ()
109
+
110
+ def verify_number_of_inputs (self , tx : Transaction ) -> None :
111
+ """Verify number of inputs is in a valid range"""
112
+ tx .verify_number_of_inputs ()
113
+
114
+ def verify_outputs (self , tx : BaseTransaction ) -> None :
115
+ """Verify outputs reference an existing token uid in the tokens list
116
+
117
+ :raises InvalidToken: output references non existent token uid
118
+ """
119
+ tx .verify_outputs ()
120
+
121
+ def update_token_info_from_outputs (self , tx : Transaction , * , token_dict : dict [TokenUid , TokenInfo ]) -> None :
122
+ """Iterate over the outputs and add values to token info dict. Updates the dict in-place.
123
+
124
+ Also, checks if no token has authorities on the outputs not present on the inputs
125
+
126
+ :raises InvalidToken: when there's an error in token operations
127
+ """
128
+ tx .update_token_info_from_outputs (token_dict )
0 commit comments