From 5c92c24c5091b0eeb25add95b7d0d076bb925d6a Mon Sep 17 00:00:00 2001 From: m-kus Date: Thu, 27 Feb 2020 13:39:44 +0300 Subject: [PATCH] adapting for the kernel --- Makefile | 4 ++-- pytezos/encoding.py | 8 ++++++++ pytezos/repl/blockchain.py | 25 +++++++++++++------------ pytezos/repl/context.py | 26 +++++++++++++++++++------- pytezos/repl/helpers.py | 24 +++++++++++++++++------- pytezos/repl/interpreter.py | 21 ++++++++++++--------- pytezos/repl/parser.py | 11 +++++++++-- 7 files changed, 80 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index e7a02e3db..b237d4fd0 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ -local: - pip install . --force --no-deps \ No newline at end of file +debug: + pip install . --force --no-deps diff --git a/pytezos/encoding.py b/pytezos/encoding.py index 6434b88fd..3b1f9a7cb 100644 --- a/pytezos/encoding.py +++ b/pytezos/encoding.py @@ -230,6 +230,14 @@ def parse_public_key(data: bytes): return base58_encode(data[1:], key_prefix[data[:1]]).decode() +def parse_chain_id(data: bytes): + return base58_encode(data, b'Net').decode() + + +def parse_signature(data: bytes): + return base58_encode(data, b'sig').decode() + + def forge_address(value, tz_only=False) -> bytes: prefix = value[:3] address = base58.b58decode_check(value)[3:] diff --git a/pytezos/repl/blockchain.py b/pytezos/repl/blockchain.py index 8fc788d82..b1b0c438f 100644 --- a/pytezos/repl/blockchain.py +++ b/pytezos/repl/blockchain.py @@ -60,7 +60,7 @@ def do_self(ctx: Context, prim, args, annots): assert p_type_expr, f'parameter type is not initialized' entrypoint = next((a for a in annots if a[0] == '%'), '%default') - ctx.print(f' use {entrypoint};') + ctx.printf(f' use {entrypoint};') p_type_expr = get_entry_expr(p_type_expr, entrypoint) res = Contract.new(SELF_ADDRESS + entrypoint, type_expr=p_type_expr) @@ -83,20 +83,21 @@ def do_source(ctx: Context, prim, args, annots): @instruction('NOW') def do_now(ctx: Context, prim, args, annots): - chain_id = ctx.get('CHAIN_ID') - if chain_id: - now = pytezos.using(get_network_by_chain_id(chain_id)).now() - else: - now = int(datetime.utcnow().timestamp()) - - res = Timestamp(now) + res = ctx.get('NOW') + if not res: + chain_id = ctx.get('CHAIN_ID') + if chain_id: + now = pytezos.using(get_network_by_chain_id(chain_id)).now() + else: + now = int(datetime.utcnow().timestamp()) + res = Timestamp(now) ctx.push(res, annots=annots) def check_contract(ctx: Context, address, type_expr): chain_id = ctx.get('CHAIN_ID') if not chain_id: - ctx.print(' skip check;') + ctx.printf(' skip check;') return True network = get_network_by_chain_id(chain_id) @@ -106,9 +107,9 @@ def check_contract(ctx: Context, address, type_expr): if expr_equal(type_expr, ci.contract.parameter.code): return True else: - ctx.print(' type mismatch;') + ctx.printf(' type mismatch;') except Exception: - ctx.print(' not found;') + ctx.printf(' not found;') return False @@ -117,7 +118,7 @@ def check_contract(ctx: Context, address, type_expr): def do_address(ctx: Context, prim, args, annots): top = ctx.pop1() assert_stack_type(top, Contract) - res = Address.new(str(top)) + res = Address.new(str(top)[:36]) ctx.push(res, annots=annots) diff --git a/pytezos/repl/context.py b/pytezos/repl/context.py index 214973a31..9ff479c1e 100644 --- a/pytezos/repl/context.py +++ b/pytezos/repl/context.py @@ -1,4 +1,5 @@ import re +from copy import deepcopy from pprint import pformat from typing import List @@ -8,13 +9,13 @@ class Context: - def __init__(self, stack=None, debug=True): + def __init__(self, stack=None, meta=None, debug=True): self.stack = stack or [] + self.meta = meta or {} self.protected = 0 - self.meta = dict() - self.stdout = list() self.exec_depth = 0 self.debug = debug + self.stdout = list() def protect(self, count: int): assert len(self.stack) >= count, f'got {len(self.stack)} items, wanted to protect {count}' @@ -40,7 +41,7 @@ def pop(self, count: int) -> List[StackItem]: f'got {len(self.stack) - self.protected} items, requested {count} ' res = [self.stack.pop(self.protected) for _ in range(count)] if count <= 3: - body = ', '.join(map(repr, res)) + body = ', '.join(map(repr, res)) # TODO: restrict line length else: body = f'{count} items' self._print(f' pop {body};') @@ -76,14 +77,17 @@ def drop_all(self): self._print(f' drop all;') def dump(self, count: int): - count = min(count, len(self.stack)) - return self.stack[:count] + if len(self.stack) > 0: + count = min(count, len(self.stack)) + return self.stack[:count] + else: + return 'stack is empty' def _print(self, message): if self.debug: self.stdout.append(message) - def print(self, template: str): + def printf(self, template: str): def format_stack_item(match): i = int(match.groups()[0]) assert i < len(self.stack), f'requested {i}th element, got only {len(self.stack)} items' @@ -106,3 +110,11 @@ def __len__(self) -> int: def __repr__(self): return pformat(self.stack) + + def __deepcopy__(self, memodict={}): + ctx = Context(stack=deepcopy(self.stack), + meta=deepcopy(self.meta), + debug=self.debug) + ctx.protected = self.protected + ctx.exec_depth = self.exec_depth + return ctx diff --git a/pytezos/repl/helpers.py b/pytezos/repl/helpers.py index 6a970c089..a9b102b6e 100644 --- a/pytezos/repl/helpers.py +++ b/pytezos/repl/helpers.py @@ -4,11 +4,11 @@ from pytezos.encoding import is_kt from pytezos.repl.control import instruction, do_interpret from pytezos.repl.context import Context, StackItem -from pytezos.repl.parser import get_int, get_string, parse_prim_expr, get_entry_expr +from pytezos.repl.parser import get_int, get_string, get_bool, parse_prim_expr, get_entry_expr from pytezos.repl.types import Pair, Mutez, Address, ChainID, Timestamp -helpers_prim = ['DUMP', 'PRINT', 'DROP_ALL', 'EXPAND', 'RUN', 'PATCH', 'UNSET', 'INCLUDE'] -patch_prim = ['ADDRESS', 'AMOUNT', 'BALANCE', 'CHAIN_ID', 'SENDER', 'SOURCE', 'NOW'] +helpers_prim = ['DUMP', 'PRINT', 'DROP_ALL', 'EXPAND', 'RUN', 'PATCH', 'INCLUDE', 'DEBUG'] +patch_prim = ['AMOUNT', 'BALANCE', 'CHAIN_ID', 'SENDER', 'SOURCE', 'NOW'] @instruction('DUMP', args_len=1) @@ -16,9 +16,19 @@ def do_dump(ctx: Context, prim, args, annots): return ctx.dump(count=get_int(args[0])) +@instruction('DUMP') +def do_dump(ctx: Context, prim, args, annots): + return ctx.dump(count=len(ctx)) + + @instruction('PRINT', args_len=1) def do_print(ctx: Context, prim, args, annots): - ctx.print(template=get_string(args[0])) + ctx.printf(template=f' {get_string(args[0])};') + + +@instruction('DEBUG', args_len=1) +def do_debug(ctx: Context, prim, args, annots): + ctx.debug = get_bool(args[0]) @instruction('DROP_ALL') @@ -37,7 +47,7 @@ def do_run(ctx: Context, prim, args, annots): assert p_type_expr, f'parameter type is not initialized' entrypoint = next((a for a in annots if a[0] == '%'), '%default') - ctx.print(f' use {entrypoint};') + ctx.printf(f' use {entrypoint};') p_type_expr = get_entry_expr(p_type_expr, entrypoint) parameter = StackItem.parse(args[0], p_type_expr) @@ -59,7 +69,7 @@ def do_run(ctx: Context, prim, args, annots): @instruction('PATCH', args_len=2) def do_patch(ctx: Context, prim, args, annots): - key, = parse_prim_expr(args[0]) + key, _ = parse_prim_expr(args[0]) assert key in patch_prim, f'expected one of {", ".join(patch_prim)}, got {args[0]}' if key in ['AMOUNT', 'BALANCE']: res = Mutez(get_int(args[1])) @@ -76,7 +86,7 @@ def do_patch(ctx: Context, prim, args, annots): @instruction('PATCH', args_len=1) def do_unset(ctx: Context, prim, args, annots): - key, = parse_prim_expr(args[0]) + key, _ = parse_prim_expr(args[0]) assert key in patch_prim, f'expected one of {", ".join(patch_prim)}, got {args[0]}' ctx.unset(key) diff --git a/pytezos/repl/interpreter.py b/pytezos/repl/interpreter.py index a955a33ff..387511279 100644 --- a/pytezos/repl/interpreter.py +++ b/pytezos/repl/interpreter.py @@ -1,5 +1,5 @@ from copy import deepcopy -from pprint import pformat, pprint +from pprint import pformat from pytezos.michelson.grammar import MichelsonParser, MichelsonParserError from pytezos.michelson.converter import michelson_to_micheline, micheline_to_michelson @@ -21,7 +21,8 @@ def format_stack_item(item: StackItem): def format_stdout(items): - return ''.join(items) + res = ''.join(items).lstrip('\n') + return res if any(map(lambda x: x in res, ['\n', 'PRINT'])) else '' def format_result(result): @@ -30,7 +31,7 @@ def format_result(result): elif isinstance(result, StackItem): return [format_stack_item(result)] elif isinstance(result, list): - if isinstance(result[0], StackItem): + if len(result) > 0 and isinstance(result[0], StackItem): return list(map(format_stack_item, result)) else: return micheline_to_michelson(result) @@ -71,27 +72,29 @@ def execute(self, code): int_res['stderr'] = format_stderr(e) return int_res - backup = deepcopy(self.ctx.stack) + backup = deepcopy(self.ctx) try: res = do_interpret(self.ctx, code_expr) if res is None and len(self.ctx) > 0: res = self.ctx.peek() + int_res['result'] = format_result(res) + int_res['stdout'] = format_stdout(self.ctx.stdout) int_res['success'] = True + self.ctx.stdout.clear() except MichelsonRuntimeError as e: if self.debug: raise e + int_res['stderr'] = format_stderr(e) - self.ctx.stack = backup - finally: int_res['stdout'] = format_stdout(self.ctx.stdout) - self.ctx.stdout.clear() + self.ctx = backup + finally: if self.debug: print(int_res['stdout']) if self.debug and int_res['result']: - print('RETURN: ') - pprint(int_res['result']) + print('RETURN: ' + pformat(int_res['result'])) return int_res diff --git a/pytezos/repl/parser.py b/pytezos/repl/parser.py index e30ab550b..5b41b1825 100644 --- a/pytezos/repl/parser.py +++ b/pytezos/repl/parser.py @@ -107,6 +107,10 @@ def get_int(val_expr): return int(get_core_val(val_expr, core_type='int')) +def get_bool(val_expr): + return dispatch_prim_map(val_expr, {'True': True, 'False': False}) + + def get_bytes(val_expr): return bytes.fromhex(get_core_val(val_expr, core_type='bytes')) @@ -357,13 +361,16 @@ def parse_key_hash(val_expr, type_args): @primitive('signature') def parse_signature(val_expr, type_args): - return get_string(val_expr) + return dispatch_core_map(val_expr, { + 'bytes': lambda x: encoding.parse_signature(bytes.fromhex(x)), + 'string': lambda x: x + }) @primitive('chain_id') def parse_chain_id(val_expr, type_args): return dispatch_core_map(val_expr, { - 'bytes': lambda x: encoding.base58_encode(bytes.fromhex(x), b'Net').decode(), + 'bytes': lambda x: encoding.parse_chain_id(bytes.fromhex(x)), 'string': lambda x: x })