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

CityOfZion / neo3-boa / 0aff654d-b027-49a1-9d97-558d9a2a52ff

05 Mar 2024 04:57PM UTC coverage: 91.985% (-0.1%) from 92.106%
0aff654d-b027-49a1-9d97-558d9a2a52ff

push

circleci

web-flow
Merge pull request #1215 from CityOfZion/CU-86drpndkk

CU-86drpndkk - Refactor test_interop/test_blockchain.py to use BoaTestConstructor

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

658 existing lines in 143 files now uncovered.

20635 of 22433 relevant lines covered (91.99%)

1.84 hits per line

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

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

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

8

9
def get_pushdata_and_data(bytestring: str | bytes) -> tuple[Opcode, bytes]:
2✔
10
    """
11
    Gets the push opcode and data to the respective str ot bytes value
12

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

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

31
    data = bytes_size + bytestring
×
UNCOV
32
    return opcode, data
×
33

34

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

45
    return opcode, bytes_size
2✔
46

47

48
def get_literal_push(integer: int) -> Opcode | None:
2✔
49
    """
50
    Gets the push opcode to the respective integer
51

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

62

63
def get_push_and_data(integer: int) -> tuple[Opcode, bytes]:
2✔
64
    """
65
    Gets the push opcode and data to the respective integer
66

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

93
        return opcode, data
2✔
94

95

96
def has_larger_opcode(opcode: Opcode) -> bool:
2✔
97
    return opcode in _larger_opcode
2✔
98

99

100
def get_larger_opcode(opcode: Opcode):
2✔
101
    """
102
    Gets the large opcode to the standard opcode
103

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

114

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

130

131
def is_jump(opcode: Opcode) -> bool:
2✔
132
    return Opcode.JMP <= opcode <= Opcode.JMPLE_L
2✔
133

134

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

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

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

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

156

157
def get_jump_and_data(opcode: Opcode, integer: int, jump_through: bool = False) -> tuple[Opcode, bytes]:
2✔
158
    """
159
    Gets the jump opcode and data to the respective integer
160

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

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

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

186
    jmp_arg = jmp_arg[-arg_size:]
2✔
187

188
    return jmp_opcode, jmp_arg
2✔
189

190

191
def has_target(opcode: Opcode) -> bool:
2✔
192
    return Opcode.JMP <= opcode <= Opcode.CALL_L or Opcode.TRY <= opcode < Opcode.ENDFINALLY
2✔
193

194

195
def get_drop(position: int) -> Opcode | None:
2✔
196
    """
197
    Gets the opcode to remove the item n back in the stack
198

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

208
    if position > 0:
2✔
209
        if position in duplicate_item:
2✔
210
            return duplicate_item[position]
2✔
211
        else:
212
            return Opcode.XDROP
2✔
213

214

215
def get_dup(position: int) -> Opcode | None:
2✔
216
    """
217
    Gets the opcode to duplicate the item n back in the stack
218

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

228
    if position >= 0:
2✔
229
        if position in duplicate_item:
2✔
230
            return duplicate_item[position]
2✔
231
        else:
232
            return Opcode.PICK
2✔
233

234

235
def get_reverse(no_stack_items: int, rotate: bool = False) -> Opcode | None:
2✔
236
    """
237
    Gets the opcode to reverse n items on the stack
238

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

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

253
    if no_stack_items >= 0:
2✔
254
        if no_stack_items in reverse_stack:
2✔
255
            return reverse_stack[no_stack_items]
2✔
256
        else:
257
            return Opcode.REVERSEN
2✔
258

259

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

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

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

289

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

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

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

319

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

325

326
def get_store_from_load(load_opcode) -> Opcode | None:
2✔
327
    """
328
    Gets the store slot opcode equivalent to the given load slot opcode.
329

330
    :param load_opcode: load opcode
331
    :type load_opcode: Opcode
332
    :return: equivalent store opcode if the given opcode is a load slot. Otherwise, returns None
333
    :rtype: Opcode or None
334
    """
335
    if is_load_slot(load_opcode):
2✔
336
        opcode_value: int = Integer.from_bytes(load_opcode) + 8
2✔
337
        return Opcode(Integer(opcode_value).to_byte_array())
2✔
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