• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CityOfZion / neo3-boa / 30ae3248-fada-4b8d-8735-31794b222593

17 Jan 2024 01:43PM UTC coverage: 92.103%. Remained the same
30ae3248-fada-4b8d-8735-31794b222593

push

circleci

web-flow
Merge pull request #1170 from CityOfZion/CU-86a1hepnw

CU-86a1hepnw - Fix tests using Python 3.12 (parsing manifest JSON)

1 of 1 new or added line in 1 file covered. (100.0%)

253 existing lines in 51 files now uncovered.

20773 of 22554 relevant lines covered (92.1%)

2.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.51
/boa3/internal/neo/vm/opcode/OpcodeHelper.py
1
from __future__ import annotations
3✔
2

3
from typing import Dict, Optional, Tuple, Union
3✔
4

5
from boa3.internal.constants import FOUR_BYTES_MAX_VALUE
3✔
6
from boa3.internal.neo.vm.opcode.Opcode import Opcode
3✔
7
from boa3.internal.neo.vm.type.Integer import Integer
3✔
8
from boa3.internal.neo.vm.type.String import String
3✔
9

10

11
def get_pushdata_and_data(bytestring: Union[str, bytes]) -> Tuple[Opcode, bytes]:
3✔
12
    """
13
    Gets the push opcode and data to the respective str ot bytes value
14

15
    :param bytestring: value that will be pushed
16
    :return: the respective opcode and its required data
17
    :rtype: Tuple[Opcode, bytes]
18
    """
19
    if isinstance(bytestring, str):
×
20
        bytestring = String(bytestring).to_bytes()
×
21

22
    bytes_size = Integer(len(bytestring)).to_byte_array(min_length=1)
×
23
    if len(bytes_size) == 1:
×
24
        opcode = Opcode.PUSHDATA1
×
25
    elif len(bytes_size) == 2:
×
26
        opcode = Opcode.PUSHDATA2
×
27
    else:
28
        if len(bytes_size) > 4:
×
29
            bytestring = bytestring[:FOUR_BYTES_MAX_VALUE]
×
30
        bytes_size = Integer(len(bytestring)).to_byte_array(min_length=4)
×
31
        opcode = Opcode.PUSHDATA4
×
32

33
    data = bytes_size + bytestring
×
34
    return opcode, data
×
35

36

37
def get_pushdata_and_data_from_size(data_size: int) -> Tuple[Opcode, bytes]:
3✔
UNCOV
38
    bytes_size = Integer(data_size).to_byte_array(min_length=1)
2✔
UNCOV
39
    if len(bytes_size) == 1:
2✔
UNCOV
40
        opcode = Opcode.PUSHDATA1
2✔
41
    elif len(bytes_size) == 2:
×
42
        opcode = Opcode.PUSHDATA2
×
43
    else:
44
        bytes_size = FOUR_BYTES_MAX_VALUE
×
45
        opcode = Opcode.PUSHDATA4
×
46

UNCOV
47
    return opcode, bytes_size
2✔
48

49

50
def get_literal_push(integer: int) -> Optional[Opcode]:
3✔
51
    """
52
    Gets the push opcode to the respective integer
53

54
    :param integer: value that will be pushed
55
    :return: the respective opcode
56
    :rtype: Opcode or None
57
    """
58
    if -1 <= integer <= 16:
3✔
59
        opcode_value: int = Integer.from_bytes(Opcode.PUSH0) + integer
3✔
60
        return Opcode(Integer(opcode_value).to_byte_array())
3✔
61
    else:
62
        return None
3✔
63

64

65
def get_push_and_data(integer: int) -> Tuple[Opcode, bytes]:
3✔
66
    """
67
    Gets the push opcode and data to the respective integer
68

69
    :param integer: value that will be pushed
70
    :return: the respective opcode and its required data
71
    :rtype: Tuple[Opcode, bytes]
72
    """
73
    opcode = get_literal_push(integer)
3✔
74
    if isinstance(opcode, Opcode):
3✔
75
        return opcode, b''
3✔
76
    else:
77
        data = Integer(integer).to_byte_array(signed=True, min_length=1)
3✔
78
        if len(data) == 1:
3✔
79
            opcode = Opcode.PUSHINT8
3✔
80
        elif len(data) == 2:
3✔
81
            opcode = Opcode.PUSHINT16
3✔
82
        elif len(data) <= 4:
3✔
83
            data = Integer(integer).to_byte_array(signed=True, min_length=4)
3✔
84
            opcode = Opcode.PUSHINT32
3✔
85
        elif len(data) <= 8:
3✔
86
            data = Integer(integer).to_byte_array(signed=True, min_length=8)
×
87
            opcode = Opcode.PUSHINT64
×
88
        elif len(data) <= 16:
3✔
89
            data = Integer(integer).to_byte_array(signed=True, min_length=16)
×
90
            opcode = Opcode.PUSHINT128
×
91
        else:
92
            data = data[:32]
3✔
93
            opcode = Opcode.PUSHINT256
3✔
94

95
        return opcode, data
3✔
96

97

98
def has_larger_opcode(opcode: Opcode) -> bool:
3✔
99
    return opcode in _larger_opcode
3✔
100

101

102
def get_larger_opcode(opcode: Opcode):
3✔
103
    """
104
    Gets the large opcode to the standard opcode
105

106
    :return: the respective opcode
107
    :rtype: Opcode or None
108
    """
109
    if opcode in _larger_opcode:
3✔
110
        return _larger_opcode[opcode]
3✔
111
    elif opcode in _larger_opcode.values():
3✔
112
        return opcode
3✔
113
    else:
114
        return None
×
115

116

117
_larger_opcode: Dict[Opcode, Opcode] = {
3✔
118
    Opcode.JMP: Opcode.JMP_L,
119
    Opcode.JMPIF: Opcode.JMPIF_L,
120
    Opcode.JMPIFNOT: Opcode.JMPIFNOT_L,
121
    Opcode.JMPEQ: Opcode.JMPEQ_L,
122
    Opcode.JMPNE: Opcode.JMPNE_L,
123
    Opcode.JMPGT: Opcode.JMPGT_L,
124
    Opcode.JMPGE: Opcode.JMPGE_L,
125
    Opcode.JMPLT: Opcode.JMPLT_L,
126
    Opcode.JMPLE: Opcode.JMPLE_L,
127
    Opcode.CALL: Opcode.CALL_L,
128
    Opcode.TRY: Opcode.TRY_L,
129
    Opcode.ENDTRY: Opcode.ENDTRY_L
130
}
131

132

133
def is_jump(opcode: Opcode) -> bool:
3✔
134
    return Opcode.JMP <= opcode <= Opcode.JMPLE_L
3✔
135

136

137
def get_try_and_data(except_target: int, finally_target: int = 0, jump_through: bool = False) -> Tuple[Opcode, bytes]:
3✔
138
    """
139
    Gets the try opcode and data to the respective targets
140
    """
141
    jmp_placeholder = Opcode.JMP
×
142
    jmp_to_except_placeholder = get_jump_and_data(jmp_placeholder, except_target + jump_through, jump_through)
×
143
    jmp_to_finally_placeholder = get_jump_and_data(jmp_placeholder, finally_target, jump_through and finally_target > 0)
×
144

145
    if jmp_to_except_placeholder[0] == jmp_placeholder and jmp_to_finally_placeholder[0] == jmp_placeholder:
×
146
        opcode = Opcode.TRY
×
147
    else:
148
        opcode = Opcode.TRY_L
×
149

150
    from boa3.internal.neo.vm.opcode.OpcodeInfo import OpcodeInfo
×
151
    opcode_info = OpcodeInfo.get_info(opcode)
×
152
    each_arg_len = opcode_info.data_len // 2
×
153

154
    return (opcode,
×
155
            jmp_to_except_placeholder[1].rjust(each_arg_len, b'\x00')
156
            + jmp_to_finally_placeholder[1].rjust(each_arg_len, b'\x00'))
157

158

159
def get_jump_and_data(opcode: Opcode, integer: int, jump_through: bool = False) -> Tuple[Opcode, bytes]:
3✔
160
    """
161
    Gets the jump opcode and data to the respective integer
162

163
    :param opcode: which jump will be used
164
    :param integer: number of bytes that'll be jumped
165
    :param jump_through: whether it should go over the instructions or not
166
    :return: the respective opcode and its required data
167
    :rtype: Tuple[Opcode, bytes]
168
    """
169
    if not has_target(opcode):
3✔
170
        opcode = Opcode.JMP
×
171

172
    from boa3.internal.neo.vm.opcode.OpcodeInfo import OpcodeInfo
3✔
173
    opcode_info = OpcodeInfo.get_info(opcode)
3✔
174
    arg_size = opcode_info.data_len
3✔
175
    jmp_arg = Integer(arg_size + integer + jump_through).to_byte_array(min_length=arg_size) if integer > 0 \
3✔
176
        else Integer(integer).to_byte_array(min_length=arg_size)
177

178
    if len(jmp_arg) > opcode_info.max_data_len and has_larger_opcode(opcode):
3✔
179
        opcode = get_larger_opcode(opcode)
×
180
        opcode_info = OpcodeInfo.get_info(opcode)
×
181
        arg_size = opcode_info.data_len
×
182
        jmp_arg = Integer(arg_size + integer + jump_through).to_byte_array(min_length=arg_size) if integer > 0 \
×
183
            else Integer(integer).to_byte_array(min_length=arg_size)
184
        jmp_opcode = opcode
×
185
    else:
186
        jmp_opcode = opcode
3✔
187

188
    jmp_arg = jmp_arg[-arg_size:]
3✔
189

190
    return jmp_opcode, jmp_arg
3✔
191

192

193
def has_target(opcode: Opcode) -> bool:
3✔
194
    return Opcode.JMP <= opcode <= Opcode.CALL_L or Opcode.TRY <= opcode < Opcode.ENDFINALLY
3✔
195

196

197
def get_drop(position: int) -> Optional[Opcode]:
3✔
198
    """
199
    Gets the opcode to remove the item n back in the stack
200

201
    :param position: index of the variable
202
    :return: the respective opcode
203
    :rtype: Opcode
204
    """
205
    duplicate_item = {
3✔
206
        1: Opcode.DROP,
207
        2: Opcode.NIP
208
    }
209

210
    if position > 0:
3✔
211
        if position in duplicate_item:
3✔
212
            return duplicate_item[position]
3✔
213
        else:
214
            return Opcode.XDROP
3✔
215

216

217
def get_dup(position: int) -> Optional[Opcode]:
3✔
218
    """
219
    Gets the opcode to duplicate the item n back in the stack
220

221
    :param position: index of the variable
222
    :return: the respective opcode
223
    :rtype: Opcode
224
    """
225
    duplicate_item = {
3✔
226
        1: Opcode.DUP,
227
        2: Opcode.OVER
228
    }
229

230
    if position >= 0:
3✔
231
        if position in duplicate_item:
3✔
232
            return duplicate_item[position]
3✔
233
        else:
234
            return Opcode.PICK
3✔
235

236

237
def get_reverse(no_stack_items: int, rotate: bool = False) -> Optional[Opcode]:
3✔
238
    """
239
    Gets the opcode to reverse n items on the stack
240

241
    :param no_stack_items: index of the variable
242
    :param rotate: whether the stack should be reversed or rotated
243
    :return: the respective opcode
244
    :rtype: Opcode
245
    """
246
    if no_stack_items == 3 and rotate:
3✔
247
        return Opcode.ROT
3✔
248

249
    reverse_stack = {
3✔
250
        2: Opcode.SWAP,
251
        3: Opcode.REVERSE3,
252
        4: Opcode.REVERSE4
253
    }
254

255
    if no_stack_items >= 0:
3✔
256
        if no_stack_items in reverse_stack:
3✔
257
            return reverse_stack[no_stack_items]
3✔
258
        else:
259
            return Opcode.REVERSEN
3✔
260

261

262
def get_store(index: int, local: bool, is_arg: bool = False) -> Opcode:
3✔
263
    """
264
    Gets the opcode to store the variable
265

266
    :param index: index of the variable
267
    :param local: identifies if the variable is local or global
268
    :param is_arg: identifies if the variable is an argument of a function. False if local is False.
269
    :return: the respective opcode
270
    :rtype: Opcode
271
    """
272
    if not local:
3✔
273
        is_arg = False
3✔
274

275
    if 0 <= index <= 6:
3✔
276
        if is_arg:
3✔
277
            opcode_value: int = Integer.from_bytes(Opcode.STARG0) + index
3✔
278
        elif local:
3✔
279
            opcode_value: int = Integer.from_bytes(Opcode.STLOC0) + index
3✔
280
        else:
281
            opcode_value: int = Integer.from_bytes(Opcode.STSFLD0) + index
3✔
282
        return Opcode(Integer(opcode_value).to_byte_array())
3✔
283
    else:
284
        if is_arg:
3✔
285
            return Opcode.STARG
×
286
        elif local:
3✔
287
            return Opcode.STLOC
3✔
288
        else:
289
            return Opcode.STSFLD
3✔
290

291

292
def get_load(index: int, local: bool, is_arg: bool = False) -> Opcode:
3✔
293
    """
294
    Gets the opcode to load the variable
295

296
    :param index: index of the variable
297
    :param local: identifies if the variable is local or global
298
    :param is_arg: identifies if the variable is an argument of a function. False if local is False.
299
    :return: the respective opcode
300
    :rtype: Opcode
301
    """
302
    if not local:
3✔
303
        is_arg = False
3✔
304

305
    if 0 <= index <= 6:
3✔
306
        if is_arg:
3✔
307
            opcode_value: int = Integer.from_bytes(Opcode.LDARG0) + index
3✔
308
        elif local:
3✔
309
            opcode_value: int = Integer.from_bytes(Opcode.LDLOC0) + index
3✔
310
        else:
311
            opcode_value: int = Integer.from_bytes(Opcode.LDSFLD0) + index
3✔
312
        return Opcode(Integer(opcode_value).to_byte_array())
3✔
313
    else:
314
        if is_arg:
3✔
315
            return Opcode.LDARG
×
316
        elif local:
3✔
317
            return Opcode.LDLOC
3✔
318
        else:
319
            return Opcode.LDSFLD
3✔
320

321

322
def is_load_slot(opcode: Opcode) -> bool:
3✔
323
    return (Opcode.LDSFLD0 <= opcode <= Opcode.LDSFLD
3✔
324
            or Opcode.LDLOC0 <= opcode <= Opcode.LDLOC
325
            or Opcode.LDARG0 <= opcode <= Opcode.LDARG)
326

327

328
def get_store_from_load(load_opcode) -> Optional[Opcode]:
3✔
329
    """
330
    Gets the store slot opcode equivalent to the given load slot opcode.
331

332
    :param load_opcode: load opcode
333
    :type load_opcode: Opcode
334
    :return: equivalent store opcode if the given opcode is a load slot. Otherwise, returns None
335
    :rtype: Opcode or None
336
    """
337
    if is_load_slot(load_opcode):
3✔
338
        opcode_value: int = Integer.from_bytes(load_opcode) + 8
3✔
339
        return Opcode(Integer(opcode_value).to_byte_array())
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc