1
- # Copyright 2023 Hathor Labs
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
1
# Copyright 2021 Hathor Labs
16
2
#
17
3
# Licensed under the Apache License, Version 2.0 (the "License");
28
14
29
15
import re
30
16
import struct
31
- from typing import Any , Generator , NamedTuple , Optional , Pattern , Union
17
+ from typing import TYPE_CHECKING , Any , Generator , NamedTuple , Optional , Pattern , Union
32
18
33
19
from hathor .conf .get_settings import get_settings
34
- from hathor .crypto .util import (
35
- decode_address ,
36
- )
20
+ from hathor .crypto .util import decode_address
37
21
from hathor .transaction import BaseTransaction , Transaction , TxInput
38
- from hathor .transaction .exceptions import (
39
- DataIndexError ,
40
- FinalStackInvalid ,
41
- InvalidScriptError ,
42
- OutOfData ,
43
- ScriptError ,
44
- )
22
+ from hathor .transaction .exceptions import DataIndexError , FinalStackInvalid , InvalidScriptError , OutOfData , ScriptError
45
23
from hathor .transaction .script .base_script import BaseScript
46
- from hathor .transaction .script .multi_sig import MultiSig
47
- from hathor .transaction .script .opcode import Opcode , MAP_OPCODE_TO_FN
48
- from hathor .transaction .script .p2pkh import P2PKH
24
+
25
+ if TYPE_CHECKING :
26
+ from hathor .transaction .script .multi_sig import MultiSig
27
+ from hathor .transaction .script .opcode import Opcode
28
+ from hathor .transaction .script .p2pkh import P2PKH
49
29
50
30
# XXX: Because the Stack is a heterogeneous list of bytes and int, and some OPs only work for when the stack has some
51
31
# or the other type, there are many places that require an assert to prevent the wrong type from being used,
@@ -90,6 +70,7 @@ def re_compile(pattern: str) -> Pattern[bytes]:
90
70
def _to_byte_pattern (m ):
91
71
x = m .group ().decode ('ascii' ).strip ()
92
72
if x .startswith ('OP_' ):
73
+ from hathor .transaction .script .opcode import Opcode
93
74
return bytes ([Opcode [x ]])
94
75
elif x .startswith ('DATA_' ):
95
76
length = int (m .group ()[5 :])
@@ -112,6 +93,7 @@ def _re_pushdata(length: int) -> bytes:
112
93
:return: A non-compiled regular expression
113
94
:rtype: bytes
114
95
"""
96
+ from hathor .transaction .script .opcode import Opcode
115
97
ret = [bytes ([Opcode .OP_PUSHDATA1 ]), bytes ([length ]), b'.{' , str (length ).encode ('ascii' ), b'}' ]
116
98
117
99
if length <= 75 :
@@ -127,8 +109,10 @@ def create_base_script(address: str, timelock: Optional[Any] = None) -> BaseScri
127
109
settings = get_settings ()
128
110
baddress = decode_address (address )
129
111
if baddress [0 ] == binary_to_int (settings .P2PKH_VERSION_BYTE ):
112
+ from hathor .transaction .script .p2pkh import P2PKH
130
113
return P2PKH (address , timelock )
131
114
elif baddress [0 ] == binary_to_int (settings .MULTISIG_VERSION_BYTE ):
115
+ from hathor .transaction .script .multi_sig import MultiSig
132
116
return MultiSig (address , timelock )
133
117
else :
134
118
raise ScriptError ('The address is not valid' )
@@ -150,14 +134,16 @@ def create_output_script(address: bytes, timelock: Optional[Any] = None) -> byte
150
134
settings = get_settings ()
151
135
# XXX: if the address class can somehow be simplified create_base_script could be used here
152
136
if address [0 ] == binary_to_int (settings .P2PKH_VERSION_BYTE ):
137
+ from hathor .transaction .script .p2pkh import P2PKH
153
138
return P2PKH .create_output_script (address , timelock )
154
139
elif address [0 ] == binary_to_int (settings .MULTISIG_VERSION_BYTE ):
140
+ from hathor .transaction .script .multi_sig import MultiSig
155
141
return MultiSig .create_output_script (address , timelock )
156
142
else :
157
143
raise ScriptError ('The address is not valid' )
158
144
159
145
160
- def parse_address_script (script : bytes ) -> Optional [Union [P2PKH , MultiSig ]]:
146
+ def parse_address_script (script : bytes ) -> Optional [Union [' P2PKH' , ' MultiSig' ]]:
161
147
""" Verifies if address is P2PKH or Multisig and calls correct parse_script method
162
148
163
149
:param script: script to decode
@@ -166,6 +152,8 @@ def parse_address_script(script: bytes) -> Optional[Union[P2PKH, MultiSig]]:
166
152
:return: P2PKH or MultiSig class or None
167
153
:rtype: class or None
168
154
"""
155
+ from hathor .transaction .script .multi_sig import MultiSig
156
+ from hathor .transaction .script .p2pkh import P2PKH
169
157
script_classes : list [type [Union [P2PKH , MultiSig ]]] = [P2PKH , MultiSig ]
170
158
# Each class verifies its script
171
159
for script_class in script_classes :
@@ -185,6 +173,7 @@ def decode_opn(opcode: int) -> int:
185
173
:return: int value for opcode param
186
174
:rtype: int
187
175
"""
176
+ from hathor .transaction .script .opcode import Opcode
188
177
int_val = opcode - Opcode .OP_0
189
178
if not (0 <= int_val <= 16 ):
190
179
raise InvalidScriptError ('unknown opcode {}' .format (opcode ))
@@ -258,6 +247,7 @@ def get_script_op(pos: int, data: bytes, stack: Optional[Stack] = None) -> Opcod
258
247
opcode = get_data_single_byte (pos , data )
259
248
260
249
# validate opcode
250
+ from hathor .transaction .script .opcode import Opcode
261
251
if not Opcode .is_valid_opcode (opcode ):
262
252
raise InvalidScriptError ('Invalid Opcode ({}) at position {} in {!r}' .format (opcode , pos , data ))
263
253
@@ -293,7 +283,7 @@ def get_script_op(pos: int, data: bytes, stack: Optional[Stack] = None) -> Opcod
293
283
294
284
295
285
class _ScriptOperation (NamedTuple ):
296
- opcode : Union [Opcode , int ]
286
+ opcode : Union [' Opcode' , int ]
297
287
position : int
298
288
data : Union [None , bytes , int , str ]
299
289
@@ -308,6 +298,7 @@ def parse_script_ops(data: bytes) -> Generator[_ScriptOperation, None, None]:
308
298
:return: generator for operations on script
309
299
:rtype: Generator[_ScriptOperation, None, None]
310
300
"""
301
+ from hathor .transaction .script .opcode import Opcode
311
302
op : Union [Opcode , int ]
312
303
313
304
pos = 0
@@ -341,6 +332,7 @@ def count_sigops(data: bytes) -> int:
341
332
:return: number of signature operations the script would do if it was executed
342
333
:rtype: int
343
334
"""
335
+ from hathor .transaction .script .opcode import Opcode
344
336
settings = get_settings ()
345
337
n_ops : int = 0
346
338
data_len : int = len (data )
@@ -388,6 +380,7 @@ def get_sigops_count(data: bytes, output_script: Optional[bytes] = None) -> int:
388
380
# If validating an input, should check the spent_tx for MultiSig
389
381
if output_script is not None :
390
382
# If it's multisig we have to validate the redeem_script sigop count
383
+ from hathor .transaction .script .multi_sig import MultiSig
391
384
if MultiSig .re_match .search (output_script ):
392
385
multisig_data = MultiSig .get_multisig_data (data )
393
386
# input_script + redeem_script
@@ -411,6 +404,7 @@ def execute_eval(data: bytes, log: list[str], extras: ScriptExtras) -> None:
411
404
:raises ScriptError: case opcode is not found
412
405
:raises FinalStackInvalid: case the evaluation fails
413
406
"""
407
+ from hathor .transaction .script .opcode import MAP_OPCODE_TO_FN , Opcode
414
408
stack : Stack = []
415
409
data_len = len (data )
416
410
pos = 0
@@ -466,6 +460,7 @@ def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction) -> No
466
460
log : list [str ] = []
467
461
extras = ScriptExtras (tx = tx , txin = txin , spent_tx = spent_tx )
468
462
463
+ from hathor .transaction .script .multi_sig import MultiSig
469
464
if MultiSig .re_match .search (output_script ):
470
465
# For MultiSig there are 2 executions:
471
466
# First we need to evaluate that redeem_script matches redeem_script_hash
@@ -551,5 +546,3 @@ def binary_to_int(binary: bytes) -> int:
551
546
552
547
(value ,) = struct .unpack (_format , binary )
553
548
return value
554
-
555
-
0 commit comments