def test_generate_manifest_file_abi_method_offset(self): path = self.get_contract_path('GenerationWithDecorator.py') manifest_path = path.replace('.py', '.manifest.json') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef')) methods: Dict[str, Method] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Method) } self.assertGreater(len(methods), 0) output, manifest = self.get_output(path) self.assertTrue(os.path.exists(manifest_path)) self.assertIn('abi', manifest) abi = manifest['abi'] self.assertIn('methods', abi) abi_methods = abi['methods'] self.assertGreater(len(abi['methods']), 0) for method in abi_methods: self.assertIn('name', method) self.assertIn('offset', method) self.assertIn(method['name'], methods) self.assertEqual(method['offset'], methods[method['name']].start_address)
def test_declaration_with_type(self): path = '%s/boa3_test/test_sc/variable_test/DeclarationWithType.py' % self.dirname test_variable_id = 'a' test_method_id = 'Main' compiler = Compiler() expected_compiler_output = ( Opcode.INITSLOT # function signature + b'\x01' + b'\x00' + Opcode.PUSHNULL + Opcode.RET # return ) compiler_output = compiler.compile(path) self.assertEqual(expected_compiler_output, compiler_output) main_symbol_table: Dict[str, ISymbol] = self.get_compiler_analyser( compiler).symbol_table # the variable is local to a method, so it shouldn't be in the main symbol table self.assertFalse(test_variable_id in main_symbol_table) self.assertTrue(test_method_id in main_symbol_table) self.assertIsInstance(main_symbol_table[test_method_id], Method) method: Method = main_symbol_table[test_method_id] method_symbol_table: Dict[str, Variable] = method.symbols # the variable is local to this method, so it should be in the method symbol table self.assertTrue(test_variable_id in method_symbol_table)
def test_generate_manifest_file_with_imported_event(self): path = self.get_contract_path('GenerationWithImportedEvent.py') expected_manifest_output = path.replace('.py', '.manifest.json') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef')) events: Dict[str, Event] = {} for name, method in self.get_compiler_analyser( compiler).symbol_table.items(): if isinstance(method, Event): events[name] = method if method.name not in events: events[method.name] = method output, manifest = self.get_output(path) self.assertTrue(os.path.exists(expected_manifest_output)) self.assertIn('abi', manifest) abi = manifest['abi'] self.assertIn('events', abi) self.assertEqual(1, len(abi['events'])) for abi_event in abi['events']: self.assertIn('name', abi_event) self.assertIn(abi_event['name'], events) self.assertIn('parameters', abi_event) event_args = events[abi_event['name']].args for event_param in abi_event['parameters']: self.assertIn('name', event_param) self.assertIn(event_param['name'], event_args) self.assertIn('type', event_param) self.assertEqual(event_args[event_param['name']].type.abi_type, event_param['type'])
def test_generate_manifest_file_with_nep5_transfer_event(self): path = self.get_contract_path('test_sc/event_test', 'EventNep5Transfer.py') expected_manifest_output = path.replace('.py', '.manifest.json') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef')) events: Dict[str, Event] = { event.name: event for event in self.get_compiler_analyser( compiler).symbol_table.values() if isinstance(event, Event) } output, manifest = self.get_output(path) self.assertTrue(os.path.exists(expected_manifest_output)) self.assertIn('abi', manifest) abi = manifest['abi'] self.assertIn('methods', abi) self.assertEqual(1, len(abi['methods'])) self.assertIn('events', abi) self.assertEqual(1, len(abi['events'])) for abi_event in abi['events']: self.assertIn('name', abi_event) self.assertIn(abi_event['name'], events) self.assertIn('parameters', abi_event) event_args = events[abi_event['name']].args for event_param in abi_event['parameters']: self.assertIn('name', event_param) self.assertIn(event_param['name'], event_args) self.assertIn('type', event_param) self.assertEqual(event_args[event_param['name']].type.abi_type, event_param['type'])
def test_generate_manifest_file_with_event(self): path = '%s/boa3_test/test_sc/event_test/EventWithArgument.py' % self.dirname expected_manifest_output = path.replace('.py', '.manifest.json') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef')) events: Dict[str, Event] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Event) } output, manifest = self.get_output(path) self.assertTrue(os.path.exists(expected_manifest_output)) self.assertIn('abi', manifest) abi = manifest['abi'] self.assertIn('methods', abi) self.assertEqual(1, len(abi['methods'])) self.assertIn('events', abi) self.assertEqual(1, len(abi['events'])) for abi_event in abi['events']: self.assertIn('name', abi_event) self.assertIn(abi_event['name'], events) self.assertIn('parameters', abi_event) event_args = events[abi_event['name']].args for event_param in abi_event['parameters']: self.assertIn('name', event_param) self.assertIn(event_param['name'], event_args) self.assertIn('type', event_param) self.assertEqual(event_args[event_param['name']].type.abi_type, event_param['type'])
def test_generate_init_method(self): path = self.get_contract_path('test_sc/variable_test', 'GlobalAssignmentWithType.py') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef'), debug=True) methods: Dict[str, Method] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Method) } self.assertGreater(len(methods), 0) self.assertIn(constants.INITIALIZE_METHOD_ID, methods) init_method = methods[constants.INITIALIZE_METHOD_ID] output, manifest = self.get_output(path) self.assertIn('abi', manifest) abi = manifest['abi'] self.assertIn('methods', abi) self.assertGreater(len(abi['methods']), 0) abi_init = next(method for method in abi['methods'] if 'name' in method and method['name'] == constants.INITIALIZE_METHOD_ID) self.assertIsNotNone(abi_init) self.assertIn('offset', abi_init) self.assertEqual(init_method.start_address, abi_init['offset']) self.assertIn('parameters', abi_init) self.assertEqual(0, len(abi_init['parameters'])) self.assertIn('returntype', abi_init) self.assertEqual(AbiType.Void, abi_init['returntype']) debug_info = self.get_debug_info(path) self.assertIn('methods', debug_info) self.assertGreater(len(debug_info['methods']), 0) debug_method = next( (method for method in debug_info['methods'] if 'id' in method and method['id'] == str(id(init_method))), None) self.assertIsNotNone(debug_method) parsed_name = debug_method['name'].split( constants.VARIABLE_NAME_SEPARATOR) self.assertEqual(2, len(parsed_name)) self.assertIn(parsed_name[-1], methods) # validate parameters self.assertIn('params', debug_method) self.assertEqual(0, len(debug_method['params'])) # validate local variables self.assertIn('variables', debug_method) self.assertEqual(0, len(debug_method['variables'])) # validate sequence points self.assertIn('sequence-points', debug_method) self.assertEqual(0, len(debug_method['sequence-points']))
def test_generate_nefdbgnfo_file(self): from boa3.model.type.itype import IType path = self.get_contract_path('GenerationWithDecorator.py') expected_nef_output = path.replace('.py', '.nefdbgnfo') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef'), debug=True) methods: Dict[str, Method] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Method) } self.assertTrue(os.path.exists(expected_nef_output)) debug_info = self.get_debug_info(path) self.assertNotIn('entrypoint', debug_info) self.assertIn('methods', debug_info) self.assertGreater(len(debug_info['methods']), 0) for debug_method in debug_info['methods']: self.assertIn('name', debug_method) parsed_name = debug_method['name'].split( constants.VARIABLE_NAME_SEPARATOR) self.assertEqual(2, len(parsed_name)) self.assertIn(parsed_name[-1], methods) actual_method = methods[parsed_name[-1]] # validate id self.assertIn('id', debug_method) self.assertEqual(str(id(actual_method)), debug_method['id']) # validate parameters self.assertIn('params', debug_method) self.assertEqual(len(actual_method.args), len(debug_method['params'])) for var in debug_method['params']: self.assertEqual( 2, len(var.split(constants.VARIABLE_NAME_SEPARATOR))) param_id, param_type = var.split( constants.VARIABLE_NAME_SEPARATOR) self.assertIn(param_id, actual_method.args) self.assertEqual(param_type, actual_method.args[param_id].type.abi_type) # validate local variables self.assertIn('variables', debug_method) self.assertEqual(len(actual_method.locals), len(debug_method['variables'])) for var in debug_method['variables']: self.assertEqual( 2, len(var.split(constants.VARIABLE_NAME_SEPARATOR))) var_id, var_type = var.split(constants.VARIABLE_NAME_SEPARATOR) self.assertIn(var_id, actual_method.locals) local_type = actual_method.locals[var_id].type self.assertEqual( local_type.abi_type if isinstance(local_type, IType) else AbiType.Any, var_type)
def test_generate_debug_info_with_multiple_flows(self): path = self.get_contract_path('GenerationWithMultipleFlows.py') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef'), debug=True) methods: Dict[str, Method] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Method) } self.assertGreater(len(methods), 0) debug_info = self.get_debug_info(path) self.assertIn('methods', debug_info) self.assertGreater(len(debug_info['methods']), 0) for debug_method in debug_info['methods']: self.assertIn('name', debug_method) parsed_name = debug_method['name'].split( constants.VARIABLE_NAME_SEPARATOR) self.assertEqual(2, len(parsed_name)) self.assertIn(parsed_name[-1], methods) actual_method = methods[parsed_name[-1]] # validate id self.assertIn('id', debug_method) self.assertEqual(str(id(actual_method)), debug_method['id']) # validate parameters self.assertIn('params', debug_method) self.assertEqual(len(actual_method.args), len(debug_method['params'])) for var in debug_method['params']: self.assertEqual( 2, len(var.split(constants.VARIABLE_NAME_SEPARATOR))) param_id, param_type = var.split( constants.VARIABLE_NAME_SEPARATOR) self.assertIn(param_id, actual_method.args) self.assertEqual(param_type, actual_method.args[param_id].type.abi_type) # validate local variables self.assertIn('variables', debug_method) self.assertEqual(len(actual_method.locals), len(debug_method['variables'])) for var in debug_method['variables']: self.assertEqual( 2, len(var.split(constants.VARIABLE_NAME_SEPARATOR))) var_id, var_type = var.split(constants.VARIABLE_NAME_SEPARATOR) self.assertIn(var_id, actual_method.locals) self.assertEqual(actual_method.locals[var_id].type.abi_type, var_type)
def test_generate_nefdbgnfo_file(self): path = '%s/boa3_test/test_sc/generation_test/GenerationWithDecorator.py' % self.dirname expected_nef_output = path.replace('.py', '.nefdbgnfo') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef')) methods: Dict[str, Method] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Method) } self.assertTrue(os.path.exists(expected_nef_output)) debug_info = self.get_debug_info(path) self.assertNotIn('entrypoint', debug_info) self.assertIn('methods', debug_info) self.assertGreater(len(debug_info['methods']), 0) for debug_method in debug_info['methods']: self.assertIn('name', debug_method) parsed_name = debug_method['name'].split(',') self.assertEqual(2, len(parsed_name)) self.assertIn(parsed_name[-1], methods) actual_method = methods[parsed_name[-1]] # validate id self.assertIn('id', debug_method) self.assertEqual(str(id(actual_method)), debug_method['id']) # validate parameters self.assertIn('params', debug_method) self.assertEqual(len(actual_method.args), len(debug_method['params'])) for var in debug_method['params']: self.assertEqual(2, len(var.split(','))) param_id, param_type = var.split(',') self.assertIn(param_id, actual_method.args) self.assertEqual(param_type, actual_method.args[param_id].type.abi_type) # validate local variables self.assertIn('variables', debug_method) self.assertEqual(len(actual_method.locals), len(debug_method['variables'])) for var in debug_method['variables']: self.assertEqual(2, len(var.split(','))) var_id, var_type = var.split(',') self.assertIn(var_id, actual_method.locals) self.assertEqual(actual_method.locals[var_id].type.abi_type, var_type)
def compile_and_save(path: str, output_path: str = None, root_folder: str = None, show_errors: bool = True, debug: bool = False): """ Load a Python file to be compiled and save the result into the files. By default, the resultant .nef file is saved in the same folder of the source file. :param path: the path of the Python file to compile :param output_path: Optional path to save the generated files :param root_folder: the root path of the project :param show_errors: if compiler errors should be logged. :param debug: if nefdbgnfo file should be generated. """ if not path.endswith('.py'): raise InvalidPathException(path) if output_path is None: output_path = path.replace('.py', '.nef') elif not output_path.endswith('.nef'): raise InvalidPathException(path) Compiler().compile_and_save(path, output_path, root_folder, show_errors, debug)
def test_generate_nefdbgnfo_file_with_event(self): path = self.get_contract_path('test_sc/event_test', 'EventWithArgument.py') expected_nef_output = path.replace('.py', '.nefdbgnfo') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef'), debug=True) events: Dict[str, Event] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Event) } self.assertTrue(os.path.exists(expected_nef_output)) debug_info = self.get_debug_info(path) self.assertNotIn('entrypoint', debug_info) self.assertIn('events', debug_info) self.assertGreater(len(debug_info['events']), 0) for debug_event in debug_info['events']: self.assertIn('name', debug_event) parsed_name = debug_event['name'].split( constants.VARIABLE_NAME_SEPARATOR) self.assertEqual(2, len(parsed_name)) self.assertIn(parsed_name[-1], events) actual_event = events[parsed_name[-1]] # validate id self.assertIn('id', debug_event) self.assertEqual(str(id(actual_event)), debug_event['id']) # validate parameters self.assertIn('params', debug_event) self.assertEqual(len(actual_event.args), len(debug_event['params'])) for var in debug_event['params']: self.assertEqual( 2, len(var.split(constants.VARIABLE_NAME_SEPARATOR))) param_id, param_type = var.split( constants.VARIABLE_NAME_SEPARATOR) self.assertIn(param_id, actual_event.args) self.assertEqual(param_type, actual_event.args[param_id].type.abi_type)
def compile(path: str) -> bytes: """ Load a Python file to be compiled but don't write the result into a file :param path: the path of the Python file to compile :return: the bytecode of the compiled .nef file """ if not path.endswith('.py'): raise InvalidPathException(path) return Compiler().compile(path)
def test_generate_nefdbgnfo_file_with_static_variables(self): path = self.get_contract_path('GenerationWithStaticVariables.py') expected_nef_output = path.replace('.py', '.nefdbgnfo') compiler = Compiler() compiler.compile_and_save(path, path.replace('.py', '.nef'), debug=True) variables: Dict[str, Method] = { name: method for name, method in self.get_compiler_analyser( compiler).symbol_table.items() if isinstance(method, Variable) } self.assertTrue(os.path.exists(expected_nef_output)) debug_info = self.get_debug_info(path) self.assertNotIn('entrypoint', debug_info) self.assertIn('static-variables', debug_info) self.assertGreater(len(debug_info['static-variables']), 0) for static_variable in debug_info['static-variables']: # validate parameters self.assertEqual( 3, len(static_variable.split(constants.VARIABLE_NAME_SEPARATOR))) var_id, var_type, var_slot = static_variable.split( constants.VARIABLE_NAME_SEPARATOR) if var_id not in variables: self.assertIn(var_id, [ var_full_id.split(constants.VARIABLE_NAME_SEPARATOR)[-1] for var_full_id in variables ]) var_inner_id = next((var for var in variables if var.split( constants.VARIABLE_NAME_SEPARATOR)[-1] == var_id), var_id) else: var_inner_id = var_id self.assertIn(var_inner_id, variables) self.assertEqual(var_type, variables[var_inner_id].type.abi_type)