def fill_content(content: Dict[str, Any]) -> Dict[str, Any]: if validation_passes[content['kind']] == 3: _gas_limit, _storage_limit, _fee = gas_limit, storage_limit, fee if _gas_limit is None: _gas_limit = OperationResult.consumed_gas(content) if content['kind'] in ['origination', 'transaction']: _gas_limit += gas_reserve if storage_limit is None: _paid_storage_size_diff = OperationResult.paid_storage_size_diff(content) _burned = OperationResult.burned(content) _storage_limit = _paid_storage_size_diff + _burned if content['kind'] in ['origination', 'transaction']: _storage_limit += burn_reserve if _fee is None: _fee = calculate_fee(content, _gas_limit, extra_size) current_counter = int(content['counter']) content.update( gas_limit=str(_gas_limit), storage_limit=str(_storage_limit), fee=str(_fee), counter=str(current_counter + self.context.get_counter_offset()), ) content.pop('metadata') logger.debug("autofilled transaction content: %s" % content) return content
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 pull(cls, stack: MichelsonStack, stdout: List[str], context: AbstractContext): res_type: MichelsonType literal: Type[MichelineLiteral] res_type, literal = cls.args # type: ignore res = res_type.from_literal(literal) if res_type.is_pushable(): expected_res = stack.pop1() elif res_type.prim == 'big_map': if issubclass(literal, MichelineSequence): expected_res = stack.pop1() else: expected_res = stack.pop1() # NOTE: We care about validity of the pointer, not it's contents res = expected_res else: raise Exception( f'`{res_type.prim}` is neither pushable nor big_map') if res != expected_res: logger.debug('expected: %s(%s)', expected_res.__class__.__name__, expected_res.__dict__) logger.debug('actual: %s(%s)', res.__class__.__name__, res.__dict__) raise Exception('Stack content is not equal to expected') stdout.append(format_stdout(cls.prim, [], [res])) # type: ignore
def walk_state_change_interval(head: int, last: int, get: Callable, equals: Callable, head_value: Any, last_value: Any) -> Generator: level = last value = last_value while not equals(value, head_value): level, value = find_state_change(head, level, get, equals, pred_value=value) logger.debug('%s -> %s at %s' % last_value, value, level) yield level, value
def wrapper(*args, **kwargs): for attempt in range(REQUEST_RETRY_COUNT): logger.debug('Node request attempt %s/%s', attempt + 1, REQUEST_RETRY_COUNT) try: return fn(*args, **kwargs) except requests.exceptions.ConnectionError as e: if attempt + 1 == REQUEST_RETRY_COUNT: raise e logger.warning(e) time.sleep(REQUEST_RETRY_SLEEP)
def bisect(start: int, end: int): if end == start + 1: return end, get(end) level = (end + start) // 2 value = get(level) logger.debug('%s at level %s' % value, level) if equals(value, pred_value): return bisect(level, end) else: return bisect(start, level)
def request(self, method: str, path: str, **kwargs) -> requests.Response: """Perform HTTP request to node. :param method: one of GET/POST/PUT/DELETE :param path: path to endpoint :param kwargs: requests.request arguments :raises RpcError: node has returned an error :returns: node response """ logger.debug('>>>>> %s %s\n%s', method, path, json.dumps(kwargs, indent=4)) res = requests.request( method=method, url=_urljoin(self.uri[0], path), headers={ 'content-type': 'application/json', 'user-agent': 'PyTezos', }, **kwargs, ) if res.status_code == 404: logger.debug('<<<<< %s\n%s', res.status_code, res.text) raise RpcError(f'Not found: {path}') if res.status_code != 200: logger.debug('<<<<< %s\n%s', res.status_code, pformat(res.text, indent=4)) raise RpcError.from_response(res) logger.debug('<<<<< %s\n%s', res.status_code, json.dumps(res.json(), indent=4)) return res
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 find_operation(self, operation_group_hash) -> dict: """ Find operation by hash. :param operation_group_hash: base58 :raises: StopIteration if not found """ last, head = self.get_range() if self._start < 0: levels = range(head, max(1, last - 1), -1) else: levels = range(last, head, 1) for block_level in levels: try: logger.debug(f'checking level %s...' % block_level) return self._getitem(block_level).operations[operation_group_hash]() except StopIteration: continue raise StopIteration(operation_group_hash)
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) res, stdout, error = Interpreter.run_view( parameter=self.parameters['value'], entrypoint=self.parameters['entrypoint'], storage=initial_storage, context=self.context) if error: logger.debug('\n'.join(stdout)) raise error return res
def get_counter_offset(self) -> int: """Return current count of pending transactions in mempool. """ if self.key is None: raise Exception('`key` is not set') if self.shell is None: raise Exception('`shell` is not set') counter_offset = 0 key_hash = self.key.public_key_hash() mempool = self.shell.mempool.pending_operations() for operations in mempool.values(): for operation in operations: if isinstance(operation, list): operation = operation[1] for content in operation.get('contents', []): if content.get('source') == key_hash: logger.debug("pending transaction in mempool: %s" % content) counter_offset += 1 logger.debug("counter offset: %s" % counter_offset) return counter_offset
def find_state_change_intervals(head: int, last: int, get: Callable, equals: Callable, step=60) -> Generator: succ_value = get(head) logger.debug('%s at head %s' % succ_value, head) for level in range(head - step, last, -step): value = get(level) logger.debug('%s at level %s' % value, level) if not equals(value, succ_value): logger.debug('%s -> %s at (%s, {level + step})' % value, succ_value, level) yield level + step, succ_value, level, value succ_value = value
def request(self, method, path, **kwargs) -> requests.Response: logger.debug('>>>>> %s %s\n%s', method, path, json.dumps(kwargs, indent=4)) res = self._session.request(method=method, url=urljoin(self.uri, path), headers={ 'content-type': 'application/json', 'user-agent': 'PyTezos' }, **kwargs) if res.status_code == 404: logger.debug('<<<<< %s\n%s', res.status_code, res.text) raise RpcError(f'Not found: {path}') elif res.status_code != 200: logger.debug('<<<<< %s\n%s', res.status_code, pformat(res.text, indent=4)) raise RpcError.from_response(res) logger.debug('<<<<< %s\n%s', res.status_code, json.dumps(res.json(), indent=4)) return res