def test_tzt(self) -> None: parser = MichelsonParser() for filename in listdir(self.path): if filename in self.exclude: continue with self.subTest(filename): filename = join(self.path, filename) with open(filename) as file: script = michelson_to_micheline( file.read(), parser=parser, ) Interpreter.run_tzt(script=script)
def callback_view(self): """Get return value of an on-chain callback method. :returns: Decoded parameters of a callback """ if self.address: initial_storage = self.shell.blocks[ self.context.block_id].context.contracts[ self.address].storage() else: storage_ty = StorageSection.match(self.context.storage_expr) initial_storage = storage_ty.dummy( self.context).to_micheline_value(lazy_diff=True) operations, _, stdout, error = Interpreter.run_view( parameter=self.parameters['value'], entrypoint=self.parameters['entrypoint'], storage=initial_storage, context=self.context, ) if not len(operations) == 1: raise Exception( 'Multiple internal operations, not sure which one to pick') if error: logger.debug('\n'.join(stdout)) raise error return operations[0]
def interpret( self, storage=None, source=None, sender=None, amount=None, balance=None, chain_id=None, level=None, now=None, self_address=None, ) -> ContractCallResult: """Run code in the builtin REPL (WARNING! Not recommended for critical tasks). :param storage: initial storage as Python object, leave None if you want to generate a dummy one :param source: patch SOURCE :param sender: patch SENDER :param amount: patch AMOUNT :param balance: patch BALANCE :param chain_id: patch CHAIN_ID :param level: patch LEVEL :param now: patch NOW :param self_address: patch SELF/SELF_ADDRESS :rtype: pytezos.contract.result.ContractCallResult """ storage_ty = StorageSection.match(self.context.storage_expr) if storage is None: initial_storage = storage_ty.dummy( self.context).to_micheline_value(lazy_diff=True) else: initial_storage = storage_ty.from_python_object( storage).to_micheline_value(lazy_diff=True) assert self.context.script operations, storage, lazy_diff, stdout, error = Interpreter.run_code( parameter=self.parameters['value'], entrypoint=self.parameters['entrypoint'], storage=initial_storage, script=self.context.script['code'], source=source, sender=sender or source, amount=amount or self.amount, balance=balance, chain_id=chain_id, level=level, now=now, address=self_address, ) if error: logger.debug('\n'.join(stdout)) raise error res = { 'operations': operations, 'storage': storage, 'lazy_diff': lazy_diff, } return ContractCallResult.from_run_code( res, parameters=self.parameters, context=self.context, )
def test_failed_macros(self, filename, storage, parameter): with open(join(dirname(__file__), 'macros', filename)) as f: script = f.read() _, _, _, _, error = Interpreter.run_code( parameter=michelson_to_micheline(parameter), storage=michelson_to_micheline(storage), script=michelson_to_micheline(script)) self.assertIsNotNone(error)
def test_execute(self) -> None: # Arrange interpreter = Interpreter() code = "PUSH int 1; PUSH int 2; PAIR" # Act result = interpreter.execute(code) # Assert self.assertEqual(None, result.error) self.assertEqual( [ "PUSH / _ => 1", "PUSH / _ => 2", "PAIR / 2 : 1 => (2 * 1)", ], result.stdout, ) self.assertEqual([PairType((IntType(2), IntType(1)))], interpreter.stack.items)
def test_execution(self, filename, parameter, storage, result): with open(join(dirname(__file__), 'execution', filename)) as f: script = f.read() _, storage, lazy_diff, stdout, error = Interpreter.run_code( parameter=michelson_to_micheline(parameter), storage=michelson_to_micheline(storage), script=michelson_to_micheline(script), ) self.assertIsNone(error) self.assertEqual(storage, result)
def test_macros(self, filename, storage, parameter, result): with open(join(dirname(__file__), 'macros', filename)) as f: script = f.read() _, storage, _, stdout, error = Interpreter.run_code( parameter=michelson_to_micheline(parameter), storage=michelson_to_micheline(storage), script=michelson_to_micheline(script)) if error: print('\n'.join(stdout)) raise error self.assertEqual(michelson_to_micheline(result), storage)
def test_execute_rollback(self) -> None: # Arrange interpreter = Interpreter() code = "PUSH int 1; PUSH int 2; PAIR" bad_code = "PUSH int 1; PAIR; PAIR;" # Act interpreter.execute(code) result = interpreter.execute(bad_code) # Assert self.assertIsInstance(result.error, MichelsonRuntimeError) self.assertEqual( [ "PUSH / _ => 1", "PAIR / 1 : (2 * 1) => (1 * (2 * 1))", "PAIR: got 1 items on the stack, want to pop 2", ], result.stdout, ) self.assertEqual([PairType((IntType(2), IntType(1)))], interpreter.stack.items)
def test_execute_contract(self) -> None: # Arrange interpreter = Interpreter() code = """ storage unit ; parameter unit ; BEGIN Unit Unit ; DROP ; PUSH unit Unit ; NIL operation ; PAIR ; COMMIT ; """ # Act result = interpreter.execute(code) # Assert self.assertEqual(None, result.error) self.assertEqual( [ "storage: updated", "parameter: updated", "BEGIN %default / _ => (Unit * Unit)", "DROP / (Unit * Unit) => _", "PUSH / _ => Unit", "NIL / _ => []", "PAIR / [] : Unit => ([] * Unit)", "END %default / ([] * Unit) => _", ], result.stdout, ) self.assertEqual( [], interpreter.stack.items, )
def storage_view(self): """Get return value of an off-chain storage view. :returns: Decoded parameters of a callback """ _, storage, stdout, error = Interpreter.run_view( parameter=self.parameters['value'], entrypoint=self.parameters['entrypoint'], storage={'prim': 'None'}, context=self.context, ) if error: logger.debug('\n'.join(stdout)) raise error return storage # type: ignore
def test_big_map_opcodes(self, filename, storage, parameter, result, big_map_log): with open(join(dirname(__file__), 'opcodes', filename)) as f: script = f.read() _, storage, lazy_diff, stdout, error = Interpreter.run_code( parameter=michelson_to_micheline(parameter), storage=michelson_to_micheline(storage), script=michelson_to_micheline(script), output_mode='optimized') if error: print('\n'.join(stdout)) raise error self.assertEqual(michelson_to_micheline(result), storage) log = list(iter_lazy_diff(lazy_diff)) self.assertEqual(sorted(big_map_log), sorted(log))
def view(self): """ Get return value of a view method. :returns: Decoded parameters of a callback """ if self.address: initial_storage = self.shell.blocks[ self.context.block_id].context.contracts[ self.address].storage() else: storage_ty = StorageSection.match(self.context.storage_expr) initial_storage = storage_ty.dummy( self.context).to_micheline_value(lazy_diff=True) res, stdout, error = Interpreter.run_view( parameter=self.parameters['value'], entrypoint=self.parameters['entrypoint'], storage=initial_storage, context=self.context) if error: print('\n'.join(stdout)) raise error return res
def test_execute_contract_big_map(self) -> None: # Arrange interpreter = Interpreter() code = """ storage (big_map string nat) ; parameter unit ; BEGIN Unit {}; DROP ; EMPTY_BIG_MAP string nat ; PUSH nat 15 ; SOME ; PUSH string "cherry" ; UPDATE ; PUSH nat 22 ; SOME ; PUSH string "banana" ; UPDATE ; DUP ; PUSH string "cherry" ; DUP ; SWAP ; DIP { SWAP } ; MEM ; IF { DIP { DUP } ; DUP ; DIP { SWAP } ; GET ; IF_SOME { PUSH nat 5 ; ADD ; SOME ; SWAP ; UPDATE } { DROP } } { DROP } ; NIL operation ; PAIR ; COMMIT ; """ # Act result = interpreter.execute(code) # Assert self.assertEqual(None, result.error) self.assertEqual( [ "storage: updated", "parameter: updated", "BEGIN %default / _ => (Unit * <-1>)", "DROP / (Unit * <-1>) => _", "EMPTY_BIG_MAP / _ => <-2>", "PUSH / _ => 15", "SOME / 15 => 15?", "PUSH / _ => 'cherry'", "UPDATE / 'cherry' : 15? : <-2> => <-2>", "PUSH / _ => 22", "SOME / 22 => 22?", "PUSH / _ => 'banana'", "UPDATE / 'banana' : 22? : <-2> => <-2>", "DUP / <-2> => <-2> : <-2>", "PUSH / _ => 'cherry'", "DUP / 'cherry' => 'cherry' : 'cherry'", "SWAP / 'cherry' : 'cherry' => 'cherry' : 'cherry'", "DIP / * => _", "SWAP / 'cherry' : <-2> => <-2> : 'cherry'", "DIP 1 / _ => *", "MEM / 'cherry' : <-2> => True", "IF / True => _", "DIP / * => _", "DUP / <-2> => <-2> : <-2>", "DIP 1 / _ => *", "DUP / 'cherry' => 'cherry' : 'cherry'", "DIP / * => _", "SWAP / 'cherry' : <-2> => <-2> : 'cherry'", "DIP 1 / _ => *", "GET / 'cherry' : <-2> => 15?", "IF_NONE / 15? => 15", "PUSH / _ => 5", "ADD / 5 : 15 => 20", "SOME / 20 => 20?", "SWAP / 20? : 'cherry' => 'cherry' : 20?", "UPDATE / 'cherry' : 20? : <-2> => <-2>", "NIL / _ => []", "PAIR / [] : <-2> => ([] * <-2>)", "END %default / ([] * <-2>) => _", ], result.stdout, ) self.assertEqual( [], interpreter.stack.items, ) self.assertEqual({}, interpreter.context.big_maps) commit_instruction = next((i for i in result.instructions.items[::-1] if isinstance(i, CommitInstruction))) self.assertEqual( [{ "diff": { "action": "alloc", "key_type": { "prim": "string" }, "updates": [ { "key": { "string": "banana" }, "key_hash": "expruyWGhjeJ3v2cWgMkRyYuvbdZKjjARtHvhVeJDCyHgLebMmhBEo", "value": { "int": "22" }, }, { "key": { "string": "cherry" }, "key_hash": "expruVgSSodFW5ZDLidXaTVVczu6ddLVebXjMZBG33Z2oyQDUugvdE", "value": { "int": "20" }, }, ], "value_type": { "prim": "nat" }, }, "id": "0", "kind": "big_map", }], commit_instruction.lazy_diff, )
def test_execute_contract_operations(self) -> None: # Arrange interpreter = Interpreter() code = """ PATCH SENDER "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" ; PATCH BALANCE 200 ; parameter mutez ; storage unit ; BEGIN 100 Unit ; CAR ; DUP ; BALANCE ; IFCMPLT { FAIL } { SENDER ; CONTRACT unit ; IF_NONE { FAIL } { SWAP ; UNIT ; TRANSFER_TOKENS ; NIL operation ; SWAP ; CONS ; UNIT ; SWAP ; PAIR } ; }; COMMIT """ # Act result = interpreter.execute(code) # Assert self.assertEqual(None, result.error) self.assertEqual( [ "parameter: updated", "storage: updated", "BEGIN %default / _ => (0.0001 * Unit)", "CAR / (0.0001 * Unit) => 0.0001", "DUP / 0.0001 => 0.0001 : 0.0001", "BALANCE / _ => 0.0002", "COMPARE / 0.0002 : 0.0001 => 1", "LT / 1 => False", "IF / False => _", "SENDER / _ => tz1VSU…cjb", "CONTRACT: skip type checking for tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb", "CONTRACT / tz1VSU…cjb => tz1VSU…cjb%default?", "IF_NONE / tz1VSU…cjb%default? => tz1VSU…cjb%default", "SWAP / tz1VSU…cjb%default : 0.0001 => 0.0001 : tz1VSU…cjb%default", "UNIT / _ => Unit", "TRANSFER_TOKENS / Unit : 0.0001 : tz1VSU…cjb%default => transaction", "NIL / _ => []", "SWAP / [] : transaction => transaction : []", "CONS / transaction : [] => [transaction]", "UNIT / _ => Unit", "SWAP / Unit : [transaction] => [transaction] : Unit", "PAIR / [transaction] : Unit => ([transaction] * Unit)", "END %default / ([transaction] * Unit) => _", ], result.stdout, ) commit_instruction = next((i for i in result.instructions.items[::-1] if isinstance(i, CommitInstruction))) self.assertEqual( PairType(( ListType([ OperationType({ "kind": "transaction", "source": "KT1BEqzn5Wx8uJrZNvuS9DVHmLvG9td3fDLi", "destination": "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb", "amount": "100", "parameters": { "entrypoint": "default", "value": { "prim": "Unit" }, }, }) ]), UnitType(), )), commit_instruction.result, )
def __init__(self, **kwargs): super(MichelsonKernel, self).__init__(**kwargs) self.interpreter = Interpreter()
def __init__(self, **kwargs): super().__init__(**kwargs) self.interpreter = Interpreter()
class MichelsonKernel(Kernel): implementation = 'IMichelson' implementation_version = __version__ language_info = { 'name': 'Michelson', 'mimetype': 'text/x-michelson', 'file_extension': '.tz', 'codemirror_mode': 'michelson', } banner = 'Michelson (Tezos VM language)' help_links = [ 'https://michelson.nomadic-labs.com/', 'https://tezos.gitlab.io/whitedoc/michelson.html', ] def __init__(self, **kwargs): super().__init__(**kwargs) self.interpreter = Interpreter() def _stdout(self, text: str) -> None: self.send_response( self.iopub_socket, 'stream', { 'name': 'stdout', 'text': text, }, ) def _find_stack_items(self, instructions: MichelineSequence, stack: MichelsonStack) -> Optional[List[MichelsonInstruction]]: for operation in instructions.items[::-1]: items = getattr(operation, 'items', None) if isinstance(items, list): stack_items = self._find_stack_items(MichelineSequence(items), stack) if stack_items: return stack_items if not isinstance(operation, MichelsonInstruction): continue if operation.stack_items_added: return cast(List[MichelsonInstruction], stack.items[-operation.stack_items_added :]) return None def _find_lazy_diff(self, instructions: MichelineSequence) -> Optional[List[Dict[str, str]]]: for instruction in instructions.items[::-1]: if isinstance(instruction, CommitInstruction) and instruction.lazy_diff: return instruction.lazy_diff if isinstance(instruction, BigMapDiffInstruction) and instruction.lazy_diff: return instruction.lazy_diff return None def _find_contract_result(self, instructions: MichelineSequence) -> Optional[PairType]: for instruction in instructions.items[::-1]: if isinstance(instruction, MichelineSequence): return self._find_contract_result(instruction) if isinstance( instruction, ( CommitInstruction, RunInstruction, ), ): return instruction.result return None def _send_success_response(self, instructions: MichelineSequence, stack: MichelsonStack) -> Dict[str, Any]: plain, html = '', '' contract_result = self._find_contract_result(instructions) if contract_result: header = 'Operations' table = preformat_operations_table(cast(List[OperationType], contract_result.items[0])) plain += plain_table(table, header) html += html_table(table, header) header = 'Storage' table = preformat_storage_table(contract_result.items[1]) plain += plain_table(table, header) html += html_table(table, header) modified_items = self._find_stack_items(instructions, stack) if modified_items is not None and not contract_result: header = 'Stack updates' table = preformat_stack_table(modified_items) plain += plain_table(table, header) html += html_table(table, header) lazy_diff = self._find_lazy_diff(instructions) if lazy_diff: header = 'BigMap diff' lazy_diff_table = preformat_lazy_diff_table(lazy_diff) plain += plain_table(lazy_diff_table, header) html += html_table(lazy_diff_table, header) result = { 'data': { 'text/plain': plain, 'text/html': html, }, 'metadata': {}, 'execution_count': self.execution_count, } self.send_response( self.iopub_socket, 'execute_result', result, ) return result def _send_fail_response(self, error: Exception) -> Dict[str, Any]: if isinstance(error, (MichelsonParserError, MichelsonRuntimeError)): traceback = [error.format_stdout()] else: traceback = format_exception(error.__class__, error, None) result = { 'status': 'error', 'ename': error.__class__.__name__, 'evalue': str(error), 'traceback': traceback, } self.send_response( self.iopub_socket, 'stream', { 'name': 'stderr', 'text': '\n'.join(traceback), }, ) return result def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False): interpreter_result = self.interpreter.execute(code) if not silent and interpreter_result.stdout: self._stdout('\n'.join(interpreter_result.stdout)) if not interpreter_result.error: if not silent: return self._send_success_response(interpreter_result.instructions, interpreter_result.stack) else: return self._send_fail_response(interpreter_result.error) return {} def do_complete(self, code, cursor_pos): token, begin_pos, end_pos = parse_token(code, cursor_pos) suggests = [] for word_set in [prim_tags, static_macros]: for word in word_set: if word.startswith(token): suggests.append(word) if suggests: res = { 'matches': suggests, 'cursor_start': begin_pos, 'cursor_end': end_pos, } else: res = { 'matches': [], 'cursor_start': cursor_pos, 'cursor_end': cursor_pos, } res['status'] = 'ok' return res def do_inspect(self, code, cursor_pos, detail_level=0): token, _, _ = parse_token(code, cursor_pos) docstring = docs.get(token) if docstring: res = {'found': True, 'data': {'text/plain': docstring}} else: res = {'found': False} res['status'] = 'ok' return res