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,84 @@ 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
+ @staticmethod
65
+ def verify_parents_basic (tx : Transaction ) -> None :
66
+ """Verify number and non-duplicity of parents."""
67
+ tx .verify_parents_basic ()
68
+
69
+ def verify_weight (self , tx : Transaction ) -> None :
70
+ """Validate minimum tx difficulty."""
71
+ tx .verify_weight ()
72
+
73
+ def verify_without_storage (self , tx : Transaction ) -> None :
74
+ """ Run all verifications that do not need a storage.
75
+ """
50
76
tx .verify_without_storage ()
77
+
78
+ def verify_sigops_input (self , tx : Transaction ) -> None :
79
+ """ Count sig operations on all inputs and verify that the total sum is below the limit
80
+ """
51
81
tx .verify_sigops_input ()
52
- tx .verify_inputs () # need to run verify_inputs first to check if all inputs exist
53
- tx .verify_parents ()
82
+
83
+ def verify_inputs (self , tx : Transaction , * , skip_script : bool = False ) -> None :
84
+ """Verify inputs signatures and ownership and all inputs actually exist"""
85
+ tx .verify_inputs (skip_script = skip_script )
86
+
87
+ @staticmethod
88
+ def verify_script (* , tx : Transaction , input_tx : TxInput , spent_tx : BaseTransaction ) -> None :
89
+ """
90
+ :type tx: Transaction
91
+ :type input_tx: TxInput
92
+ :type spent_tx: Transaction
93
+ """
94
+ tx .verify_script (input_tx , spent_tx )
95
+
96
+ def verify_sum (self , tx : Transaction ) -> None :
97
+ """Verify that the sum of outputs is equal of the sum of inputs, for each token.
98
+
99
+ If there are authority UTXOs involved, tokens can be minted or melted, so the above rule may
100
+ not be respected.
101
+
102
+ :raises InvalidToken: when there's an error in token operations
103
+ :raises InputOutputMismatch: if sum of inputs is not equal to outputs and there's no mint/melt
104
+ """
54
105
tx .verify_sum ()
55
- if reject_locked_reward :
56
- tx .verify_reward_locked ()
106
+
107
+ @staticmethod
108
+ def verify_reward_locked (tx : Transaction ) -> None :
109
+ """Will raise `RewardLocked` if any reward is spent before the best block height is enough, considering only
110
+ the block rewards spent by this tx itself, and not the inherited `min_height`."""
111
+ tx .verify_reward_locked ()
112
+
113
+ def verify_number_of_inputs (self , tx : Transaction ) -> None :
114
+ """Verify number of inputs is in a valid range"""
115
+ tx .verify_number_of_inputs ()
116
+
117
+ def verify_outputs (self , tx : BaseTransaction ) -> None :
118
+ """Verify outputs reference an existing token uid in the tokens list
119
+
120
+ :raises InvalidToken: output references non existent token uid
121
+ """
122
+ tx .verify_outputs ()
123
+
124
+ @staticmethod
125
+ def update_token_info_from_outputs (tx : Transaction , * , token_dict : dict [TokenUid , TokenInfo ]) -> None :
126
+ """Iterate over the outputs and add values to token info dict. Updates the dict in-place.
127
+
128
+ Also, checks if no token has authorities on the outputs not present on the inputs
129
+
130
+ :raises InvalidToken: when there's an error in token operations
131
+ """
132
+ tx .update_token_info_from_outputs (token_dict )
0 commit comments