Example #1
0
    def get_load(index: int, local: bool, is_arg: bool = False) -> Opcode:
        """
        Gets the opcode to load the variable

        :param index: index of the variable
        :param local: identifies if the variable is local or global
        :param is_arg: identifies if the variable is an argument of a function. False if local is False.
        :return: the respective opcode
        :rtype: Opcode
        """
        if not local:
            is_arg = False

        if 0 <= index <= 6:
            if is_arg:
                opcode_value: int = Integer.from_bytes(Opcode.LDARG0) + index
            elif local:
                opcode_value: int = Integer.from_bytes(Opcode.LDLOC0) + index
            else:
                opcode_value: int = Integer.from_bytes(Opcode.LDSFLD0) + index
            return Opcode(Integer(opcode_value).to_byte_array())
        else:
            if is_arg:
                return Opcode.LDARG
            elif local:
                return Opcode.LDLOC
            else:
                return Opcode.LDSFLD
Example #2
0
    def test_ambiguous_integer_constant(self):
        byte_input = b'\x00\x80'
        unsigned = Integer.from_bytes(byte_input, signed=False)
        signed = Integer.from_bytes(byte_input, signed=True)
        self.assertNotEqual(unsigned, signed)

        unsigned_byte_input = Integer(unsigned).to_byte_array(signed=True)
        self.assertNotEqual(byte_input, unsigned_byte_input)
        unsigned_expected_output = (
            Opcode.PUSHDATA1  # push the bytes
            + Integer(len(unsigned_byte_input)).to_byte_array(min_length=1) +
            unsigned_byte_input + Opcode.CONVERT  # convert to integer
            + Type.int.stack_item)

        generator = self.build_code_generator()
        generator.convert_integer_literal(unsigned)
        output = generator.bytecode

        self.assertEqual(unsigned_expected_output, output)

        signed_byte_input = Integer(signed).to_byte_array(signed=True)
        self.assertEqual(byte_input, signed_byte_input)
        signed_expected_output = (
            Opcode.PUSHDATA1  # push the bytes
            + Integer(len(signed_byte_input)).to_byte_array(min_length=1) +
            signed_byte_input + Opcode.CONVERT  # convert to integer
            + Type.int.stack_item)

        generator = self.build_code_generator()
        generator.convert_integer_literal(signed)
        output = generator.bytecode

        self.assertEqual(signed_expected_output, output)
Example #3
0
    def test_ambiguous_integer_constant(self):
        byte_input = b'\x00\x80'
        unsigned = Integer.from_bytes(byte_input, signed=False)
        signed = Integer.from_bytes(byte_input, signed=True)
        self.assertNotEqual(unsigned, signed)

        unsigned_byte_input = Integer(unsigned).to_byte_array(signed=True,
                                                              min_length=4)
        self.assertNotEqual(byte_input, unsigned_byte_input)
        unsigned_expected_output = (
            Opcode.PUSHINT32  # push the bytes
            + unsigned_byte_input)

        generator = self.build_code_generator()
        generator.convert_integer_literal(unsigned)
        output = generator.bytecode

        self.assertEqual(unsigned_expected_output, output)

        signed_byte_input = Integer(signed).to_byte_array(signed=True,
                                                          min_length=2)
        self.assertEqual(byte_input, signed_byte_input)
        signed_expected_output = (
            Opcode.PUSHINT16  # push the bytes
            + signed_byte_input)

        generator = self.build_code_generator()
        generator.convert_integer_literal(signed)
        output = generator.bytecode

        self.assertEqual(signed_expected_output, output)
Example #4
0
    def get_push_and_data(integer: int) -> Tuple[Opcode, bytes]:
        """
        Gets the push opcode and data to the respective integer

        :param integer: value that will be pushed
        :return: the respective opcode and its required data
        :rtype: Tuple[Opcode, bytes]
        """
        if -1 <= integer <= 16:
            opcode_value: int = Integer.from_bytes(Opcode.PUSH0) + integer
            return Opcode(Integer(opcode_value).to_byte_array()), b''
        else:
            data = Integer(integer).to_byte_array(signed=True, min_length=1)
            if len(data) == 1:
                opcode = Opcode.PUSHINT8
            elif len(data) == 2:
                opcode = Opcode.PUSHINT16
            elif len(data) <= 4:
                data = Integer(integer).to_byte_array(signed=True,
                                                      min_length=4)
                opcode = Opcode.PUSHINT32
            elif len(data) <= 8:
                data = Integer(integer).to_byte_array(signed=True,
                                                      min_length=8)
                opcode = Opcode.PUSHINT64
            elif len(data) <= 16:
                data = Integer(integer).to_byte_array(signed=True,
                                                      min_length=16)
                opcode = Opcode.PUSHINT128
            else:
                data = data[:32]
                opcode = Opcode.PUSHINT256

            return opcode, data
Example #5
0
    def test_boa2_storage_test2(self):
        path = self.get_contract_path('StorageBoa2Test.py')
        engine = TestEngine()

        result = self.run_smart_contract(engine, path, 'main', 'sget', 100,
                                         10000000000)
        if isinstance(result, str):
            result = String(result).to_bytes()
        self.assertEqual(b'', result)

        result = self.run_smart_contract(engine, path, 'main', 'sput', 100,
                                         10000000000)
        self.assertEqual(True, result)

        result = self.run_smart_contract(engine, path, 'main', 'sget', 100,
                                         10000000000)
        if isinstance(result, bytes):
            result = Integer.from_bytes(result)
        self.assertEqual(10000000000, result)

        result = self.run_smart_contract(engine, path, 'main', 'sdel', 100,
                                         10000000000)
        self.assertEqual(True, result)

        result = self.run_smart_contract(engine, path, 'main', 'sget', 100,
                                         10000000000)
        if isinstance(result, str):
            result = String(result).to_bytes()
        self.assertEqual(b'', result)
Example #6
0
    def test_while_interop_condition(self):
        path = self.get_contract_path('WhileWithInteropCondition.py')

        engine = TestEngine()
        result = self.run_smart_contract(engine, path, 'deploy')
        self.assertEqual(True, result)
        contract_hash = engine.executed_script_hash.to_array()
        result = self.run_smart_contract(engine, path, 'test_end_while_jump')
        self.assertEqual(True, result)

        # test notifications inserted into the code for validating if the code flow is correct
        notifications = engine.get_events('notify', origin=contract_hash)
        self.assertEqual(2, len(notifications))

        self.assertEqual(1, len(notifications[0].arguments))
        self.assertIsInstance(notifications[0].arguments[0], list)
        self.assertEqual(4, len(notifications[0].arguments[0]))
        token, executing_script_hash, fee_receiver, fee_amount = notifications[
            0].arguments[0]
        if isinstance(fee_receiver, str):
            fee_receiver = String(fee_receiver).to_bytes()
        if isinstance(fee_amount, str):
            fee_amount = String(fee_amount).to_bytes()
        if isinstance(fee_amount, bytes):
            fee_amount = Integer.from_bytes(fee_amount)

        self.assertEqual(constants.GAS_SCRIPT, token)
        self.assertEqual(contract_hash, executing_script_hash)
        self.assertEqual(bytes(20), fee_receiver)
        self.assertEqual(10, fee_amount)

        self.assertEqual(1, len(notifications[1].arguments))
        self.assertIsInstance(notifications[1].arguments[0], list)
        self.assertEqual(4, len(notifications[1].arguments[0]))
        token, executing_script_hash, fee_receiver, fee_amount = notifications[
            1].arguments[0]
        if isinstance(fee_receiver, str):
            fee_receiver = String(fee_receiver).to_bytes()
        if isinstance(fee_amount, str):
            fee_amount = String(fee_amount).to_bytes()
        if isinstance(fee_amount, bytes):
            fee_amount = Integer.from_bytes(fee_amount)

        self.assertEqual(constants.NEO_SCRIPT, token)
        self.assertEqual(contract_hash, executing_script_hash)
        self.assertEqual(bytes(20), fee_receiver)
        self.assertEqual(20, fee_amount)
Example #7
0
 def _update_targets(self):
     from boa3.neo.vm.type.Integer import Integer
     for address, code in self.code_map.items():
         if code.opcode.has_target() and code.target is None:
             relative = Integer.from_bytes(code.data)
             absolute = address + relative
             if absolute in self.code_map:
                 code.set_target(self.code_map[absolute])
Example #8
0
    def get_store_from_load(load_opcode) -> Optional[Opcode]:
        """
        Gets the store slot opcode equivalent to the given load slot opcode.

        :param load_opcode: load opcode
        :type load_opcode: Opcode
        :return: equivalent store opcode if the given opcode is a load slot. Otherwise, returns None
        :rtype: Opcode or None
        """
        if load_opcode.is_load_slot:
            opcode_value: int = Integer.from_bytes(load_opcode) + 8
            return Opcode(Integer(opcode_value).to_byte_array())
Example #9
0
    def test_big_integer_constant(self):
        byte_input = bytes(100000) + b'\x01'
        input = Integer.from_bytes(byte_input)
        expected_output = (
            Opcode.PUSHINT256  # push the bytes
            + byte_input[:32])

        generator = self.build_code_generator()
        generator.convert_integer_literal(input)
        output = generator.bytecode

        self.assertEqual(expected_output, output)
Example #10
0
    def run_smart_contract(self,
                           test_engine: TestEngine,
                           smart_contract_path: Union[str, bytes],
                           method: str,
                           *arguments: Any,
                           reset_engine: bool = False,
                           fake_storage: Dict[str, Any] = None,
                           signer_accounts: Iterable[bytes] = (),
                           expected_result_type: Type = None,
                           rollback_on_fault: bool = True) -> Any:

        if isinstance(smart_contract_path,
                      str) and smart_contract_path.endswith('.py'):
            if not (os.path.isfile(smart_contract_path.replace('.py', '.nef'))
                    and os.path.isfile(
                        smart_contract_path.replace('.py', '.manifest.json'))):
                # both .nef and .manifest.json are required to execute the smart contract
                self.compile_and_save(smart_contract_path, log=False)
            smart_contract_path = smart_contract_path.replace('.py', '.nef')
        elif isinstance(smart_contract_path, bytes):
            from boa3.neo3.core.types import UInt160
            smart_contract_path = UInt160(smart_contract_path)

        if isinstance(fake_storage, dict):
            test_engine.set_storage(fake_storage)

        for account in signer_accounts:
            test_engine.add_signer_account(account)

        result = test_engine.run(smart_contract_path,
                                 method,
                                 *arguments,
                                 reset_engine=reset_engine,
                                 rollback_on_fault=rollback_on_fault)

        if test_engine.vm_state is not VMState.HALT and test_engine.error is not None:
            raise TestExecutionException(test_engine.error)

        if expected_result_type is not None:
            if expected_result_type is not str and isinstance(result, str):
                result = String(result).to_bytes()

            if expected_result_type is bool:
                if isinstance(result, bytes):
                    result = Integer.from_bytes(result, signed=True)
                if isinstance(result, int) and result in (False, True):
                    result = bool(result)

            if expected_result_type is bytearray and isinstance(result, bytes):
                result = bytearray(result)

        return result
Example #11
0
    def get_literal_push(integer: int) -> Optional[Opcode]:
        """
        Gets the push opcode to the respective integer

        :param integer: value that will be pushed
        :return: the respective opcode
        :rtype: Opcode or None
        """
        if -1 <= integer <= 16:
            opcode_value: int = Integer.from_bytes(Opcode.PUSH0) + integer
            return Opcode(Integer(opcode_value).to_byte_array())
        else:
            return None
Example #12
0
    def test_big_integer_constant(self):
        byte_input = bytes(100000) + b'\x01'
        input = Integer.from_bytes(byte_input)
        expected_output = (
            Opcode.PUSHDATA4  # push the bytes
            + Integer(len(byte_input)).to_byte_array(min_length=4) +
            byte_input + Opcode.CONVERT  # convert to integer
            + Type.int.stack_item)

        generator = self.build_code_generator()
        generator.convert_integer_literal(input)
        output = generator.bytecode

        self.assertEqual(expected_output, output)
Example #13
0
def deserialize_binary(reader: BinaryReader) -> Any:
    deserialized_items = []
    underserialized = 1

    while underserialized > 0:
        stack_type = reader.read_byte()
        if stack_type == StackItemType.Any:
            deserialized_items.append(None)
        elif stack_type == StackItemType.Boolean:
            deserialized_items.append(reader.read_bool())
        elif stack_type == StackItemType.Integer:
            value_in_bytes = reader.read_var_bytes()
            deserialized_items.append(Integer.from_bytes(value_in_bytes))
        elif stack_type in (StackItemType.ByteString, StackItemType.Buffer):
            deserialized_items.append(reader.read_var_bytes())
        elif stack_type in (StackItemType.Array, StackItemType.Struct):
            count = reader.read_var_int()
            deserialized_items.append(list(range(count)))
            underserialized += count
        elif stack_type == StackItemType.Map:
            count = reader.read_var_int()
            deserialized_items.append({key: None for key in range(count)})
            underserialized += count * 2
        else:
            raise ValueError

        underserialized -= 1

    stack_temp = []
    while len(deserialized_items) > 0:
        item = deserialized_items.pop()
        if isinstance(item, list):
            new_item = []
            for _ in range(len(item)):
                new_item.append(stack_temp.pop())
            item = new_item
        elif isinstance(item, dict):
            new_item = {}
            for _ in range(0, len(item), 2):
                key = stack_temp.pop()
                value = stack_temp.pop()
                new_item[key] = value
            item = new_item
        stack_temp.append(item)

    return stack_temp.pop()
Example #14
0
    def test_mixed_inequality_operation(self):
        expected_output = (Opcode.INITSLOT + b'\x00' + b'\x02' +
                           Opcode.LDARG0 + Opcode.LDARG1 + Opcode.NOTEQUAL +
                           Opcode.RET)

        path = '%s/boa3_test/test_sc/relational_test/MixedInequality.py' % self.dirname
        output = Boa3.compile(path)
        self.assertEqual(expected_output, output)

        engine = TestEngine(self.dirname)
        result = self.run_smart_contract(engine, path, 'Main', 1, 'unit')
        self.assertEqual(True, result)
        result = self.run_smart_contract(engine, path, 'Main', 123, '123')
        self.assertEqual(True, result)
        result = self.run_smart_contract(engine, path, 'Main',
                                         Integer.from_bytes(b'123'), '123')
        self.assertEqual(True, result)
Example #15
0
    def test_mixed_equality_operation(self):
        expected_output = (Opcode.INITSLOT + b'\x00' + b'\x02' +
                           Opcode.LDARG0 + Opcode.LDARG1 + Opcode.EQUAL +
                           Opcode.RET)

        path = self.get_contract_path('MixedEquality.py')
        output = Boa3.compile(path)
        self.assertEqual(expected_output, output)

        engine = TestEngine()
        result = self.run_smart_contract(engine, path, 'Main', 1, 'unit')
        self.assertEqual(False, result)
        result = self.run_smart_contract(engine, path, 'Main', 123, '123')
        self.assertEqual(False, result)
        result = self.run_smart_contract(engine, path, 'Main',
                                         Integer.from_bytes(b'123'), '123')
        self.assertEqual(False, result)
Example #16
0
    def _filter_result(self, test_engine, expected_result_type, result) -> Any:
        if test_engine.vm_state is not VMState.HALT and test_engine.error is not None:
            raise TestExecutionException(test_engine.error)

        if expected_result_type is not None:
            if expected_result_type is not str and isinstance(result, str):
                result = String(result).to_bytes()

            if expected_result_type is bool:
                if isinstance(result, bytes):
                    result = Integer.from_bytes(result, signed=True)
                if isinstance(result, int) and result in (False, True):
                    result = bool(result)

            if expected_result_type is bytearray and isinstance(result, bytes):
                result = bytearray(result)

        return result
Example #17
0
    def set_code_targets(self):
        for target, vmcodes in self._missing_target.copy().items():
            if target is None:
                for code in vmcodes.copy():
                    relative_address: int = Integer.from_bytes(code.raw_data,
                                                               signed=True)
                    code_address: int = VMCodeMapping.instance(
                    ).get_start_address(code)
                    absolute_address = code_address + relative_address
                    code.set_target(
                        VMCodeMapping.instance().get_code(absolute_address))

                    vmcodes.remove(code)
            else:
                for code in vmcodes.copy():
                    code.set_target(VMCodeMapping.instance().get_code(target))
                    vmcodes.remove(code)

            if len(vmcodes) == 0:
                self._missing_target.pop(target)
Example #18
0
    def test_storage_between_contracts(self):
        path1 = self.get_contract_path('StorageGetAndPut1.py')
        path2 = self.get_contract_path('StorageGetAndPut2.py')
        self.compile_and_save(path1)
        self.compile_and_save(path2)
        key = 'example_key'
        value = 42

        engine = TestEngine()
        result = self.run_smart_contract(engine, path1, 'put_value', key, value)
        self.assertIsVoid(result)
        storage_value = engine.storage_get(key, path1)
        self.assertIsNotNone(storage_value)
        self.assertEqual(value, Integer.from_bytes(storage_value))

        result = self.run_smart_contract(engine, path2, 'get_value', key)
        self.assertEqual(0, result)

        result = self.run_smart_contract(engine, path1, 'get_value', key)
        self.assertEqual(value, result)
Example #19
0
    def add_token(self, token_script: bytes, script_hash: bytes, amount: int) -> bool:
        if len(token_script) != 20 or len(script_hash) != 20 or amount <= 0:
            return False

        from boa3_test.tests.test_classes.nativetokenprefix import get_native_token_data
        token_prefix, token_id = get_native_token_data(token_script)
        if token_prefix is None or token_id is None:
            return False

        balance_key = token_prefix + script_hash
        if balance_key in self._dict:
            balance = Integer.from_bytes(self[balance_key])
        else:
            balance = 0

        balance += amount

        from boa3_test.tests.test_classes.nativeaccountstate import NativeAccountState
        key = StorageKey(balance_key)
        key._ID = token_id
        self._dict[key] = StorageItem(NativeAccountState(balance).serialize())
        return True
Example #20
0
    def __insert1(self, op_info: OpcodeInformation, data: bytes = None):
        """
        Inserts one opcode into the bytecode

        :param op_info: info of the opcode  that will be inserted
        :param data: data of the opcode, if needed
        """
        vm_code = VMCode(op_info, data)

        if op_info.opcode.has_target():
            data = vm_code.data
            relative_address: int = Integer.from_bytes(data, signed=True)
            actual_address = VMCodeMapping.instance(
            ).bytecode_size + relative_address
            if (self._can_append_target and relative_address != 0
                    and actual_address in VMCodeMapping.instance().code_map):
                vm_code.set_target(
                    VMCodeMapping.instance().code_map[actual_address])
            else:
                self._include_missing_target(vm_code, actual_address)

        self.__insert_code(vm_code)
        self._update_codes_with_target(vm_code)
Example #21
0
 def as_int(self) -> int:
     return Integer.from_bytes(self._value)