def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.Integer import Integer number_zero_to_bytes = b'\x00' jmp_place_holder = (Opcode.JMP, b'\x01') verify_number_is_zero = [ (Opcode.DUP, b''), (Opcode.NZ, b''), jmp_place_holder ] number_is_not_zero = super().opcode.copy() number_is_not_zero.append(jmp_place_holder) number_is_zero = [ (Opcode.DROP, b''), (Opcode.PUSHDATA1, Integer(len(number_zero_to_bytes)).to_byte_array() + number_zero_to_bytes), ] number_is_not_zero[-1] = Opcode.get_jump_and_data(Opcode.JMP, get_bytes_count(number_is_zero)) verify_number_is_zero[-1] = Opcode.get_jump_and_data(Opcode.JMPIFNOT, get_bytes_count(number_is_not_zero)) return ( verify_number_is_zero + number_is_not_zero + number_is_zero + [(Opcode.NOP, b'')] # TODO: change this when refactoring calling methods with methods as args )
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.neo.vm.type.Integer import Integer from boa3.neo.vm.type.StackItem import StackItemType ECPOINT_SIZE = 33 throw_if_invalid = [ (Opcode.THROW, b''), ] check_bytestr_size = [ (Opcode.DUP, b''), (Opcode.SIZE, b''), (Opcode.PUSHINT8, Integer(ECPOINT_SIZE).to_byte_array(signed=True)), Opcode.get_jump_and_data(Opcode.JMPEQ, get_bytes_count(throw_if_invalid), jump_through=True), ] return [ (Opcode.CONVERT, StackItemType.ByteString), # convert to ECPoint (Opcode.DUP, b''), (Opcode.ISNULL, b''), Opcode.get_jump_and_data(Opcode.JMPIF, get_bytes_count(check_bytestr_size), jump_through=True), ] + check_bytestr_size + throw_if_invalid
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count jmp_place_holder = (Opcode.JMP, b'\x01') create_auxiliary_vars = [ # initializes auxiliary variables (Opcode.NEWMAP, b''), # dict_copy = {} (Opcode.OVER, b''), (Opcode.KEYS, b''), # list_keys = list(dict_target) (Opcode.DUP, b''), (Opcode.SIZE, b''), (Opcode.DEC, b''), # index = len(list_keys) - 1 ] verify_while_condition = [ # if index < 0: go clean stack (Opcode.DUP, b''), (Opcode.PUSH0, b''), jmp_place_holder # jump to clean_stack ] while_loop = [ # while index >= 0: add key value pair into new dict (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.PICKITEM, b''), # key = list_keys[index] (Opcode.PUSH5, b''), (Opcode.PICK, b''), (Opcode.OVER, b''), (Opcode.PICKITEM, b''), # value = dict_target[key] (Opcode.SETITEM, b''), # dict_copy[key] = value (Opcode.DEC, b''), # index-- # jump back to verify_while_condition ] jmp_back_to_verify = Opcode.get_jump_and_data(Opcode.JMP, -get_bytes_count(while_loop + verify_while_condition)) while_loop.append(jmp_back_to_verify) jmp_while_loop = Opcode.get_jump_and_data(Opcode.JMPLT, get_bytes_count(while_loop)) verify_while_condition[-1] = jmp_while_loop clean_stack = [ # remove all values from stack except the dict_copy (Opcode.DROP, b''), (Opcode.DROP, b''), (Opcode.NIP, b'') ] return ( create_auxiliary_vars + verify_while_condition + while_loop + clean_stack )
def is_instance_opcodes(self) -> List[Tuple[Opcode, bytes]]: is_type_opcodes = [ (Opcode.DUP, b''), # if is the same internal type (Opcode.ISTYPE, self.stack_item) ] return_false = [ (Opcode.DROP, b''), (Opcode.PUSH0, b''), # return False ] return_false_bytes_size = get_bytes_count(return_false) final_instructions = [] final_jmp = [(Opcode.JMP, Integer(2 + return_false_bytes_size).to_byte_array(min_length=1))] validate_type = self._is_instance_inner_opcodes(get_bytes_count(final_jmp)) if len(validate_type) > 0: final_instructions += (validate_type + final_jmp) bytes_size = get_bytes_count(final_instructions) jmp_opcode, jmp_arg = Opcode.get_jump_and_data(Opcode.JMPIFNOT, bytes_size + 1) final_instructions = (is_type_opcodes + [(jmp_opcode, jmp_arg)] + final_instructions + return_false) else: is_type_opcodes.remove((Opcode.DUP, b'')) final_instructions = is_type_opcodes return final_instructions
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.neo3.contracts.namedcurve import NamedCurve curve = NamedCurve.SECP256K1 return ([(Opcode.DUP, b''), Opcode.get_push_and_data(curve), (Opcode.APPEND, b'')] + super().opcode)
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.Integer import Integer from boa3.neo.vm.type.String import String message = String("decimals cannot be negative").to_bytes() if_negative_decimal = [ (Opcode.PUSHDATA1, Integer(len(message)).to_byte_array(signed=True, min_length=1) + message), (Opcode.THROW, b''), ] decimals_unit = [ (Opcode.OVER, b''), (Opcode.PUSH0, b''), Opcode.get_jump_and_data(Opcode.JMPGE, get_bytes_count(if_negative_decimal), True), ] + if_negative_decimal + [ (Opcode.DUP, b''), (Opcode.REVERSE3, b''), Opcode.get_push_and_data(10), (Opcode.SWAP, b''), (Opcode.POW, b''), (Opcode.SWAP, b''), (Opcode.OVER, b''), (Opcode.MOD, b'') ] if_negative_mod = [ (Opcode.ADD, b''), (Opcode.JMP, b'') ] else_negative_mod = [ (Opcode.NIP, b'') ] num_jmp_code = get_bytes_count(else_negative_mod) if_negative_mod[-1] = Opcode.get_jump_and_data(Opcode.JMP, num_jmp_code, True) num_jmp_code = get_bytes_count(if_negative_mod) floor_computation = [ (Opcode.DUP, b''), (Opcode.PUSH0, b''), Opcode.get_jump_and_data(Opcode.JMPGE, num_jmp_code, True), ] + if_negative_mod + else_negative_mod + [ (Opcode.SUB, b'') ] return decimals_unit + floor_computation
def _is_instance_inner_opcodes(self, jmp_to_if_false: int = 0) -> List[Tuple[Opcode, bytes]]: if self.stack_item not in (StackItemType.Array, StackItemType.Struct, StackItemType.Map): return [] variables_count_is_equal = [ (Opcode.DUP, b''), (Opcode.SIZE, b''), Opcode.get_push_and_data(len(self._all_variables)), (Opcode.NUMEQUAL, b''), ] total_size = jmp_to_if_false variables_type_validations = [] for var_index, (var_id, var) in reversed(list(enumerate(self._all_variables.items()))): if var.type.stack_item != StackItemType.Any: # validate primitive types only to avoid recursive code if self.stack_item == StackItemType.Map: get = Opcode.get_pushdata_and_data(var_id) else: get = Opcode.get_push_and_data(var_index) new_opcodes = [ (Opcode.DUP, b''), get, (Opcode.PICKITEM, b''), (Opcode.ISTYPE, var.type.stack_item) ] if len(variables_type_validations) == 0: new_opcodes.append((Opcode.NIP, b'')) elif total_size > 0: new_opcodes.append((Opcode.get_jump_and_data(Opcode.JMPIFNOT, total_size + 1))) variables_type_validations = new_opcodes + variables_type_validations total_size += get_bytes_count(new_opcodes) if total_size > 0: if len(variables_type_validations) > 0: final_opcode = (Opcode.get_jump_and_data(Opcode.JMPIFNOT, total_size + 1)) else: final_opcode = (Opcode.NIP, b'') variables_count_is_equal.append(final_opcode) return variables_count_is_equal + variables_type_validations
def opcode(self) -> List[Tuple[Opcode, bytes]]: return [ Opcode.get_pushdata_and_data(bytes(33)), # vote_to (Opcode.PUSH0, b''), # height (Opcode.PUSH0, b''), # balance (Opcode.PUSH3, b''), (Opcode.PACK, b'') ]
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3 import constants from boa3.model.builtin.interop.crypto.checksigmethod import CheckSigMethod from boa3.model.builtin.interop.crypto.sha256method import Sha256Method from boa3.model.builtin.interop.crypto.ripemd160method import Ripemd160Method from boa3.model.type.type import Type # build CheckSig script pushdata, pushbytes = Opcode.get_pushdata_and_data_from_size( constants.SIZE_OF_ECPOINT) opcodes = [ Opcode.get_pushdata_and_data(pushdata + pushbytes), (Opcode.SWAP, b''), Opcode.get_pushdata_and_data(CheckSigMethod.get_raw_bytes()), (Opcode.CAT, b''), (Opcode.CAT, b''), ] opcodes.extend([ Opcode.get_push_and_data(1), # pack argument to call syscall (Opcode.PACK, b'') ] + Sha256Method().opcode) opcodes.extend([ Opcode.get_push_and_data(1), # pack argument to call syscall (Opcode.PACK, b'') ] + Ripemd160Method().opcode) opcodes.extend([ # limit result to UInt160 length Opcode.get_push_and_data(0), Opcode.get_push_and_data(constants.SIZE_OF_INT160), (Opcode.SUBSTR, b''), (Opcode.CONVERT, Type.bytes.stack_item) ]) return opcodes
def _is_instance_inner_opcodes(self, jmp_to_if_false: int = 0) -> List[Tuple[Opcode, bytes]]: from boa3 import constants push_int_opcode, size_data = Opcode.get_push_and_data(constants.SIZE_OF_ECPOINT) return [ (Opcode.SIZE, b''), # return len(value) == 33 (push_int_opcode, size_data), (Opcode.NUMEQUAL, b'') ]
def _is_instance_inner_opcodes(self, jmp_to_if_false: int = 0 ) -> List[Tuple[Opcode, bytes]]: push_int_opcode, size_data = Opcode.get_push_and_data(32) return [ (Opcode.SIZE, b''), # return len(value) == 32 (push_int_opcode, size_data), (Opcode.NUMEQUAL, b'') ]
def swap_reverse_stack_items(self, no_items: int = 0): # n = 0 -> value varies in runtime if 0 <= no_items != 1: opcode: Opcode = Opcode.get_reverse(no_items) if opcode is Opcode.REVERSEN and no_items > 0: self.convert_literal(no_items) op_info = OpcodeInfo.get_info(opcode) self.__insert1(op_info) if no_items > 0: reverse = list(reversed(self._stack[-no_items:])) self._stack = self._stack[:-no_items] self._stack.extend(reverse)
def convert_integer_literal(self, value: int): """ Converts an integer literal value :param value: the value to be converted """ opcode = Opcode.get_literal_push(value) if opcode is not None: op_info: OpcodeInformation = OpcodeInfo.get_info(opcode) self.__insert1(op_info) self._stack.append(Type.int) else: opcode = Opcode.get_literal_push(-value) if opcode is not None: op_info: OpcodeInformation = OpcodeInfo.get_info(opcode) self.__insert1(op_info) self._stack.append(Type.int) self.convert_operation(UnaryOp.Negative) else: array = Integer(value).to_byte_array(signed=True) self.insert_push_data(array) # cast the value to integer self.convert_cast(Type.int)
def opcode(self) -> List[Tuple[Opcode, bytes]]: jmp_place_holder = (Opcode.JMP, b'') mod = [ ( Opcode.SWAP, b'' ), # neo's mod has a different result from python's mod in some cases (Opcode.OVER, b''), (Opcode.MOD, b''), (Opcode.DUP, b''), (Opcode.SIGN, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIGN, b''), ( Opcode.OVER, b'' ), # if the result is not zero and the sign is different than the second operator (Opcode.NUMEQUAL, b''), # the result is different (Opcode.SWAP, b''), (Opcode.PUSH0, b''), (Opcode.NUMEQUAL, b''), # the result is different (Opcode.BOOLOR, b''), jmp_place_holder, ] from boa3.compiler.codegenerator import get_bytes_count if_negative = [(Opcode.ADD, b''), jmp_place_holder] num_jmp_code = get_bytes_count(if_negative) mod[-1] = Opcode.get_jump_and_data(Opcode.JMPIF, num_jmp_code, True) else_negative = [(Opcode.NIP, b'')] num_jmp_code = get_bytes_count(else_negative) if_negative[-1] = Opcode.get_jump_and_data(Opcode.JMP, num_jmp_code, True) return mod + if_negative + else_negative
def duplicate_stack_item(self, pos: int = 0): """ Duplicates the item n back in the stack :param pos: index of the variable """ # n = 1 -> duplicates stack top item # n = 0 -> value varies in runtime if pos >= 0: opcode: Opcode = Opcode.get_dup(pos) if opcode is Opcode.PICK and pos > 0: self.convert_literal(pos - 1) self._stack.pop() op_info = OpcodeInfo.get_info(opcode) self.__insert1(op_info) self._stack.append(self._stack[-pos])
def remove_stack_item(self, pos: int = 0): """ Removes the item n from the stack :param pos: index of the variable """ # n = 1 -> removes stack top item if pos > 0: opcode: Opcode = Opcode.get_drop(pos) if opcode is Opcode.XDROP: self.convert_literal(pos - 1) self._stack.pop() op_info = OpcodeInfo.get_info(opcode) self.__insert1(op_info) if pos > 0: self._stack.pop(-pos)
def is_instance_opcodes(self) -> List[Tuple[Opcode, bytes]]: from boa3.neo.vm.type.Integer import Integer push_int_opcode, size_data = Opcode.get_push_and_data(20) return [ (Opcode.DUP, b''), # if isinstance(value, bytes): (Opcode.ISTYPE, self.stack_item), (Opcode.JMPIFNOT, Integer(7 + len(size_data)).to_byte_array(min_length=1)), (Opcode.SIZE, b''), # return len(value) == 20 (push_int_opcode, size_data), (Opcode.NUMEQUAL, b''), (Opcode.JMP, Integer(4).to_byte_array(min_length=1)), (Opcode.DROP, b''), (Opcode.PUSH0, b''), # return False ]
def convert_store_variable(self, var_id: str): """ Converts the assignment of a variable :param var_id: the value to be converted """ index, local, is_arg = self._get_variable_info(var_id) if index >= 0: opcode = Opcode.get_store(index, local, is_arg) if opcode is not None: op_info = OpcodeInfo.get_info(opcode) if op_info.data_len > 0: self.__insert1(op_info, Integer(index).to_byte_array()) else: self.__insert1(op_info) self._stack.pop()
def convert_builtin_method_call(self, function: IBuiltinMethod, args_address: List[int] = None): """ Converts a builtin method function call :param function: the function to be converted :param args_address: a list with each function arguments' first addresses """ if args_address is None: args_address = [] store_opcode: OpcodeInformation = None store_data: bytes = b'' if function.stores_on_slot and 0 < len( function.args) <= len(args_address): address = args_address[-len(function.args)] load_instr = VMCodeMapping.instance().code_map[address] if load_instr.opcode.is_load_slot: store: Opcode = Opcode.get_store_from_load(load_instr.opcode) store_opcode = OpcodeInfo.get_info(store) store_data = load_instr.data for opcode, data in function.opcode: op_info = OpcodeInfo.get_info(opcode) self.__insert1(op_info, data) if store_opcode is not None: self._insert_jump(OpcodeInfo.JMP) jump = self.last_code_start_address self.__insert1(store_opcode, store_data) self._update_jump(jump, VMCodeMapping.instance().bytecode_size) for _ in range(function.args_on_stack): self._stack.pop() if function.return_type not in (None, Type.none): self._stack.append(function.return_type)
def convert_load_variable(self, var_id: str, var: Variable): """ Converts the assignment of a variable :param var_id: the value to be converted :param var: the actual variable to be loaded """ index, local, is_arg = self._get_variable_info(var_id) if index >= 0: opcode = Opcode.get_load(index, local, is_arg) op_info = OpcodeInfo.get_info(opcode) if op_info.data_len > 0: self.__insert1(op_info, Integer(index).to_byte_array()) else: self.__insert1(op_info) self._stack.append(var.type) elif hasattr(var.type, 'get_value'): # the variable is a type constant # TODO: change this when implement class conversion value = var.type.get_value(var_id.split('.')[-1]) if value is not None: self.convert_literal(value)
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.model.type.type import Type jmp_place_holder = (Opcode.JMP, b'\x01') # region verify end verify_end_null = [ # verifies if end is null (Opcode.REVERSE4, b''), (Opcode.DUP, b''), (Opcode.ISNULL, b''), jmp_place_holder ] end_null = [ # if end is null, end = len(self) (Opcode.DROP, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), jmp_place_holder # skip the other end verifications ] num_jmp_code = get_bytes_count(end_null) jmp_str_end_null_statement = Opcode.get_jump_and_data( Opcode.JMPIFNOT, num_jmp_code, True) verify_end_null[-1] = jmp_str_end_null_statement verify_end_neg = [ # verifies if end is < 0 (Opcode.DUP, b''), (Opcode.PUSH0, b''), jmp_place_holder ] end_neg = [ # if end is < 0, then get the positive equivalent number (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), (Opcode.ADD, b''), (Opcode.DUP, b''), (Opcode.PUSH0, b''), jmp_place_holder ] end_still_neg = [ # if end is still < 0 after adding it to len(self), end = 0 (Opcode.DROP, b''), (Opcode.PUSH0, b''), ] num_jmp_code = get_bytes_count(end_still_neg) jmp_end_still_neg_statement = Opcode.get_jump_and_data( Opcode.JMPGE, num_jmp_code, True) end_neg[-1] = jmp_end_still_neg_statement skip_end_gt_size = [ jmp_place_holder # skip the other end verification ] num_jmp_code = get_bytes_count(end_still_neg + end_neg + skip_end_gt_size) jmp_end_neg_statement = Opcode.get_jump_and_data( Opcode.JMPGE, num_jmp_code, True) verify_end_neg[-1] = jmp_end_neg_statement verify_end_gt_size = [ # verifies if end >= len(self) (Opcode.DUP, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), jmp_place_holder, ] end_gt_size = [ # if end >= len(self), end = len(self) (Opcode.DROP, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), ] num_jmp_code = get_bytes_count(end_gt_size) jmp_end_gt_size_statement = Opcode.get_jump_and_data( Opcode.JMPLE, num_jmp_code, True) verify_end_gt_size[-1] = jmp_end_gt_size_statement num_jmp_code = get_bytes_count(end_gt_size + verify_end_gt_size) jmp_whole_end_gt_size_statement = Opcode.get_jump_and_data( Opcode.JMP, num_jmp_code, True) skip_end_gt_size[-1] = jmp_whole_end_gt_size_statement num_jmp_code = get_bytes_count(end_gt_size + verify_end_gt_size + skip_end_gt_size + end_still_neg + end_neg + verify_end_neg) jmp_whole_end_gt_size_statement = Opcode.get_jump_and_data( Opcode.JMP, num_jmp_code, True) end_null[-1] = jmp_whole_end_gt_size_statement verify_end = (verify_end_null + end_null + verify_end_neg + end_neg + end_still_neg + skip_end_gt_size + verify_end_gt_size + end_gt_size) # endregion # region verify start verify_start_neg = [ # verifies if start is < 0 (Opcode.OVER, b''), (Opcode.PUSH0, b''), jmp_place_holder ] start_neg = [ # if start is < 0, then get the positive equivalent index (Opcode.SWAP, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), (Opcode.ADD, b''), (Opcode.DUP, b''), (Opcode.PUSH0, b''), jmp_place_holder ] start_still_neg = [ # if start is still < 0 after adding it to len(self), start = 0 (Opcode.DROP, b''), (Opcode.PUSH0, b''), ] num_jmp_code = get_bytes_count(start_still_neg) jmp_str_start_still_neg_statement = Opcode.get_jump_and_data( Opcode.JMPGE, num_jmp_code, True) start_neg[-1] = jmp_str_start_still_neg_statement correct_start_position = [ # put start on the correct position (Opcode.SWAP, b'') ] num_jmp_code = get_bytes_count(correct_start_position + start_still_neg + start_neg) jmp_str_start_neg_statement = Opcode.get_jump_and_data( Opcode.JMPGE, num_jmp_code, True) verify_start_neg[-1] = jmp_str_start_neg_statement verify_start = (verify_start_neg + start_neg + start_still_neg + correct_start_position) # endregion # region Count logic initialize = [ # initialize variables (Opcode.PUSH0, b''), # count = 0 (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), # substr_size = len(substr) (Opcode.SWAP, b''), ] verify_while = [ # verifies if substr_size + index >= end (Opcode.OVER, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.ADD, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), jmp_place_holder ] count_substring = [ # verifies if self[index: index+substr_size] == substr (Opcode.PUSH5, b''), (Opcode.PICK, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SUBSTR, b''), (Opcode.CONVERT, Type.str.stack_item), (Opcode.PUSH5, b''), (Opcode.PICK, b''), (Opcode.NUMEQUAL, b''), jmp_place_holder ] count_plusplus = [ # if self[index: index+substr_size] == substr (Opcode.INC, b''), # count++ ] num_jmp_code = get_bytes_count(count_plusplus) jmp_count_plusplus_statement = Opcode.get_jump_and_data( Opcode.JMPIFNOT, num_jmp_code, True) count_substring[-1] = jmp_count_plusplus_statement go_back_to_while = [ # go back to while verification (Opcode.REVERSE4, b''), (Opcode.INC, b''), # index ++ (Opcode.REVERSE4, b''), # jump back to while ] num_jmp_code = -get_bytes_count(go_back_to_while + count_plusplus + count_substring + verify_while) jmp_back_to_while_statement = Opcode.get_jump_and_data( Opcode.JMP, num_jmp_code) go_back_to_while.append(jmp_back_to_while_statement) num_jmp_code = get_bytes_count(go_back_to_while + count_plusplus + count_substring) jmp_to_clean_stack_statement = Opcode.get_jump_and_data( Opcode.JMPGT, num_jmp_code, True) verify_while[-1] = jmp_to_clean_stack_statement clean_stack = [ # remove auxiliary values (Opcode.REVERSE4, b''), (Opcode.DROP, b''), (Opcode.DROP, b''), (Opcode.DROP, b''), (Opcode.REVERSE3, b''), (Opcode.DROP, b''), (Opcode.DROP, b''), ] count_logic = (initialize + verify_while + count_substring + count_plusplus + go_back_to_while + clean_stack) # endregion # count string logic is verify_end + verify_start + count_logic return (verify_end + verify_start + count_logic)
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.StackItem import StackItemType jmp_place_holder = (Opcode.JMP, b'\x01') a_lower = ord('a') a_upper = ord('A') zero = ord('0') # region verify_base verify_base_ge37 = [ # verifies if base >= 37, base greater than 36 shouldn't be accepted (Opcode.OVER, b''), Opcode.get_push_and_data(37), jmp_place_holder, # jumps to an assertion error ] verify_base_equal1 = [ # verifies if base == 1, base equals 1 shouldn't be accepted (Opcode.OVER, b''), (Opcode.PUSH1, b''), jmp_place_holder # jumps to an assertion error ] verify_base_le_minus1 = [ # verifies if base <= -1, base less than 0 shouldn't be accepted (Opcode.OVER, b''), (Opcode.PUSHM1, b''), jmp_place_holder, # jumps to an assertion error ] verify_base_jump_error = [ # jumps the next set of instructions jmp_place_holder ] verify_base_error = [ # an invalid base was used (Opcode.PUSH0, b''), (Opcode.ASSERT, b''), ] # region verify_base jump logic jump_instructions = verify_base_error verify_base_jump_error[-1] = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(jump_instructions), True) jump_instructions = verify_base_jump_error verify_base_le_minus1[-1] = Opcode.get_jump_and_data( Opcode.JMPLE, get_bytes_count(jump_instructions), True) jump_instructions = verify_base_jump_error + verify_base_le_minus1 verify_base_equal1[-1] = Opcode.get_jump_and_data( Opcode.JMPEQ, get_bytes_count(jump_instructions), True) jump_instructions = verify_base_jump_error + verify_base_le_minus1 + verify_base_equal1 verify_base_ge37[-1] = Opcode.get_jump_and_data( Opcode.JMPGE, get_bytes_count(jump_instructions), True) # endregion verify_base = (verify_base_ge37 + verify_base_equal1 + verify_base_le_minus1 + verify_base_jump_error + verify_base_error) # endregion # region verify_code_literal verify_code_literal_first_char_is_0 = [ # verifies if the first char in the value is '0' (Opcode.DUP, b''), (Opcode.PUSH0, b''), (Opcode.PUSH1, b''), (Opcode.SUBSTR, b''), (Opcode.CONVERT, StackItemType.ByteString), Opcode.get_pushdata_and_data('0'), jmp_place_holder, # if first char is not 0, skip all verify_code_literal methods ] verify_code_literal_remove_first_char = [ # the first char is 0, so it will be removed from the value (Opcode.PUSH1, b''), (Opcode.OVER, b''), (Opcode.SIZE, b''), (Opcode.DEC, b''), (Opcode.SUBSTR, b''), (Opcode.CONVERT, StackItemType.ByteString), ] verify_code_literal_get_base_char = [ # puts the char that could refer to a base in the top of the stack (Opcode.DUP, b''), (Opcode.PUSH0, b''), (Opcode.PUSH1, b''), (Opcode.SUBSTR, b''), (Opcode.CONVERT, StackItemType.ByteString), ] verify_code_literal_initialize_verification = [ # puts a False in the top of the stack to assist the (Opcode.PUSH0, b'') # verifications above ] verify_code_literal_base_verification = ( # verifies if the original value starts with 0b, 0B, 0o, 0O self._verify_is_specific_base( 'b', 2) + # 0x, or 0X, and changes the base if necessary self._verify_is_specific_base('B', 2) + self._verify_is_specific_base('o', 8) + self._verify_is_specific_base('O', 8) + self._verify_is_specific_base('x', 16) + self._verify_is_specific_base('X', 16)) verify_code_literal_drop_aux = [ # drops auxiliary values from the stack and just keeps the (Opcode.DROP, b''), # base and value on it (Opcode.DROP, b''), ] # region verify_code_literal jump logic jump_instructions = (verify_code_literal_remove_first_char + verify_code_literal_get_base_char + verify_code_literal_initialize_verification + verify_code_literal_base_verification + verify_code_literal_drop_aux) verify_code_literal_first_char_is_0[-1] = Opcode.get_jump_and_data( Opcode.JMPNE_L, get_bytes_count(jump_instructions), True) # endregion verify_code_literal = (verify_code_literal_first_char_is_0 + verify_code_literal_remove_first_char + verify_code_literal_get_base_char + verify_code_literal_initialize_verification + verify_code_literal_base_verification + verify_code_literal_drop_aux) # endregion # region ascii to int ascii_to_int_initialize = [ # initialize auxiliary variables on the stack (Opcode.DUP, b''), (Opcode.SIZE, b''), (Opcode.DEC, b''), # index = len(value) - 1 (Opcode.DUP, b''), (Opcode.PUSH0, b''), (Opcode.GE, b''), (Opcode.ASSERT, b''), # verifies if value was empty (Opcode.PUSH0, b''), # sum = 0 (Opcode.PUSH1, b''), # mult = 1 ] ascii_to_int_while = [ # loops from left to right of the value to calculate the equivalent sum in base 10 (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.PUSH1, b''), (Opcode.SUBSTR, b''), # char = value[index] (Opcode.CONVERT, StackItemType.Integer), ] ascii_to_int_verify_az_lower_lt = [ # verifies ord(char) is less than ord('a') (Opcode.DUP, b''), # if ord(char) is indeed less, go to verify with ord('A') Opcode.get_push_and_data(a_lower), jmp_place_holder # jump to ascii_to_int_verify_az_upper_lt ] ascii_to_int_verify_az_lower_gt = [ # verifies ord(char) is greater than base + 86 ( Opcode.DUP, b'' ), # the minimum base that accepts letters is base 11, that's why it's (Opcode.PUSH6, b''), # adding 86 (Opcode.PICK, b'' ), # if ord(char) is greater than base + 86, the char is invalid Opcode.get_push_and_data(86), (Opcode.ADD, b''), (Opcode.LE, b''), (Opcode.ASSERT, b''), # if assert is False, then the char was indeed invalid ] ascii_to_int_verify_az_lower = [ # the char was in between 'a' and 'z', Opcode.get_push_and_data( 87 ), # so it will be converted to the respective base 10 number (Opcode.SUB, b''), (Opcode.OVER, b''), (Opcode.MUL, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.ADD, b''), # sum = sum + (ord(char) - 55)) * mult (Opcode.REVERSE3, b''), (Opcode.DROP, b''), jmp_place_holder # jumps to ascii_to_int_verify_while ] ascii_to_int_verify_az_upper_lt = [ # verifies ord(char) is less than ord('A') (Opcode.DUP, b''), # if ord(char) is indeed less, go to verify with ord('0') Opcode.get_push_and_data(a_upper), jmp_place_holder # jump to ascii_to_int_verify_09_lt ] ascii_to_int_verify_az_upper_gt = [ # verifies ord(char) is greater than base + 54 ( Opcode.DUP, b'' ), # the minimum base that accepts letters is base 11, that's why it's (Opcode.PUSH6, b''), # adding 54 (Opcode.PICK, b'' ), # if ord(char) is greater than base + 54, the char is invalid Opcode.get_push_and_data(54), (Opcode.ADD, b''), (Opcode.LE, b''), (Opcode.ASSERT, b''), # if assert is False, then the char was indeed invalid ] ascii_to_int_verify_az_upper = [ # the char was in between 'A' and 'Z', Opcode.get_push_and_data( 55 ), # so it will be converted to the respective base 10 number (Opcode.SUB, b''), (Opcode.OVER, b''), (Opcode.MUL, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.ADD, b''), # sum = sum + (ord(char) - 55)) * mult (Opcode.REVERSE3, b''), (Opcode.DROP, b''), jmp_place_holder # jumps to ascii_to_int_verify_while ] ascii_to_int_verify_09_lt = [ # verifies ord(char) is less than ord('0') (Opcode.DUP, b''), # if ord(char) is indeed less, the char is invalid Opcode.get_push_and_data(zero), (Opcode.GE, b''), (Opcode.ASSERT, b''), # if assert is False, then the char was indeed invalid ] ascii_to_int_verify_09_gt = [ # verifies ord(char) is greater than base + ord('0') ( Opcode.DUP, b'' ), # if ord(char) is greater than base + ord('0'), the char is invalid (Opcode.PUSH6, b''), (Opcode.PICK, b''), Opcode.get_push_and_data(zero), (Opcode.ADD, b''), (Opcode.LE, b''), (Opcode.ASSERT, b''), # if assert is False, then the char was indeed invalid ] ascii_to_int_verify_09 = [ # the char was in between '0' and '9', Opcode.get_push_and_data( zero ), # so it will be converted to the respective base 10 number (Opcode.SUB, b''), (Opcode.OVER, b''), (Opcode.MUL, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.ADD, b''), # sum = sum + (ord(char) - ord('0')) * mult (Opcode.REVERSE3, b''), (Opcode.DROP, b''), ] ascii_to_int_verify_while = [ # verifies if the loop already went through all the chars in value (Opcode.REVERSE3, b''), (Opcode.DEC, b''), # index-- (Opcode.REVERSE3, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.MUL, b''), # mult = mult * base (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.PUSH0, b''), # jmp back to ascii_to_int_while if index >= 0 ] # region ascii to int jump logic jump_instructions = (ascii_to_int_verify_09_lt + ascii_to_int_verify_09_gt + ascii_to_int_verify_09) ascii_to_int_verify_az_upper[-1] = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(jump_instructions), True) jump_instructions = (ascii_to_int_verify_az_upper_gt + ascii_to_int_verify_az_upper) ascii_to_int_verify_az_upper_lt[-1] = Opcode.get_jump_and_data( Opcode.JMPLT, get_bytes_count(jump_instructions), True) jump_instructions = (ascii_to_int_verify_az_upper_lt + ascii_to_int_verify_az_upper_gt + ascii_to_int_verify_az_upper + ascii_to_int_verify_09_lt + ascii_to_int_verify_09_gt + ascii_to_int_verify_09) ascii_to_int_verify_az_lower[-1] = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(jump_instructions), True) jump_instructions = (ascii_to_int_verify_az_lower_gt + ascii_to_int_verify_az_lower) ascii_to_int_verify_az_lower_lt[-1] = Opcode.get_jump_and_data( Opcode.JMPLT, get_bytes_count(jump_instructions), True) jump_instructions = ( ascii_to_int_while + ascii_to_int_verify_az_lower_lt + ascii_to_int_verify_az_lower_gt + ascii_to_int_verify_az_lower + ascii_to_int_verify_az_upper_lt + ascii_to_int_verify_az_upper_gt + ascii_to_int_verify_az_upper + ascii_to_int_verify_09_gt + ascii_to_int_verify_09_lt + ascii_to_int_verify_09 + ascii_to_int_verify_while) ascii_to_int_verify_while.append( Opcode.get_jump_and_data(Opcode.JMPGE, -get_bytes_count(jump_instructions), True)) # endregion ascii_to_int = (ascii_to_int_initialize + ascii_to_int_while + ascii_to_int_verify_az_lower_lt + ascii_to_int_verify_az_lower_gt + ascii_to_int_verify_az_lower + ascii_to_int_verify_az_upper_lt + ascii_to_int_verify_az_upper_gt + ascii_to_int_verify_az_upper + ascii_to_int_verify_09_lt + ascii_to_int_verify_09_gt + ascii_to_int_verify_09 + ascii_to_int_verify_while) # endregion # region clean stack clean_stack = [ # clean everything but the number in base 10 (Opcode.DROP, b''), (Opcode.NIP, b''), (Opcode.NIP, b''), (Opcode.NIP, b''), ] # endregion return (verify_base + verify_code_literal + ascii_to_int + clean_stack)
def _verify_is_specific_base(self, char: str, base: int) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.StackItem import StackItemType jmp_place_holder = (Opcode.JMP, b'\x01') verify_top_stack = [ # verify if True is in the top of the stack jmp_place_holder ] top_stack_is_true = [ # if True is indeed at the top, ignore the rest of this method (Opcode.PUSH1, b''), jmp_place_holder # jumps everything below ] compare_char = [ # compares the char with b, B, o, O, x, or X (Opcode.DUP, b''), Opcode.get_pushdata_and_data(char), jmp_place_holder ] char_not_equal = [ # if char at the top of the stack is not b, B, o, O, x, or X, (Opcode.PUSH0, b''), # put False at the top of the stack jmp_place_holder # jumps everything below ] verify_base_is_the_same = [ # verify if the equivalent base is the same as current base (Opcode.PUSH2, b''), (Opcode.PICK, b''), Opcode.get_push_and_data(base), jmp_place_holder # jump to remove_base_char if equivalent base is the same as current base ] verify_base_is_0 = [ # verify if the current base is 0 (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.PUSH0, b''), jmp_place_holder # if base is not 0, jump to put_true_in_stack ] change_base = [ # if base was 0, then change it to the equivalent base (Opcode.REVERSE3, b''), (Opcode.DROP, b''), Opcode.get_push_and_data(base), (Opcode.REVERSE3, b''), ] remove_base_char = [ # if base was 0 or 16, remove the char in value (Opcode.SWAP, b''), (Opcode.PUSH1, b''), (Opcode.OVER, b''), (Opcode.SIZE, b''), (Opcode.DEC, b''), (Opcode.SUBSTR, b''), (Opcode.CONVERT, StackItemType.ByteString), (Opcode.SWAP, b''), ] put_true_in_stack = [ # puts True in the top of the stack to skip the next verifications (Opcode.PUSH1, b'') ] # region jmp logic jump_instructions = change_base + remove_base_char verify_base_is_0[-1] = Opcode.get_jump_and_data( Opcode.JMPNE, get_bytes_count(jump_instructions), True) jump_instructions = change_base + verify_base_is_0 verify_base_is_the_same[-1] = Opcode.get_jump_and_data( Opcode.JMPEQ, get_bytes_count(jump_instructions), True) jump_instructions = change_base + remove_base_char + put_true_in_stack + verify_base_is_0 + verify_base_is_the_same char_not_equal[-1] = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(jump_instructions), True) jump_instructions = char_not_equal compare_char[-1] = Opcode.get_jump_and_data( Opcode.JMPEQ, get_bytes_count(jump_instructions), True) jump_instructions = (compare_char + char_not_equal + change_base + put_true_in_stack + verify_base_is_the_same + verify_base_is_0 + remove_base_char) top_stack_is_true[-1] = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(jump_instructions), True) jump_instructions = top_stack_is_true verify_top_stack[-1] = Opcode.get_jump_and_data( Opcode.JMPIFNOT, get_bytes_count(jump_instructions), True) # endregion return (verify_top_stack + top_stack_is_true + compare_char + char_not_equal + verify_base_is_the_same + verify_base_is_0 + change_base + remove_base_char + put_true_in_stack)
def opcode(self) -> List[Tuple[Opcode, bytes]]: value = NEO_SCRIPT return [Opcode.get_pushdata_and_data(value)]
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.Integer import Integer from boa3.neo.vm.type.StackItem import StackItemType number0 = Integer(ord('0')).to_byte_array() number9 = Integer(ord('9')).to_byte_array() jmp_place_holder = (Opcode.JMP, b'\x01') initializing = [ # initialize auxiliary values (Opcode.DUP, b''), (Opcode.SIZE, b''), (Opcode.DEC, b''), # index = len(string) - 1 (Opcode.PUSH1, b''), # isdigit = True ] verify_empty_string = [ # verifies if string is empty (Opcode.OVER, b''), (Opcode.PUSHM1, b''), jmp_place_holder, # jump to change_to_false if index == -1 ] skip_first_verify_while = [ # skips the first while verification, since string is not empty jmp_place_holder ] verify_while = [ # verifies if while is over (Opcode.OVER, b''), (Opcode.PUSH0, b''), jmp_place_holder, # jump to return_bool if index >= len(string) ] jmp_verify_while = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(verify_while), True) skip_first_verify_while[-1] = jmp_verify_while while_verify_lt_0 = [ # verifies if ord(string[index]) is < ord('0') (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.PUSH1, b''), (Opcode.SUBSTR, b''), (Opcode.CONVERT, StackItemType.ByteString), (Opcode.DUP, b''), (Opcode.PUSHDATA1, Integer(len(number0)).to_byte_array() + number0), jmp_place_holder, # if ord(string[index]) < ord('0'), return False ] while_verify_gt_9 = [ # verifies if ord(string[index]) is > ord('9') (Opcode.PUSHDATA1, Integer(len(number9)).to_byte_array() + number9), jmp_place_holder, # if ord(string[index]) > ord('9'), return False ] while_go_to_verify = [ # decreases index and goes back to verify if there all characters were visited already (Opcode.SWAP, b''), (Opcode.DEC, b''), # index-- (Opcode.SWAP, b''), # jump back to verify_while ] jmp_back_to_verify = Opcode.get_jump_and_data( Opcode.JMP, -get_bytes_count(verify_while + while_verify_lt_0 + while_verify_gt_9 + while_go_to_verify)) while_go_to_verify.append(jmp_back_to_verify) jmp_out_of_while = Opcode.get_jump_and_data( Opcode.JMPLT, get_bytes_count(while_verify_gt_9 + while_go_to_verify), True) while_verify_lt_0[-1] = jmp_out_of_while drop_char = [ # remove extra char from stack (Opcode.DROP, b'') ] jmp_to_change_to_false = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(while_go_to_verify + drop_char), True) while_verify_gt_9[-1] = jmp_to_change_to_false change_to_false = [ # remove True on top of stack and put False (Opcode.DROP, b''), (Opcode.PUSH0, b''), ] jmp_to_return = Opcode.get_jump_and_data( Opcode.JMPEQ, get_bytes_count(skip_first_verify_while + verify_while + while_verify_lt_0 + while_verify_gt_9 + while_go_to_verify + drop_char), True) verify_empty_string[-1] = jmp_to_return jmp_to_return = Opcode.get_jump_and_data( Opcode.JMPLT, get_bytes_count(while_verify_lt_0 + while_verify_gt_9 + while_go_to_verify + drop_char + change_to_false), True) verify_while[-1] = jmp_to_return clean_and_return_bool = [ # remove extra values from stack (Opcode.NIP, b''), (Opcode.NIP, b'') ] return (initializing + verify_empty_string + skip_first_verify_while + verify_while + while_verify_lt_0 + while_verify_gt_9 + while_go_to_verify + drop_char + change_to_false + clean_and_return_bool)
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.neo.vm.type.StackItem import StackItemType from boa3.compiler.codegenerator import get_bytes_count jmp_place_holder = (Opcode.JMP, b'\x01') verify_is_none = [ # verify if value is None (Opcode.DUP, b''), (Opcode.ISNULL, b''), jmp_place_holder ] value_is_none = [ # if value is None, then return False (Opcode.DROP, b''), (Opcode.PUSH0, b''), jmp_place_holder ] verify_is_array = [ # if value is an array, check the length of it (Opcode.DUP, b''), (Opcode.ISTYPE, StackItemType.Array), jmp_place_holder ] verify_is_map = [ # if value is a map, check the length of it (Opcode.DUP, b''), (Opcode.ISTYPE, StackItemType.Map), jmp_place_holder ] verify_is_int = [ # if value is an int, check if it is non zero (Opcode.DUP, b''), (Opcode.ISTYPE, StackItemType.Integer), jmp_place_holder ] verify_is_bytestring = [ # if value is a bytestring, check if it is non zero (Opcode.DUP, b''), (Opcode.ISTYPE, StackItemType.ByteString), jmp_place_holder ] put_true = [ # if value it's not a type from above, return True (Opcode.DROP, b''), (Opcode.PUSH1, b''), jmp_place_holder ] collection_non_zero = [ # get length of collection (Opcode.SIZE, b''), ] non_zero = [ # verify if it's non zero (Opcode.NZ, b''), ] # region jump logic jump_instructions = collection_non_zero + non_zero put_true[-1] = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(jump_instructions), True) jump_instructions = collection_non_zero + put_true verify_is_bytestring[-1] = Opcode.get_jump_and_data( Opcode.JMPIF, get_bytes_count(jump_instructions), True) jump_instructions = collection_non_zero + put_true + verify_is_bytestring verify_is_int[-1] = Opcode.get_jump_and_data( Opcode.JMPIF, get_bytes_count(jump_instructions), True) jump_instructions = put_true + verify_is_int + verify_is_bytestring verify_is_map[-1] = Opcode.get_jump_and_data( Opcode.JMPIF, get_bytes_count(jump_instructions), True) jump_instructions = verify_is_map + verify_is_int + verify_is_bytestring + put_true verify_is_array[-1] = Opcode.get_jump_and_data( Opcode.JMPIF, get_bytes_count(jump_instructions), True) jump_instructions = (verify_is_array + verify_is_map + verify_is_int + verify_is_bytestring + put_true + collection_non_zero + non_zero) value_is_none[-1] = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(jump_instructions), True) jump_instructions = value_is_none verify_is_none[-1] = Opcode.get_jump_and_data( Opcode.JMPIFNOT, get_bytes_count(jump_instructions), True) # endregion bool_method = (verify_is_none + value_is_none + verify_is_array + verify_is_map + verify_is_int + verify_is_bytestring + put_true + collection_non_zero + non_zero) return bool_method
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.StackItem import StackItemType jmp_place_holder = (Opcode.JMP, b'\x01') message = String("substring not found").to_bytes() # receives: end, start, substr, str verify_negative_index = [ # verifies if index in a negative value (Opcode.DUP, b''), (Opcode.PUSHM1, b''), jmp_place_holder # if index >= 0, jump to verify_big_end or verify_big_start ] fix_negative_end = [ # gets the correspondent positive value of end (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), (Opcode.ADD, b''), (Opcode.INC, b''), # end = end + len(str) + 1 (Opcode.DUP, b''), (Opcode.PUSHM1, b''), jmp_place_holder # if end is not negative anymore, start verifying start ] fix_still_negative_index = [ # if index is still negative, consider it 0 then (Opcode.DROP, b''), (Opcode.PUSH0, b''), # end = 0 ] jmp_fix_negative_index = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(fix_negative_end + fix_still_negative_index), True) verify_negative_index[-1] = jmp_fix_negative_index verify_big_end = [ # verify if end is bigger then len(str) (Opcode.DUP, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), jmp_place_holder # if end <= len(str), start verifying start ] fix_big_end = [ # consider end as len(str) (Opcode.DROP, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), # end = len(str) ] jmp_other_verifies = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(fix_still_negative_index + verify_big_end + fix_big_end), True) fix_negative_end[-1] = jmp_other_verifies jmp_fix_big_index = Opcode.get_jump_and_data( Opcode.JMPLE, get_bytes_count(fix_big_end), True) verify_big_end[-1] = jmp_fix_big_index verify_and_fix_end = [ # collection of Opcodes regarding verifying and fixing end index (Opcode.REVERSE4, b''), ] verify_and_fix_end.extend(verify_negative_index) verify_and_fix_end.extend(fix_negative_end) verify_and_fix_end.extend(fix_still_negative_index) verify_and_fix_end.extend(verify_big_end) verify_and_fix_end.extend(fix_big_end) verify_and_fix_start = [ # collection of Opcodes regarding verifying and fixing start index (Opcode.SWAP, b'') ] verify_and_fix_start.extend(verify_negative_index) verify_and_fix_start.extend(fix_negative_end) verify_and_fix_start.extend(fix_still_negative_index) verify_and_fix_start.extend(verify_big_end) verify_and_fix_start.extend(fix_big_end) change_stack_order = [ # change order of items on the stack (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), # size = len(substr) (Opcode.ROT, b''), (Opcode.ROT, b''), ] verify_while = [ # verify already compared all that was need (Opcode.OVER, b''), (Opcode.OVER, b''), (Opcode.SUB, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), jmp_place_holder # if end - index >= size, jump to not_inside_sequence ] compare_item = [ # compare str[index:index+size] with substr (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.OVER, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.SUBSTR, b''), (Opcode.CONVERT, StackItemType.ByteString), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.NUMEQUAL, b''), jmp_place_holder # if str[index:index+size] == substr, return index ] not_found = [ # increments index and goes back to verify again (Opcode.INC, b''), # index++ # jump to verify_while ] jmp_back_to_verify = Opcode.get_jump_and_data( Opcode.JMP, -get_bytes_count(verify_while + compare_item + not_found), True) not_found.append(jmp_back_to_verify) jmp_to_error = Opcode.get_jump_and_data( Opcode.JMPLT, get_bytes_count(compare_item + not_found), True) verify_while[-1] = jmp_to_error not_inside_sequence = [ # send error message saying that substring not found (Opcode.PUSHDATA1, Integer(len(message)).to_byte_array(signed=True, min_length=1) + message), (Opcode.THROW, b''), ] jmp_to_return_index = Opcode.get_jump_and_data( Opcode.JMPIF, get_bytes_count(not_found + not_inside_sequence), True) compare_item[-1] = jmp_to_return_index return_index = [ # removes all values in the stack but the index (Opcode.NIP, b''), (Opcode.NIP, b''), (Opcode.NIP, b''), (Opcode.NIP, b''), ] return (verify_and_fix_end + verify_and_fix_start + change_stack_order + verify_while + compare_item + not_found + not_inside_sequence + return_index)
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count jmp_place_holder = (Opcode.JMP, b'\x01') message = String("x not in sequence").to_bytes() # receives: end, start, x, sequence verify_negative_index = [ # verifies if index in a negative value (Opcode.DUP, b''), (Opcode.PUSHM1, b''), jmp_place_holder # if index >= 0, jump to verify_big_end or verify_big_start ] fix_negative_end = [ # gets the correspondent positive value of end (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), (Opcode.ADD, b''), (Opcode.INC, b''), # end = end + len(sequence) + 1 (Opcode.DUP, b''), (Opcode.PUSHM1, b''), jmp_place_holder # if end is not negative anymore, start verifying start ] fix_still_negative_index = [ # if index is still negative, consider it 0 then (Opcode.DROP, b''), (Opcode.PUSH0, b''), # end = 0 ] jmp_fix_negative_index = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(fix_negative_end + fix_still_negative_index), True) verify_negative_index[-1] = jmp_fix_negative_index verify_big_end = [ # verify if end is bigger then len(sequence) (Opcode.DUP, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), jmp_place_holder # if end <= len(sequence), start verifying start ] fix_big_end = [ # consider end as len(sequence) (Opcode.DROP, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), # end = len(sequence) ] jmp_other_verifies = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(fix_still_negative_index + verify_big_end + fix_big_end), True) fix_negative_end[-1] = jmp_other_verifies jmp_fix_big_index = Opcode.get_jump_and_data( Opcode.JMPLE, get_bytes_count(fix_big_end), True) verify_big_end[-1] = jmp_fix_big_index verify_and_fix_end = [ # collection of Opcodes regarding verifying and fixing end index (Opcode.REVERSE4, b''), ] verify_and_fix_end.extend(verify_negative_index) verify_and_fix_end.extend(fix_negative_end) verify_and_fix_end.extend(fix_still_negative_index) verify_and_fix_end.extend(verify_big_end) verify_and_fix_end.extend(fix_big_end) verify_and_fix_start = [ # collection of Opcodes regarding verifying and fixing start index (Opcode.SWAP, b'') ] verify_and_fix_start.extend(verify_negative_index) verify_and_fix_start.extend(fix_negative_end) verify_and_fix_start.extend(fix_still_negative_index) verify_and_fix_start.extend(verify_big_end) verify_and_fix_start.extend(fix_big_end) verify_while = [ # verify if already went through all items on the sequence[start:end] (Opcode.OVER, b''), # index = start (Opcode.OVER, b''), jmp_place_holder # if index <= start, jump to not_inside_sequence ] compare_item = [ # verifies if x is in sequence (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.OVER, b''), (Opcode.PICKITEM, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.NUMEQUAL, b''), # found_x = sequence[index] == x jmp_place_holder # if found_x, jump to return_index ] not_found = [ # increments index and goes back to verify again (Opcode.INC, b''), # index++ # jump to verify_while ] jmp_back_to_verify = Opcode.get_jump_and_data( Opcode.JMP, -get_bytes_count(verify_while + compare_item + not_found), True) not_found.append(jmp_back_to_verify) jmp_to_error = Opcode.get_jump_and_data( Opcode.JMPLE, get_bytes_count(compare_item + not_found), True) verify_while[-1] = jmp_to_error not_inside_sequence = [ # send error message saying that x is not in sequence (Opcode.PUSHDATA1, Integer(len(message)).to_byte_array(signed=True, min_length=1) + message), (Opcode.THROW, b''), ] jmp_to_return_index = Opcode.get_jump_and_data( Opcode.JMPIF, get_bytes_count(not_found + not_inside_sequence), True) compare_item[-1] = jmp_to_return_index return_index = [ # removes all values in the stack but the index (Opcode.NIP, b''), (Opcode.NIP, b''), (Opcode.NIP, b''), ] return (verify_and_fix_end + verify_and_fix_start + verify_while + compare_item + not_found + not_inside_sequence + return_index)
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.StackItem import StackItemType from boa3.neo.vm.type.Integer import Integer upper_a = Integer(ord('A')).to_byte_array() upper_z = Integer(ord('Z')).to_byte_array() jmp_place_holder = (Opcode.JMP, b'\x01') initializing = [ # initialize auxiliary values (Opcode.DUP, b''), (Opcode.SIZE, b''), (Opcode.PUSH0, b''), # index = 0 ] verify_while = [ # verifies if while is over (Opcode.OVER, b''), (Opcode.OVER, b''), jmp_place_holder, # jump to last statement to clean the stack if index >= len(string) ] get_substring_left = [ # gets the substring to the left of the index (Opcode.REVERSE3, b''), (Opcode.DUP, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.OVER, b''), (Opcode.OVER, b''), (Opcode.LEFT, b''), # substr_left = string[:index] (Opcode.CONVERT, StackItemType.ByteString), (Opcode.ROT, b''), (Opcode.ROT, b''), ] get_substring_middle = [ # gets the substring on the index ( Opcode.OVER, b'' ), # TODO: verify if string[index] < c0 when other values are implemented (Opcode.OVER, b''), ( Opcode.PUSH1, b'' ), # modifier = 1, since using upper is only supported with ASCII for now (Opcode.SUBSTR, b''), # substr_middle = string[index:index+modifier] (Opcode.CONVERT, StackItemType.ByteString), (Opcode.DUP, b''), (Opcode.PUSHDATA1, Integer(len(upper_a)).to_byte_array() + upper_a), jmp_place_holder, # jump to get the substring to the right if substr_middle value is lower than 'A' ] verify_greater_than_z = [ # verifies if substr_middle is between 'A' and 'Z' (Opcode.DUP, b''), (Opcode.PUSHDATA1, Integer(len(upper_z)).to_byte_array() + upper_z), jmp_place_holder, # jump to get the substring to the right if substr_middle value is greater than 'Z' ] swap_upper_to_lower_case = [ # change middle_substr to lowercase equivalent (Opcode.PUSHINT8, Integer(32).to_byte_array(signed=True)), (Opcode.ADD, b''), (Opcode.CONVERT, StackItemType.ByteString), ] jmp_to_join_substring = Opcode.get_jump_and_data( Opcode.JMPLT, get_bytes_count(verify_greater_than_z + swap_upper_to_lower_case), True) get_substring_middle[-1] = jmp_to_join_substring jmp_to_join_substring = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(swap_upper_to_lower_case), True) verify_greater_than_z[-1] = jmp_to_join_substring get_substring_middle.extend(verify_greater_than_z) get_substring_middle.extend(swap_upper_to_lower_case) get_substring_right = [ # gets the substring to the right of the index (Opcode.ROT, b''), (Opcode.ROT, b''), (Opcode.INC, b''), (Opcode.NEGATE, b''), (Opcode.OVER, b''), (Opcode.SIZE, b''), (Opcode.ADD, b''), (Opcode.RIGHT, b''), # substr_right = string[index+modifier:] ] join_substrings = [ # concatenate substr_left, substr_middle, substr_right (Opcode.CAT, b''), (Opcode.CAT, b''), # substr_left + substr_middle + substr_right (Opcode.NIP, b''), (Opcode.CONVERT, StackItemType.ByteString), (Opcode.REVERSE3, b''), (Opcode.INC, b''), # index ++ # jump back to verify, ] jmp_to_verify_while = Opcode.get_jump_and_data( Opcode.JMP, -get_bytes_count(verify_while + get_substring_left + get_substring_middle + get_substring_right + join_substrings)) join_substrings.append(jmp_to_verify_while) clean_stack = [ # removes all auxiliary values (Opcode.DROP, b''), (Opcode.DROP, b''), ] while_body = (get_substring_left + get_substring_middle + get_substring_right + join_substrings) jmp_to_clean_stack = Opcode.get_jump_and_data( Opcode.JMPLE, get_bytes_count(while_body), True) verify_while[-1] = jmp_to_clean_stack return (initializing + verify_while + while_body + clean_stack)
def opcode(self) -> List[Tuple[Opcode, bytes]]: from boa3.compiler.codegenerator import get_bytes_count from boa3.neo.vm.type.StackItem import StackItemType jmp_place_holder = (Opcode.JMP, b'\x01') # string, end, start, substring verify_negative_index = [ # verify if index is negative value (Opcode.DUP, b''), (Opcode.PUSHM1, b''), jmp_place_holder # if not index < 0, jump to verify_big_end ] fix_negative_end = [ # fix negative value to a positive equivalent (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), (Opcode.ADD, b''), (Opcode.INC, b''), # end = end + len(string) + 1 (Opcode.DUP, b''), (Opcode.PUSHM1, b''), jmp_place_holder # if not end < 0, jump to verify_and_fix_start ] fix_still_negative_index = [ # if end still is less than 0, then it should be 0 (Opcode.DROP, b''), (Opcode.PUSH0, b''), # end = 0 ] jmp_fix_negative_index = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(fix_negative_end + fix_still_negative_index), True) verify_negative_index[-1] = jmp_fix_negative_index verify_big_end = [ # verify if end is greater or equals to len(string) (Opcode.DUP, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), jmp_place_holder # if not end >= len(string), jump to verify_and_fix_start ] fix_big_end = [ # fix end value to a positive equivalent (Opcode.DROP, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), # end = len(string) ] jmp_other_verifies = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(fix_still_negative_index + verify_big_end + fix_big_end), True) fix_negative_end[-1] = jmp_other_verifies jmp_fix_big_index = Opcode.get_jump_and_data( Opcode.JMPLE, get_bytes_count(fix_big_end), True) verify_big_end[-1] = jmp_fix_big_index verify_and_fix_end = [ # verify and fix end index (Opcode.REVERSE3, b''), # change positions on stack ] verify_and_fix_end.extend(verify_negative_index) verify_and_fix_end.extend(fix_negative_end) verify_and_fix_end.extend(fix_still_negative_index) verify_and_fix_end.extend(verify_big_end) verify_and_fix_end.extend(fix_big_end) verify_and_fix_start = [ # verify and fix end index (Opcode.SWAP, b''), # change positions on stack ] # fix_negative_start is the same as fix_negative_end, but jump is different fix_negative_start = fix_negative_end.copy() verify_big_start = [ # verify if start is greater or equals to len(string) (Opcode.DUP, b''), (Opcode.PUSH4, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), jmp_place_holder # if start >= len(string), return False ] jmp_other_verifies = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(fix_still_negative_index + verify_big_start), True) fix_negative_end[-1] = jmp_other_verifies verify_size = [ # verify if len(string[start:end]) > len(substring) (Opcode.REVERSE3, b''), (Opcode.REVERSE4, b''), (Opcode.REVERSE3, b''), (Opcode.OVER, b''), (Opcode.SUB, b''), (Opcode.SUBSTR, b''), (Opcode.PUSH0, b''), (Opcode.PUSH2, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), (Opcode.DUP, b''), (Opcode.PUSH3, b''), (Opcode.PICK, b''), (Opcode.SIZE, b''), jmp_place_holder # if len(string[start:end]) > len(substring), return False ] compare_starts = [ # string.startswith(substring) (Opcode.SUBSTR, b''), (Opcode.CONVERT, StackItemType.ByteString), (Opcode.NUMEQUAL, b''), # return string[start:end][0:len(substring)] == substring jmp_place_holder # jumps other opcodes ] jmp_compare_starts = Opcode.get_jump_and_data( Opcode.JMPGT, get_bytes_count(compare_starts), True) verify_size[-1] = jmp_compare_starts jmp_to_false = Opcode.get_jump_and_data( Opcode.JMPGE, get_bytes_count(verify_size + compare_starts), True) verify_big_start[-1] = jmp_to_false verify_and_fix_start.extend(verify_negative_index) verify_and_fix_start.extend(fix_negative_start) verify_and_fix_start.extend(fix_still_negative_index) verify_and_fix_start.extend(verify_big_start) return_false = [ # remove all values from stack and put False (Opcode.DROP, b''), (Opcode.DROP, b''), (Opcode.DROP, b''), (Opcode.DROP, b''), (Opcode.PUSH0, b''), # return False ] jmp_bigger_substr = Opcode.get_jump_and_data( Opcode.JMP, get_bytes_count(return_false), True) compare_starts[-1] = jmp_bigger_substr return (verify_and_fix_end + verify_and_fix_start + verify_size + compare_starts + return_false)