Beispiel #1
0
    def newFilter(self, filter_dict):
        if not isinstance(filter_dict, dict):
            raise BadRequestError('Filter must be an object')
        b0 = self.json_rpc_server.get_block(
            block_id_decoder(filter_dict.get('fromBlock', 'latest')))
        b1 = self.json_rpc_server.get_block(
            block_id_decoder(filter_dict.get('toBlock', 'latest')))
        if b1.number < b0.number:
            raise BadRequestError('fromBlock must be prior or equal to toBlock')
        address = filter_dict.get('address', None)
        if is_string(address):
            addresses = [address_decoder(address)]
        elif isinstance(address, Iterable):
            addresses = [address_decoder(addr) for addr in address]
        elif address is None:
            addresses = None
        else:
            raise JSONRPCInvalidParamsError('Parameter must be address or list of addresses')
        if 'topics' in filter_dict:
            topics = []
            for topic in filter_dict['topics']:
                if topic is not None:
                    topics.append(big_endian_to_int(data_decoder(topic)))
                else:
                    topics.append(None)
        else:
            topics = None

        blocks = [b1]
        while blocks[-1] != b0:
            blocks.append(blocks[-1].get_parent())
        filter_ = Filter(self.chain.chain, reversed(blocks), addresses, topics)
        self.filters[self.next_id] = filter_
        self.next_id += 1
        return self.next_id - 1
Beispiel #2
0
    def get_block(self, block_id=None):
        """Return the block identified by `block_id`.

        This method also sets :attr:`default_block` to the value of `block_id`
        which will be returned if, at later calls, `block_id` is not provided.

        Subdispatchers using this function have to ensure sure that a
        chainmanager is registered via :attr:`required_services`.

        :param block_id: either the block number as integer or 'pending',
                        'earliest' or 'latest', or `None` for the default
                        block
        :returns: the requested block
        :raises: :exc:`KeyError` if the block does not exist
        """
        assert 'chain' in self.app.services
        chain = self.app.services.chain.chain
        if block_id is None:
            block_id = self.default_block
        else:
            self.default_block = block_id
        if block_id == 'pending':
            return self.app.services.chain.chain.head_candidate
        if block_id == 'latest':
            return chain.head
        if block_id == 'earliest':
            block_id = 0
        if is_numeric(block_id):
            # by number
            hash_ = chain.index.get_block_by_number(block_id)
        else:
            # by hash
            assert is_string(block_id)
            hash_ = block_id
        return chain.get(hash_)
Beispiel #3
0
    def newFilter(self, filter_dict):
        if not isinstance(filter_dict, dict):
            raise BadRequestError('Filter must be an object')
        required_keys = set(['fromBlock', 'toBlock'])
        if not required_keys.issubset(set(filter_dict.keys())):
            raise BadRequestError('Invalid filter object')

        b0 = self.json_rpc_server.get_block(filter_dict['fromBlock'])
        b1 = self.json_rpc_server.get_block(filter_dict['toBlock'])
        address = filter_dict.get('address', None)
        if is_string(address):
            addresses = [address_decoder(address)]
        elif isinstance(address, Iterable):
            addresses = [address_decoder(addr) for addr in address]
        elif address is None:
            addresses = None
        else:
            raise JSONRPCInvalidParamsError('Parameter must be address or list of addresses')
        topics = [data_decoder(topic) for topic in filter_dict.get('topics', [])]

        blocks = [b0]
        while blocks[-1] != b1:
            blocks.append(blocks[-1].get_parent())
        filter_ = Filter(self.chain.chain, blocks, addresses, topics)
        self.filters[self.next_id] = filter_
        self.next_id += 1
        return self.next_id - 1
Beispiel #4
0
 def coinbase(self):
     """Return the address that should be used as coinbase for new blocks.
     The coinbase address is given by the config field pow.coinbase_hex. If this does not exist
     or is `None`, the address of the first account is used instead. If there are no accounts,
     the coinbase is `DEFAULT_COINBASE`.
     :raises: :exc:`ValueError` if the coinbase is invalid (no string, wrong length) or there is
              no account for it and the config flag `accounts.check_coinbase` is set (does not
              apply to the default coinbase)
     """
     cb_hex = self.app.config.get('pow', {}).get('coinbase_hex')
     if cb_hex is None:
         if not self.accounts_with_address:
             return DEFAULT_COINBASE
         cb = self.accounts_with_address[0].address
     else:
         if not is_string(cb_hex):
             raise ValueError('coinbase must be string')
         try:
             cb = decode_hex(remove_0x_head(cb_hex))
         except (ValueError, TypeError):
             raise ValueError('invalid coinbase')
     if len(cb) != 20:
         raise ValueError('wrong coinbase length')
     if self.config['accounts']['must_include_coinbase']:
         if cb not in (acct.address for acct in self.accounts):
             raise ValueError('no account for coinbase')
     return cb
Beispiel #5
0
    def newFilter(self, filter_dict):
        if not isinstance(filter_dict, dict):
            raise BadRequestError('Filter must be an object')
        required_keys = set(['fromBlock', 'toBlock'])
        if not required_keys.issubset(set(filter_dict.keys())):
            raise BadRequestError('Invalid filter object')

        b0 = self.json_rpc_server.get_block(block_id_decoder(filter_dict['fromBlock']))
        b1 = self.json_rpc_server.get_block(block_id_decoder(filter_dict['toBlock']))
        if b1.number < b0.number:
            raise BadRequestError('fromBlock must be prior or equal to toBlock')
        address = filter_dict.get('address', None)
        if is_string(address):
            addresses = [address_decoder(address)]
        elif isinstance(address, Iterable):
            addresses = [address_decoder(addr) for addr in address]
        elif address is None:
            addresses = None
        else:
            raise JSONRPCInvalidParamsError('Parameter must be address or list of addresses')
        topics = [data_decoder(topic) for topic in filter_dict.get('topics', [])]

        blocks = [b1]
        while blocks[-1] != b1:
            blocks.append(blocks[-1].get_parent())
        filter_ = Filter(self.chain.chain, reversed(blocks), addresses, topics)
        self.filters[self.next_id] = filter_
        self.next_id += 1
        return self.next_id - 1
Beispiel #6
0
    def get_block(self, block_id=None):
        """Return the block identified by `block_id`.

        This method also sets :attr:`default_block` to the value of `block_id`
        which will be returned if, at later calls, `block_id` is not provided.

        Subdispatchers using this function have to ensure sure that a
        chainmanager is registered via :attr:`required_services`.

        :param block_id: either the block number as integer or 'pending',
                        'earliest' or 'latest', or `None` for the default
                        block
        :returns: the requested block
        :raises: :exc:`KeyError` if the block does not exist
        """
        assert 'chain' in self.app.services
        chain = self.app.services.chain.chain
        if block_id is None:
            block_id = self.default_block
        else:
            self.default_block = block_id
        if block_id == 'pending':
            return self.app.services.chain.chain.head_candidate
        if block_id == 'latest':
            return chain.head
        if block_id == 'earliest':
            block_id = 0
        if is_numeric(block_id):
            # by number
            hash_ = chain.index.get_block_by_number(block_id)
        else:
            # by hash
            assert is_string(block_id)
            hash_ = block_id
        return chain.get(hash_)
Beispiel #7
0
 def coinbase(self):
     """Return the address that should be used as coinbase for new blocks.
     The coinbase address is given by the config field pow.coinbase_hex. If this does not exist
     or is `None`, the address of the first account is used instead. If there are no accounts,
     the coinbase is `DEFAULT_COINBASE`.
     :raises: :exc:`ValueError` if the coinbase is invalid (no string, wrong length) or there is
              no account for it and the config flag `accounts.check_coinbase` is set (does not
              apply to the default coinbase)
     """
     cb_hex = self.app.config.get('pow', {}).get('coinbase_hex')
     if cb_hex is None:
         if not self.accounts_with_address:
             return DEFAULT_COINBASE
         cb = self.accounts_with_address[0].address
     else:
         if not is_string(cb_hex):
             raise ValueError('coinbase must be string')
         try:
             cb = decode_hex(remove_0x_head(cb_hex))
         except (ValueError, TypeError):
             raise ValueError('invalid coinbase')
     if len(cb) != 20:
         raise ValueError('wrong coinbase length')
     if self.config['accounts']['must_include_coinbase']:
         if cb not in (acct.address for acct in self.accounts):
             raise ValueError('no account for coinbase')
     return cb
Beispiel #8
0
    def __init__(self, contract_interface):
        if is_string(contract_interface):
            contract_interface = json_decode(contract_interface)

        self.constructor_data = None
        self.function_data = {}
        self.event_data = {}

        for description in contract_interface:
            encode_types = [
                element['type'] for element in description['inputs']
            ]

            signature = [(element['type'], element['name'])
                         for element in description['inputs']]

            # type can be omitted, defaulting to function
            if description.get('type', 'function') == 'function':
                normalized_name = normalize_name(description['name'])

                decode_types = [
                    element['type'] for element in description['outputs']
                ]

                self.function_data[normalized_name] = {
                    'prefix': method_id(normalized_name, encode_types),
                    'encode_types': encode_types,
                    'decode_types': decode_types,
                    'is_constant': description.get('constant', False),
                    'signature': signature,
                }

            elif description['type'] == 'event':
                normalized_name = normalize_name(description['name'])

                indexed = [
                    element['indexed'] for element in description['inputs']
                ]
                names = [element['name'] for element in description['inputs']]
                # event_id == topics[0]
                self.event_data[event_id(normalized_name, encode_types)] = {
                    'types': encode_types,
                    'name': normalized_name,
                    'names': names,
                    'indexed': indexed,
                    'anonymous': description.get('anonymous', False),
                }

            elif description['type'] == 'constructor':
                if self.constructor_data is not None:
                    raise ValueError('Only one constructor is supported.')

                self.constructor_data = {
                    'encode_types': encode_types,
                    'signature': signature,
                }

            else:
                raise ValueError('Unknown type {}'.format(description['type']))
Beispiel #9
0
def decint(n):
    if is_numeric(n) and n < 2**256 and n > -2**255:
        return n
    elif is_numeric(n):
        raise Exception("Number out of range: %r" % n)
    elif is_string(n) and len(n) == 40:
        return big_endian_to_int(decode_hex(n))
    elif is_string(n) and len(n) <= 32:
        return big_endian_to_int(n)
    elif is_string(n) and len(n) > 32:
        raise Exception("String too long: %r" % n)
    elif n is True:
        return 1
    elif n is False or n is None:
        return 0
    else:
        raise Exception("Cannot encode integer: %r" % n)
Beispiel #10
0
def decint(n):
    if is_numeric(n) and n < 2**256 and n > -2**255:
        return n
    elif is_numeric(n):
        raise Exception("Number out of range: %r" % n)
    elif is_string(n) and len(n) == 40:
        return big_endian_to_int(decode_hex(n))
    elif is_string(n) and len(n) <= 32:
        return big_endian_to_int(n)
    elif is_string(n) and len(n) > 32:
        raise Exception("String too long: %r" % n)
    elif n is True:
        return 1
    elif n is False or n is None:
        return 0
    else:
        raise Exception("Cannot encode integer: %r" % n)
Beispiel #11
0
 def from_snapshot(cls, snapshot_data, env, executing_on_head=False):
     state = State(env=env)
     if "alloc" in snapshot_data:
         for addr, data in snapshot_data["alloc"].items():
             if len(addr) == 40:
                 addr = decode_hex(addr)
             assert len(addr) == 20
             if 'wei' in data:
                 state.set_balance(addr, parse_as_int(data['wei']))
             if 'balance' in data:
                 state.set_balance(addr, parse_as_int(data['balance']))
             if 'code' in data:
                 state.set_code(addr, parse_as_bin(data['code']))
             if 'nonce' in data:
                 state.set_nonce(addr, parse_as_int(data['nonce']))
             if 'storage' in data:
                 for k, v in data['storage'].items():
                     state.set_storage_data(
                         addr,
                         big_endian_to_int(parse_as_bin(k)),
                         big_endian_to_int(parse_as_bin(v)))
     elif "state_root" in snapshot_data:
         state.trie.root_hash = parse_as_bin(snapshot_data["state_root"])
     else:
         raise Exception(
             "Must specify either alloc or state root parameter")
     for k, default in STATE_DEFAULTS.items():
         default = copy.copy(default)
         v = snapshot_data[k] if k in snapshot_data else None
         if is_numeric(default):
             setattr(state, k, parse_as_int(v)
                     if k in snapshot_data else default)
         elif is_string(default):
             setattr(state, k, parse_as_bin(v)
                     if k in snapshot_data else default)
         elif k == 'prev_headers':
             if k in snapshot_data:
                 headers = [dict_to_prev_header(h) for h in v]
             else:
                 headers = default
             setattr(state, k, headers)
         elif k == 'recent_uncles':
             if k in snapshot_data:
                 uncles = {}
                 for height, _uncles in v.items():
                     uncles[int(height)] = []
                     for uncle in _uncles:
                         uncles[int(height)].append(parse_as_bin(uncle))
             else:
                 uncles = default
             setattr(state, k, uncles)
     if executing_on_head:
         state.executing_on_head = True
     state.commit()
     state.changed = {}
     return state
Beispiel #12
0
 def set_root_hash(self, root_hash):
     assert is_string(root_hash)
     assert len(root_hash) in [0, 32]
     if self.transient:
         self.transient_root_hash = root_hash
         return
     if root_hash == BLANK_ROOT:
         self.root_node = BLANK_NODE
         return
     # print(repr(root_hash))
     self.root_node = self._decode_to_node(root_hash)
Beispiel #13
0
    def update(self, key, value):
        '''
        :param key: a string
        :value: a string
        '''
        if not is_string(key):
            raise Exception("Key must be string")

        # if len(key) > 32:
        #     raise Exception("Max key length is 32")

        if not is_string(value):
            raise Exception("Value must be string")

        # if value == '':
        #     return self.delete(key)
        old_root = copy.deepcopy(self.root_node)
        self.root_node = self._update_and_delete_storage(
            self.root_node, bin_to_nibbles(to_string(key)), to_string(value))
        self.replace_root_hash(old_root, self.root_node)
Beispiel #14
0
 def set_root_hash(self, root_hash):
     assert is_string(root_hash)
     assert len(root_hash) in [0, 32]
     if self.transient:
         self.transient_root_hash = root_hash
         return
     if root_hash == BLANK_ROOT:
         self.root_node = BLANK_NODE
         return
     # print repr(root_hash)
     self.root_node = self._decode_to_node(root_hash)
Beispiel #15
0
    def new(cls, password, key=None, uuid=None, path=None):
        """

        :param password:
        :param key:
        :param uuid:
        :param path:
        :return:
        """
        if key is None:
            key = mk_random_privkey()

        # [NOTE]: key and password should be bytes
        if not is_string(key):
            key = to_string(key)
        if not is_string(password):
            password = to_string(password)

        account = cls.create(key)
        keystore = Account.encrypt(account.privateKey, password)
        return Account(keystore, password, path)
Beispiel #16
0
    def new(cls, password, key=None, uuid=None, path=None):
        """Create a new account.

        Note that this creates the account in memory and does not store it on disk.

        :param password: the password used to encrypt the private key
        :param key: the private key, or `None` to generate a random one
        :param uuid: an optional id
        """
        if key is None:
            key = mk_random_privkey()

        # [NOTE]: key and password should be bytes
        if not is_string(key):
            key = to_string(key)
        if not is_string(password):
            password = to_string(password)

        keystore = keys.make_keystore_json(key, password)
        keystore['id'] = uuid
        return Account(keystore, password, path)
Beispiel #17
0
    def update(self, key, value):
        '''
        :param key: a string
        :value: a string
        '''
        if not is_string(key):
            raise Exception("Key must be string")

        # if len(key) > 32:
        #     raise Exception("Max key length is 32")

        if not is_string(value):
            raise Exception("Value must be string")

        # if value == '':
        #     return self.delete(key)
        old_root = copy.deepcopy(self.root_node)
        self.root_node = self._update_and_delete_storage(
            self.root_node,
            bin_to_nibbles(to_string(key)),
            value)
        self.replace_root_hash(old_root, self.root_node)
Beispiel #18
0
    def delete(self, key):
        '''
        :param key: a string with length of [0, 32]
        '''
        if not is_string(key):
            raise Exception("Key must be string")

        if len(key) > 32:
            raise Exception("Max key length is 32")

        old_root = copy.deepcopy(self.root_node)
        self.root_node = self._delete_and_delete_storage(
            self.root_node, bin_to_nibbles(to_string(key)))
        self.replace_root_hash(old_root, self.root_node)
Beispiel #19
0
    def delete(self, key):
        '''
        :param key: a string with length of [0, 32]
        '''
        if not is_string(key):
            raise Exception("Key must be string")

        if len(key) > 32:
            raise Exception("Max key length is 32")

        old_root = copy.deepcopy(self.root_node)
        self.root_node = self._delete_and_delete_storage(
            self.root_node,
            bin_to_nibbles(to_string(key)))
        self.replace_root_hash(old_root, self.root_node)
Beispiel #20
0
    def newFilter(self, filter_dict):
        log.debug('in newFilter', filter_dict=filter_dict)
        if not isinstance(filter_dict, dict):
            raise BadRequestError('Filter must be an object')
        address = filter_dict.get('address', None)
        if is_string(address):
            addresses = [address_decoder(address)]
        elif isinstance(address, Iterable):
            addresses = [address_decoder(addr) for addr in address]
        elif address is None:
            addresses = None
        else:
            raise JSONRPCInvalidParamsError('Parameter must be address or list of addresses')
        if 'topics' in filter_dict:
            topics = []
            for topic in filter_dict['topics']:
                if topic is not None:
                    log.debug('with topic', topic=topic)
                    log.debug('decoded', topic=data_decoder(topic))
                    log.debug('int', topic=big_endian_to_int(data_decoder(topic)))
                    topics.append(big_endian_to_int(data_decoder(topic)))
                else:
                    topics.append(None)
        else:
            topics = None


        fromBlock = filter_dict.get('fromBlock')
        toBlock = filter_dict.get('toBlock') or 'latest'

        if toBlock in ('latest', 'pending'):
            assert fromBlock in (None, toBlock) , 'latest/pending does not support ranges'
            filter_ = Filter(self.chain.chain, blocks=[], addresses=addresses, topics=topics,
                pending=bool(toBlock==b'pending'), latest=bool(toBlock==b'latest'))
        else:
            assert fromBlock not in ('latest', 'pending')
            b0 = self.json_rpc_server.get_block(block_id_decoder(fromBlock))
            b1 = self.json_rpc_server.get_block(block_id_decoder(toBlock))
            if b1.number < b0.number:
                raise BadRequestError('fromBlock must be prior or equal to toBlock')
            blocks = [b1]
            while blocks[-1] != b0:
                blocks.append(blocks[-1].get_parent())
            filter_ = Filter(self.chain.chain, blocks=list(
                reversed(blocks)), addresses=addresses, topics=topics)
        self.filters[self.next_id] = filter_
        self.next_id += 1
        return self.next_id - 1
Beispiel #21
0
def decint(n, signed=False):  # pylint: disable=invalid-name,too-many-branches
    """ Decode an unsigned/signed integer. """

    if isinstance(n, str):
        n = utils.to_string(n)

    if n is True:
        return 1

    if n is False:
        return 0

    if n is None:
        return 0

    if is_numeric(n):
        if signed:
            if not -TT255 <= n <= TT255 - 1:
                raise EncodingError('Number out of range: %r' % n)
        else:
            if not 0 <= n <= TT256 - 1:
                raise EncodingError('Number out of range: %r' % n)

        return n

    if is_string(n):
        if len(n) > 32:
            raise EncodingError('String too long: %r' % n)

        if len(n) == 40:
            int_bigendian = decode_hex(n)
        else:
            int_bigendian = n  # pylint: disable=redefined-variable-type

        result = big_endian_to_int(int_bigendian)
        if signed:
            if result >= TT255:
                result -= TT256

            if not -TT255 <= result <= TT255 - 1:
                raise EncodingError('Number out of range: %r' % n)
        else:
            if not 0 <= result <= TT256 - 1:
                raise EncodingError('Number out of range: %r' % n)

        return result

    raise EncodingError('Cannot decode integer: %r' % n)
Beispiel #22
0
 def __init__(self, full_signature):
     self.function_data = {}
     self.event_data = {}
     v = vars(self)
     if is_string(full_signature):
         full_signature = json_decode(full_signature)
     for sig_item in full_signature:
         if sig_item['type'] == 'constructor':
             continue
         encode_types = [f['type'] for f in sig_item['inputs']]
         signature = [(f['type'], f['name']) for f in sig_item['inputs']]
         name = sig_item['name']
         if '(' in name:
             name = name[:name.find('(')]
         if name in v:
             i = 2
             while name + utils.to_string(i) in v:
                 i += 1
             name += utils.to_string(i)
             sys.stderr.write("Warning: multiple methods with the same "
                              " name. Use %s to call %s with types %r"
                              % (name, sig_item['name'], encode_types))
         if sig_item['type'] == 'function':
             decode_types = [f['type'] for f in sig_item['outputs']]
             is_unknown_type = len(sig_item['outputs']) and \
                 sig_item['outputs'][0]['name'] == 'unknown_out'
             self.function_data[name] = {
                 "prefix": method_id(name, encode_types),
                 "encode_types": encode_types,
                 "decode_types": decode_types,
                 "is_unknown_type": is_unknown_type,
                 "is_constant": sig_item.get('constant', False),
                 "signature": signature
             }
         elif sig_item['type'] == 'event':
             indexed = [f['indexed'] for f in sig_item['inputs']]
             names = [f['name'] for f in sig_item['inputs']]
             self.event_data[event_id(name, encode_types)] = {
                 "types": encode_types,
                 "name": name,
                 "names": names,
                 "indexed": indexed,
                 "anonymous": sig_item.get('anonymous', False)
             }
Beispiel #23
0
 def __init__(self, full_signature):
     self.function_data = {}
     self.event_data = {}
     v = vars(self)
     if is_string(full_signature):
         full_signature = json_decode(full_signature)
     for sig_item in full_signature:
         encode_types = [f['type'] for f in sig_item['inputs']]
         signature = [(f['type'], f['name']) for f in sig_item['inputs']]
         name = sig_item['name']
         if '(' in name:
             name = name[:name.find('(')]
         if name in v:
             i = 2
             while name + utils.to_string(i) in v:
                 i += 1
             name += utils.to_string(i)
             sys.stderr.write("Warning: multiple methods with the same "
                              " name. Use %s to call %s with types %r" %
                              (name, sig_item['name'], encode_types))
         sig = name + '(' + ','.join(encode_types) + ')'
         if sig_item['type'] == 'function':
             prefix = big_endian_to_int(utils.sha3(sig)[:4])
             decode_types = [f['type'] for f in sig_item['outputs']]
             is_unknown_type = len(sig_item['outputs']) and \
                 sig_item['outputs'][0]['name'] == 'unknown_out'
             self.function_data[name] = {
                 "prefix": prefix,
                 "encode_types": encode_types,
                 "decode_types": decode_types,
                 "is_unknown_type": is_unknown_type,
                 "is_constant": sig_item.get('constant', False),
                 "signature": signature
             }
         elif sig_item['type'] == 'event':
             prefix = big_endian_to_int(utils.sha3(sig))
             indexed = [f['indexed'] for f in sig_item['inputs']]
             names = [f['name'] for f in sig_item['inputs']]
             self.event_data[prefix] = {
                 "types": encode_types,
                 "name": name,
                 "names": names,
                 "indexed": indexed,
             }
Beispiel #24
0
 def __init__(self, full_signature):
     self.function_data = {}
     self.event_data = {}
     v = vars(self)
     if is_string(full_signature):
         full_signature = json_decode(full_signature)
     for sig_item in full_signature:
         encode_types = [f['type'] for f in sig_item['inputs']]
         signature = [(f['type'], f['name']) for f in sig_item['inputs']]
         name = sig_item['name']
         if '(' in name:
             name = name[:name.find('(')]
         if name in v:
             i = 2
             while name + utils.to_string(i) in v:
                 i += 1
             name += utils.to_string(i)
             sys.stderr.write("Warning: multiple methods with the same "
                              " name. Use %s to call %s with types %r"
                              % (name, sig_item['name'], encode_types))
         sig = name + '(' + ','.join(encode_types) + ')'
         if sig_item['type'] == 'function':
             prefix = big_endian_to_int(utils.sha3(sig)[:4])
             decode_types = [f['type'] for f in sig_item['outputs']]
             is_unknown_type = len(sig_item['outputs']) and \
                 sig_item['outputs'][0]['name'] == 'unknown_out'
             self.function_data[name] = {
                 "prefix": prefix,
                 "encode_types": encode_types,
                 "decode_types": decode_types,
                 "is_unknown_type": is_unknown_type,
                 "is_constant": sig_item.get('constant', False),
                 "signature": signature
             }
         elif sig_item['type'] == 'event':
             prefix = big_endian_to_int(utils.sha3(sig))
             indexed = [f['indexed'] for f in sig_item['inputs']]
             names = [f['name'] for f in sig_item['inputs']]
             self.event_data[prefix] = {
                 "types": encode_types,
                 "name": name,
                 "names": names,
                 "indexed": indexed,
             }
Beispiel #25
0
 def __init__(self, full_signature):
     self.function_data = {}
     self.event_data = {}
     v = vars(self)
     if is_string(full_signature):
         full_signature = json_decode(full_signature)
     for sig_item in full_signature:
         if sig_item['type'] == 'constructor':
             continue
         encode_types = [f['type'] for f in sig_item['inputs']]
         signature = [(f['type'], f['name']) for f in sig_item['inputs']]
         name = sig_item['name']
         if '(' in name:
             name = name[:name.find('(')]
         if name in v:
             i = 2
             while name + utils.to_string(i) in v:
                 i += 1
             name += utils.to_string(i)
             sys.stderr.write("Warning: multiple methods with the same "
                              " name. Use %s to call %s with types %r" %
                              (name, sig_item['name'], encode_types))
         if sig_item['type'] == 'function':
             decode_types = [f['type'] for f in sig_item['outputs']]
             is_unknown_type = len(sig_item['outputs']) and \
                 sig_item['outputs'][0]['name'] == 'unknown_out'
             self.function_data[name] = {
                 "prefix": method_id(name, encode_types),
                 "encode_types": encode_types,
                 "decode_types": decode_types,
                 "is_unknown_type": is_unknown_type,
                 "is_constant": sig_item.get('constant', False),
                 "signature": signature
             }
         elif sig_item['type'] == 'event':
             indexed = [f['indexed'] for f in sig_item['inputs']]
             names = [f['name'] for f in sig_item['inputs']]
             self.event_data[event_id(name, encode_types)] = {
                 "types": encode_types,
                 "name": name,
                 "names": names,
                 "indexed": indexed,
                 "anonymous": sig_item.get('anonymous', False)
             }
Beispiel #26
0
def decode_filter(filter_dict, block):
    """Decodes a filter as expected by eth_newFilter or eth_getLogs to a :class:`Filter`."""
    if not isinstance(filter_dict, dict):
        raise Exception('Filter must be an object')
    address = filter_dict.get('address', None)
    if utils.is_string(address):
        addresses = [decode_hex(strip_0x(address))]
    elif isinstance(address, Iterable):
        addresses = [decode_hex(strip_0x(addr)) for addr in address]
    elif address is None:
        addresses = None
    else:
        raise Exception('Parameter must be address or list of addresses')
    if 'topics' in filter_dict:
        topics = []
        for topic in filter_dict['topics']:
            if topic is not None:
                topics.append(utils.big_endian_to_int(decode_hex(strip_0x(topic))))
            else:
                topics.append(None)
    else:
        topics = None

    from_block = filter_dict.get('fromBlock') or 'latest'
    to_block = filter_dict.get('toBlock') or 'latest'

    if from_block not in ('earliest', 'latest', 'pending'):
        from_block = decode_number(from_block)

    if to_block not in ('earliest', 'latest', 'pending'):
        to_block = decode_number(to_block)

    # check order
    block_id_dict = {
        'earliest': 0,
        'latest': block.number,
        'pending': block.number+1
    }
    range_ = [b if utils.is_numeric(b) else block_id_dict[b] for b in (from_block, to_block)]
    if range_[0] > range_[1]:
        raise Exception('fromBlock must be newer or equal to toBlock')

    return {"from_block": from_block, "to_block": to_block, "addresses": addresses, "topics": topics}
Beispiel #27
0
def quantity_decoder(data):
    """Decode `data` representing a quantity."""
    if not is_string(data):
        success = False
    elif not data.startswith('0x'):
        success = False  # must start with 0x prefix
    elif len(data) > 3 and data[2] == '0':
        success = False  # must not have leading zeros (except `0x0`)
    else:
        data = data[2:]
        # ensure even length
        if len(data) % 2 == 1:
            data = '0' + data
        try:
            return int(data, 16)
        except ValueError:
            success = False
    assert not success
    raise BadRequestError('Invalid quantity encoding')
Beispiel #28
0
def quantity_decoder(data):
    """Decode `data` representing a quantity."""
    if not is_string(data):
        success = False
    elif not data.startswith('0x'):
        success = False  # must start with 0x prefix
    elif len(data) > 3 and data[2] == '0':
        success = False  # must not have leading zeros (except `0x0`)
    else:
        data = data[2:]
        # ensure even length
        if len(data) % 2 == 1:
            data = '0' + data
        try:
            return int(data, 16)
        except ValueError:
            success = False
    assert not success
    raise BadRequestError('Invalid quantity encoding')
Beispiel #29
0
def decode_number(data):
    """Decode `data` representing a number."""
    if hasattr(data, '__int__'):
        return data
    elif not is_string(data):
        success = False
    elif not data.startswith('0x'):
        success = False  # must start with 0x prefix
    elif len(data) > 3 and data[2] == '0':
        success = False  # must not have leading zeros (except `0x0`)
    else:
        data = data[2:]
        # ensure even length
        if len(data) % 2 == 1:
            data = '0' + data
        try:
            return int(data, 16)
        except ValueError:
            success = False
    assert not success
    raise Exception('Invalid number encoding: %s' % data)
Beispiel #30
0
def decint(n, signed=False):
    if isinstance(n, str):
        n = utils.to_string(n)

    if is_numeric(n):
        min_, max_ = (-TT255, TT255 - 1) if signed else (0, TT256 - 1)
        if n > max_ or n < min_:
            raise EncodingError("Number out of range: %r" % n)
        return n
    elif is_string(n):
        if len(n) == 40:
            n = decode_hex(n)
        if len(n) > 32:
            raise EncodingError("String too long: %r" % n)

        i = big_endian_to_int(n)
        return (i - TT256) if signed and i >= TT255 else i
    elif n is True:
        return 1
    elif n is False or n is None:
        return 0
    else:
        raise EncodingError("Cannot encode integer: %r" % n)
Beispiel #31
0
    def newFilter(self, filter_dict):
        if not isinstance(filter_dict, dict):
            raise BadRequestError('Filter must be an object')
        b0 = self.json_rpc_server.get_block(
            block_id_decoder(filter_dict.get('fromBlock', 'latest')))
        b1 = self.json_rpc_server.get_block(
            block_id_decoder(filter_dict.get('toBlock', 'latest')))
        if b1.number < b0.number:
            raise BadRequestError(
                'fromBlock must be prior or equal to toBlock')
        address = filter_dict.get('address', None)
        if is_string(address):
            addresses = [address_decoder(address)]
        elif isinstance(address, Iterable):
            addresses = [address_decoder(addr) for addr in address]
        elif address is None:
            addresses = None
        else:
            raise JSONRPCInvalidParamsError(
                'Parameter must be address or list of addresses')
        if 'topics' in filter_dict:
            topics = []
            for topic in filter_dict['topics']:
                if topic is not None:
                    topics.append(big_endian_to_int(data_decoder(topic)))
                else:
                    topics.append(None)
        else:
            topics = None

        blocks = [b1]
        while blocks[-1] != b0:
            blocks.append(blocks[-1].get_parent())
        filter_ = Filter(self.chain.chain, reversed(blocks), addresses, topics)
        self.filters[self.next_id] = filter_
        self.next_id += 1
        return self.next_id - 1
Beispiel #32
0
    def __init__(self,
                 compiled_abi,
                 compiled_code,
                 name,
                 constructor_args=[],
                 sender=tester.k0,
                 endowment=0,
                 gas=None,
                 state=None,
                 log_listener=None):
        if not state:
            state = tester.state()

        self.state = state

        if is_string(compiled_abi):
            compiled_abi = abi.json_decode(compiled_abi)

        for item in compiled_abi:
            if item['type'] == 'constructor':
                item['type'] = 'function'
                item['name'] = name
                item['outputs'] = []
                break

        self._translator = tester.abi.ContractTranslator(compiled_abi)

        if log_listener:
            self.state.block.log_listeners.append(lambda x: log_listener(
                self._translator.listen(x, noprint=True)))

        if len(constructor_args) > 0:
            compiled_code += self._translator.encode(name,
                                                     constructor_args)[4:]

        self.address = self.state.evm(compiled_code, sender, endowment, gas)

        assert len(self.state.block.get_code(self.address)), \
            "Contract code empty"

        def kall_factory(f):
            def kall(*args, **kwargs):
                o = self.state._send(
                    kwargs.get('sender', tester.k0), self.address,
                    kwargs.get('value', 0), self._translator.encode(f, args),
                    **tester.dict_without(kwargs, 'sender', 'value', 'output'))
                # Compute output data
                if kwargs.get('output', '') == 'raw':
                    outdata = o['output']
                elif not o['output']:
                    outdata = None
                else:
                    outdata = self._translator.decode(f, o['output'])
                    outdata = outdata[0] if len(outdata) == 1 \
                        else outdata
                # Format output
                if kwargs.get('profiling', ''):
                    return dict_with(o, output=outdata)
                else:
                    return outdata

            return kall

        for f in self._translator.function_data:
            vars(self)[f] = kall_factory(f)
Beispiel #33
0
    def fetch_hashchain(self):
        log_st.debug('fetching hashchain')
        blockhashes_chain = [self.blockhash]  # youngest to oldest
        # For testing purposes: skip the hash downoading stage
        # import ast
        # blockhashes_chain = ast.literal_eval(open('/home/vub/blockhashes.pyast').read())[:299000]

        blockhash = self.blockhash = blockhashes_chain[-1]
        assert blockhash not in self.chain

        # get block hashes until we found a known one
        max_blockhashes_per_request = self.initial_blockhashes_per_request
        while blockhash not in self.chain:
            # proto with highest_difficulty should be the proto we got the newblock from
            blockhashes_batch = []

            # try with protos
            protocols = self.protocols
            if not protocols:
                log_st.warn('no protocols available')
                return self.exit(success=False)

            for proto in protocols:
                log.debug('syncing with', proto=proto)
                if proto.is_stopped:
                    continue

                # request
                assert proto not in self.requests
                deferred = AsyncResult()
                self.requests[proto] = deferred
                proto.send_getblockhashes(blockhash, max_blockhashes_per_request)
                try:
                    blockhashes_batch = deferred.get(block=True,
                                                     timeout=self.blockhashes_request_timeout)
                except gevent.Timeout:
                    log_st.warn('syncing hashchain timed out')
                    continue
                finally:
                    # is also executed 'on the way out' when any other clause of the try statement
                    # is left via a break, continue or return statement.
                    del self.requests[proto]

                if not blockhashes_batch:
                    log_st.warn('empty getblockhashes result')
                    continue
                if not all(isinstance(bh, bytes) for bh in blockhashes_batch):
                    log_st.warn('got wrong data type', expected='bytes',
                                received=type(blockhashes_batch[0]))
                    continue
                break

            if not blockhashes_batch:
                log_st.warn('syncing failed with all peers', num_protos=len(protocols))
                return self.exit(success=False)

            for blockhash in blockhashes_batch:  # youngest to oldest
                assert utils.is_string(blockhash)
                if blockhash not in self.chain:
                    blockhashes_chain.append(blockhash)
                else:
                    log_st.debug('found known blockhash', blockhash=utils.encode_hex(blockhash),
                                 is_genesis=bool(blockhash == self.chain.genesis.hash))
                    break
            log_st.debug('downloaded ' + str(len(blockhashes_chain)) + ' block hashes, ending with %s' % utils.encode_hex(blockhashes_chain[-1]))
            max_blockhashes_per_request = self.max_blockhashes_per_request

        self.fetch_blocks(blockhashes_chain)
Beispiel #34
0
def encode_single(typ, arg):
    base, sub, _ = typ
    # Unsigned integers: uint<sz>
    if base == 'uint':
        sub = int(sub)
        i = decint(arg)

        if not 0 <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i), 32)
    # bool: int<sz>
    elif base == 'bool':
        assert isinstance(arg, bool)
        return zpad(encode_int(int(arg)), 32)
    # Signed integers: int<sz>
    elif base == 'int':
        sub = int(sub)
        i = decint(arg)
        if not -2**(sub - 1) <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i % 2**sub), 32)
    # Unsigned reals: ureal<high>x<low>
    elif base == 'ureal':
        high, low = [int(x) for x in sub.split('x')]
        if not 0 <= arg < 2**high:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(arg * 2**low), 32)
    # Signed reals: real<high>x<low>
    elif base == 'real':
        high, low = [int(x) for x in sub.split('x')]
        if not -2**(high - 1) <= arg < 2**(high - 1):
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int((arg % 2**high) * 2**low), 32)
    # Strings
    elif base == 'string' or base == 'bytes':
        if not is_string(arg):
            raise EncodingError("Expecting string: %r" % arg)
        # Fixed length: string<sz>
        if len(sub):
            assert int(sub) <= 32
            assert len(arg) <= int(sub)
            return arg + b'\x00' * (32 - len(arg))
        # Variable length: string
        else:
            return zpad(encode_int(len(arg)), 32) + \
                arg + \
                b'\x00' * (utils.ceil32(len(arg)) - len(arg))
    # Hashes: hash<sz>
    elif base == 'hash':
        if not (int(sub) and int(sub) <= 32):
            raise EncodingError("too long: %r" % arg)
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == len(sub):
            return zpad(arg, 32)
        elif len(arg) == len(sub) * 2:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse hash: %r" % arg)
    # Addresses: address (== hash160)
    elif base == 'address':
        assert sub == ''
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == 20:
            return zpad(arg, 32)
        elif len(arg) == 40:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse address: %r" % arg)
    raise EncodingError("Unhandled type: %r %r" % (base, sub))
Beispiel #35
0
    def add_block(self, block):
        now = self.localtime
        # Are we receiving the block too early?
        if block.header.timestamp > now:
            i = 0
            while i < len(self.time_queue
                          ) and block.timestamp > self.time_queue[i].timestamp:
                i += 1
            self.time_queue.insert(i, block)
            log.info(
                'Block received too early (%d vs %d). Delaying for %d seconds'
                % (now, block.header.timestamp, block.header.timestamp - now))
            return False
        # Is the block being added to the head?
        if block.header.prevhash == self.head_hash:
            log.info('Adding to head',
                     head=encode_hex(block.header.prevhash[:4]))
            self.state.deletes = []
            self.state.changed = {}
            try:
                apply_block(self.state, block)
            except (AssertionError, KeyError, ValueError, InvalidTransaction,
                    VerificationFailed) as e:
                log.info('Block %d (%s) with parent %s invalid, reason: %s' %
                         (block.number, encode_hex(block.header.hash[:4]),
                          encode_hex(block.header.prevhash[:4]), str(e)))
                return False
            self.db.put(b'block:%d' % block.header.number, block.header.hash)
            # side effect: put 'score:' cache in db
            block_score = self.get_score(block)
            self.head_hash = block.header.hash
            for i, tx in enumerate(block.transactions):
                self.db.put(b'txindex:' + tx.hash,
                            rlp.encode([block.number, i]))
            assert self.get_blockhash_by_number(
                block.header.number) == block.header.hash
            deletes = self.state.deletes
            changed = self.state.changed
        # Or is the block being added to a chain that is not currently the
        # head?
        elif block.header.prevhash in self.env.db:
            log.info(
                'Receiving block %d (%s) not on head (%s), adding to secondary post state %s'
                % (block.number, encode_hex(
                    block.header.hash[:4]), encode_hex(self.head_hash[:4]),
                   encode_hex(block.header.prevhash[:4])))
            temp_state = self.mk_poststate_of_blockhash(block.header.prevhash)
            try:
                apply_block(temp_state, block)
            except (AssertionError, KeyError, ValueError, InvalidTransaction,
                    VerificationFailed) as e:
                log.info('Block %s with parent %s invalid, reason: %s' %
                         (encode_hex(block.header.hash[:4]),
                          encode_hex(block.header.prevhash[:4]), str(e)))
                return False
            deletes = temp_state.deletes
            block_score = self.get_score(block)
            changed = temp_state.changed
            # If the block should be the new head, replace the head
            if block_score > self.get_score(self.head):
                b = block
                new_chain = {}
                # Find common ancestor
                while b.header.number >= int(self.db.get(b'GENESIS_NUMBER')):
                    new_chain[b.header.number] = b
                    key = b'block:%d' % b.header.number
                    orig_at_height = self.db.get(
                        key) if key in self.db else None
                    if orig_at_height == b.header.hash:
                        break
                    if b.prevhash not in self.db or self.db.get(
                            b.prevhash) == b'GENESIS':
                        break
                    b = self.get_parent(b)
                replace_from = b.header.number
                # Replace block index and tx indices, and edit the state cache

                # Get a list of all accounts that have been edited along the old and
                # new chains
                changed_accts = {}
                # Read: for i in range(common ancestor block number...new block
                # number)
                for i in itertools.count(replace_from):
                    log.info('Rewriting height %d' % i)
                    key = b'block:%d' % i
                    # Delete data for old blocks
                    orig_at_height = self.db.get(
                        key) if key in self.db else None
                    if orig_at_height:
                        orig_block_at_height = self.get_block(orig_at_height)
                        log.info('%s no longer in main chain' %
                                 encode_hex(orig_block_at_height.header.hash))
                        # Delete from block index
                        self.db.delete(key)
                        # Delete from txindex
                        for tx in orig_block_at_height.transactions:
                            if b'txindex:' + tx.hash in self.db:
                                self.db.delete(b'txindex:' + tx.hash)
                        # Add to changed list
                        acct_list = self.db.get(b'changed:' +
                                                orig_block_at_height.hash)
                        for j in range(0, len(acct_list), 20):
                            changed_accts[acct_list[j:j + 20]] = True
                    # Add data for new blocks
                    if i in new_chain:
                        new_block_at_height = new_chain[i]
                        log.info('%s now in main chain' %
                                 encode_hex(new_block_at_height.header.hash))
                        # Add to block index
                        self.db.put(key, new_block_at_height.header.hash)
                        # Add to txindex
                        for j, tx in enumerate(
                                new_block_at_height.transactions):
                            self.db.put(
                                b'txindex:' + tx.hash,
                                rlp.encode([new_block_at_height.number, j]))
                        # Add to changed list
                        if i < b.number:
                            acct_list = self.db.get(b'changed:' +
                                                    new_block_at_height.hash)
                            for j in range(0, len(acct_list), 20):
                                changed_accts[acct_list[j:j + 20]] = True
                    if i not in new_chain and not orig_at_height:
                        break
                # Add changed list from new head to changed list
                for c in changed.keys():
                    changed_accts[c] = True
                # Update the on-disk state cache
                for addr in changed_accts.keys():
                    data = temp_state.trie.get(addr)
                    if data:
                        self.state.db.put(b'address:' + addr, data)
                    else:
                        try:
                            self.state.db.delete(b'address:' + addr)
                        except KeyError:
                            pass
                self.head_hash = block.header.hash
                self.state = temp_state
                self.state.executing_on_head = True
        # Block has no parent yet
        else:
            if block.header.prevhash not in self.parent_queue:
                self.parent_queue[block.header.prevhash] = []
            self.parent_queue[block.header.prevhash].append(block)
            log.info(
                'Got block %d (%s) with prevhash %s, parent not found. Delaying for now'
                % (block.number, encode_hex(
                    block.hash[:4]), encode_hex(block.prevhash[:4])))
            return False
        self.add_child(block)

        self.db.put(b'head_hash', self.head_hash)

        self.db.put(block.hash, rlp.encode(block))
        self.db.put(
            b'changed:' + block.hash, b''.join([
                k.encode() if not is_string(k) else k
                for k in list(changed.keys())
            ]))
        print('Saved %d address change logs' % len(changed.keys()))
        self.db.put(b'deletes:' + block.hash, b''.join(deletes))
        log.debug('Saved %d trie node deletes for block %d (%s)' %
                  (len(deletes), block.number, utils.encode_hex(block.hash)))
        # Delete old junk data
        old_block_hash = self.get_blockhash_by_number(block.number -
                                                      self.max_history)
        if old_block_hash:
            try:
                deletes = self.db.get(b'deletes:' + old_block_hash)
                log.debug('Deleting up to %d trie nodes' %
                          (len(deletes) // 32))
                rdb = RefcountDB(self.db)
                for i in range(0, len(deletes), 32):
                    rdb.delete(deletes[i:i + 32])
                self.db.delete(b'deletes:' + old_block_hash)
                self.db.delete(b'changed:' + old_block_hash)
            except KeyError as e:
                print(e)
                pass
        self.db.commit()
        assert (b'deletes:' + block.hash) in self.db
        log.info('Added block %d (%s) with %d txs and %d gas' %
                 (block.header.number, encode_hex(block.header.hash)[:8],
                  len(block.transactions), block.header.gas_used))
        # Call optional callback
        if self.new_head_cb and block.header.number != 0:
            self.new_head_cb(block)
        # Are there blocks that we received that were waiting for this block?
        # If so, process them.
        if block.header.hash in self.parent_queue:
            for _blk in self.parent_queue[block.header.hash]:
                self.add_block(_blk)
            del self.parent_queue[block.header.hash]
        return True
Beispiel #36
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code

    s = time.time()
    op = None
    steps = 0
    _prevop = None  # for trace only

    while 1:
      # print('op: ', op, time.time() - s)
      # s = time.time()
      # stack size limit error
      if compustate.pc not in processed_code:
          return vm_exception('INVALID START POINT')

      _data = processed_code[compustate.pc]
      gas, min_stack, max_stack, compustate.pc = _data[:4]
      ops = _data[4:]

      # out of gas error
      if gas > compustate.gas:
        return vm_exception('OUT OF GAS')

      # insufficient stack error
      if not (min_stack <= len(compustate.stack) <= max_stack):
        return vm_exception('INCOMPATIBLE STACK LENGTH', min_stack=min_stack,
                            have=len(compustate.stack), max_stack=max_stack)

      # Apply operation
      compustate.gas -= gas

      for op in ops:

        if trace_vm:
            """
            This diverges from normal logging, as we use the logging namespace
            only to decide which features get logged in 'eth.vm.op'
            i.e. tracing can not be activated by activating a sub
            like 'eth.vm.op.stack'
            """
            trace_data = {}
            trace_data['stack'] = list(map(to_string, list(compustate.stack)))
            if _prevop in (op_MLOAD, op_MSTORE, op_MSTORE8, op_SHA3, op_CALL,
                           op_CALLCODE, op_CREATE, op_CALLDATACOPY, op_CODECOPY,
                           op_EXTCODECOPY):
                if len(compustate.memory) < 1024:
                    trace_data['memory'] = \
                        b''.join([encode_hex(ascii_chr(x)) for x
                                  in compustate.memory])
                else:
                    trace_data['sha3memory'] = \
                        encode_hex(utils.sha3(''.join([ascii_chr(x) for
                                              x in compustate.memory])))
            if _prevop in (op_SSTORE, op_SLOAD) or steps == 0:
                trace_data['storage'] = ext.log_storage(msg.to)
            # trace_data['gas'] = to_string(compustate.gas + fee)
            trace_data['inst'] = op
            trace_data['pc'] = to_string(compustate.pc - 1)
            if steps == 0:
                trace_data['depth'] = msg.depth
                trace_data['address'] = msg.to
            trace_data['op'] = op
            trace_data['steps'] = steps
            # if op[:4] == 'PUSH':
            #     trace_data['pushvalue'] = pushval
            log_vm_op.trace('vm', **trace_data)
            steps += 1
            _prevop = op

        # Invalid operation
        if op == INVALID:
            return vm_exception('INVALID OP', opcode=op)

        # Valid operations
        if op < 0x10:
            if op == op_STOP:
                return peaceful_exit('STOP', compustate.gas, [])
            elif op == op_ADD:
                stk.append((stk.pop() + stk.pop()) & TT256M1)
            elif op == op_SUB:
                stk.append((stk.pop() - stk.pop()) & TT256M1)
            elif op == op_MUL:
                stk.append((stk.pop() * stk.pop()) & TT256M1)
            elif op == op_DIV:
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 // s1)
            elif op == op_MOD:
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 % s1)
            elif op == op_SDIV:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                              (-1 if s0 * s1 < 0 else 1)) & TT256M1)
            elif op == op_SMOD:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                              (-1 if s0 < 0 else 1)) & TT256M1)
            elif op == op_ADDMOD:
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 + s1) % s2 if s2 else 0)
            elif op == op_MULMOD:
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 * s1) % s2 if s2 else 0)
            elif op == op_EXP:
                base, exponent = stk.pop(), stk.pop()
                # fee for exponent is dependent on its bytes
                # calc n bytes to represent exponent
                nbytes = len(utils.encode_int(exponent))
                expfee = nbytes * opcodes.GEXPONENTBYTE
                if compustate.gas < expfee:
                    compustate.gas = 0
                    return vm_exception('OOG EXPONENT')
                compustate.gas -= expfee
                stk.append(pow(base, exponent, TT256))
            elif op == op_SIGNEXTEND:
                s0, s1 = stk.pop(), stk.pop()
                if s0 <= 31:
                    testbit = s0 * 8 + 7
                    if s1 & (1 << testbit):
                        stk.append(s1 | (TT256 - (1 << testbit)))
                    else:
                        stk.append(s1 & ((1 << testbit) - 1))
                else:
                    stk.append(s1)
        elif op < 0x20:
            if op == op_LT:
                stk.append(1 if stk.pop() < stk.pop() else 0)
            elif op == op_GT:
                stk.append(1 if stk.pop() > stk.pop() else 0)
            elif op == op_SLT:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 < s1 else 0)
            elif op == op_SGT:
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 > s1 else 0)
            elif op == op_EQ:
                stk.append(1 if stk.pop() == stk.pop() else 0)
            elif op == op_ISZERO:
                stk.append(0 if stk.pop() else 1)
            elif op == op_AND:
                stk.append(stk.pop() & stk.pop())
            elif op == op_OR:
                stk.append(stk.pop() | stk.pop())
            elif op == op_XOR:
                stk.append(stk.pop() ^ stk.pop())
            elif op == op_NOT:
                stk.append(TT256M1 - stk.pop())
            elif op == op_BYTE:
                s0, s1 = stk.pop(), stk.pop()
                if s0 >= 32:
                    stk.append(0)
                else:
                    stk.append((s1 // 256 ** (31 - s0)) % 256)
        elif op < 0x40:
            if op == op_SHA3:
                s0, s1 = stk.pop(), stk.pop()
                compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32)
                if compustate.gas < 0:
                    return vm_exception('OOG PAYING FOR SHA3')
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + s1]))
                stk.append(utils.big_endian_to_int(utils.sha3(data)))
            elif op == op_ADDRESS:
                stk.append(utils.coerce_to_int(msg.to))
            elif op == op_BALANCE:
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(ext.get_balance(addr))
            elif op == op_ORIGIN:
                stk.append(utils.coerce_to_int(ext.tx_origin))
            elif op == op_CALLER:
                stk.append(utils.coerce_to_int(msg.sender))
            elif op == op_CALLVALUE:
                stk.append(msg.value)
            elif op == op_CALLDATALOAD:
                stk.append(msg.data.extract32(stk.pop()))
            elif op == op_CALLDATASIZE:
                stk.append(msg.data.size)
            elif op == op_CALLDATACOPY:
                mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                msg.data.extract_copy(mem, mstart, dstart, size)
            elif op == op_CODESIZE:
                stk.append(len(code))
            elif op == op_CODECOPY:
                start, s1, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s1 + i < len(code):
                        mem[start + i] = utils.safe_ord(code[s1 + i])
                    else:
                        mem[start + i] = 0
            elif op == op_GASPRICE:
                stk.append(ext.tx_gasprice)
            elif op == op_EXTCODESIZE:
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(len(ext.get_code(addr) or b''))
            elif op == op_EXTCODECOPY:
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                start, s2, size = stk.pop(), stk.pop(), stk.pop()
                extcode = ext.get_code(addr) or b''
                assert utils.is_string(extcode)
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s2 + i < len(extcode):
                        mem[start + i] = utils.safe_ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
        elif op < 0x50:
            if op == op_BLOCKHASH:
                stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop())))
            elif op == op_COINBASE:
                stk.append(utils.big_endian_to_int(ext.block_coinbase))
            elif op == op_TIMESTAMP:
                stk.append(ext.block_timestamp)
            elif op == op_NUMBER:
                stk.append(ext.block_number)
            elif op == op_DIFFICULTY:
                stk.append(ext.block_difficulty)
            elif op == op_GASLIMIT:
                stk.append(ext.block_gas_limit)
        elif op < 0x60:
            if op == op_POP:
                stk.pop()
            elif op == op_MLOAD:
                s0 = stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = 0
                for c in mem[s0: s0 + 32]:
                    data = (data << 8) + c
                stk.append(data)
            elif op == op_MSTORE:
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                v = s1
                for i in range(31, -1, -1):
                    mem[s0 + i] = v % 256
                    v //= 256
            elif op == op_MSTORE8:
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 1):
                    return vm_exception('OOG EXTENDING MEMORY')
                mem[s0] = s1 % 256
            elif op == op_SLOAD:
                stk.append(ext.get_storage_data(msg.to, stk.pop()))
            elif op == op_SSTORE:
                s0, s1 = stk.pop(), stk.pop()
                if ext.get_storage_data(msg.to, s0):
                    gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                    refund = 0 if s1 else opcodes.GSTORAGEREFUND
                else:
                    gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                    refund = 0
                if compustate.gas < gascost:
                    return vm_exception('OUT OF GAS')
                compustate.gas -= gascost
                ext.add_refund(refund)  # adds neg gascost as a refund if below zero
                ext.set_storage_data(msg.to, s0, s1)
            elif op == op_JUMP:
                compustate.pc = stk.pop()
                opnew = processed_code[compustate.pc][4] if \
                    compustate.pc in processed_code else op_STOP
                if opnew != op_JUMPDEST:
                    return vm_exception('BAD JUMPDEST')
            elif op == op_JUMPI:
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    opnew = processed_code[compustate.pc][4] if \
                        compustate.pc in processed_code else op_STOP
                    if opnew != op_JUMPDEST:
                        return vm_exception('BAD JUMPDEST')
            elif op == op_PC:
                stk.append(compustate.pc - 1)
            elif op == op_MSIZE:
                stk.append(len(mem))
            elif op == op_GAS:
                stk.append(compustate.gas)  # AFTER subtracting cost 1
        elif op_PUSH1 <= (op & 255) <= op_PUSH32:
            # Hide push value in high-order bytes of op
            stk.append(op >> 8)
        elif op_DUP1 <= op <= op_DUP16:
            depth = op - op_DUP1 + 1
            stk.append(stk[-depth])
        elif op_SWAP1 <= op <= op_SWAP16:
            depth = op - op_SWAP1 + 1
            temp = stk[-depth - 1]
            stk[-depth - 1] = stk[-1]
            stk[-1] = temp

        elif op_LOG0 <= op <= op_LOG4:
            """
            0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
            a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                    MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
            b. Logs are kept track of during tx execution exactly the same way as suicides
               (except as an ordered list, not a set).
               Each log is in the form [address, [topic1, ... ], data] where:
               * address is what the ADDRESS opcode would output
               * data is mem[MEMSTART: MEMSTART + MEMSZ]
               * topics are as provided by the opcode
            c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
            """
            depth = op - op_LOG0
            mstart, msz = stk.pop(), stk.pop()
            topics = [stk.pop() for x in range(depth)]
            compustate.gas -= msz * opcodes.GLOGBYTE
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            data = b''.join(map(ascii_chr, mem[mstart: mstart + msz]))
            ext.log(msg.to, topics, data)
            log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data)))
            # print('LOG', msg.to, topics, list(map(ord, data)))

        elif op == op_CREATE:
            value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                cd = CallData(mem, mstart, msz)
                create_msg = Message(msg.to, b'', value, compustate.gas, cd, msg.depth + 1)
                o, gas, addr = ext.create(create_msg)
                if o:
                    stk.append(utils.coerce_to_int(addr))
                    compustate.gas = gas
                else:
                    stk.append(0)
                    compustate.gas = 0
            else:
                stk.append(0)
        elif op == op_CALL:
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            to = utils.encode_int(to)
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to, to, value, submsg_gas, cd,
                                   msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == op_CALLCODE:
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to, msg.to, value, submsg_gas, cd,
                                   msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == op_RETURN:
            s0, s1 = stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, s0, s1):
                return vm_exception('OOG EXTENDING MEMORY')
            return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1])
        elif op == op_SUICIDE:
            to = utils.encode_int(stk.pop())
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            xfer = ext.get_balance(msg.to)
            ext.set_balance(to, ext.get_balance(to) + xfer)
            ext.set_balance(msg.to, 0)
            ext.add_suicide(msg.to)
            # print('suiciding %s %s %d' % (msg.to, to, xfer))
            return 1, compustate.gas, []
 def decode_keyfile_json(raw_keyfile_json, password):
     if not is_string(password):
         password = to_string(password)
     return eth_keyfile.decode_keyfile_json(raw_keyfile_json, password)
Beispiel #38
0
 def encode_node(nd):
     if is_string(nd):
         return encode_hex(nd)
     else:
         return encode_hex(rlp.encode(nd))
Beispiel #39
0
def encode_single(typ, arg):  # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements,too-many-locals
    """ Encode `arg` as `typ`.

    `arg` will be encoded in a best effort manner, were necessary the function
    will try to correctly define the underlying binary representation (ie.
    decoding a hex-encoded address/hash).

    Args:
        typ (Tuple[(str, int, list)]): A 3-tuple defining the `arg` type.

            The first element defines the type name.
            The second element defines the type length in bits.
            The third element defines if it's an array type.

            Together the first and second defines the elementary type, the third
            element must be present but is ignored.

            Valid type names are:
                - uint
                - int
                - bool
                - ufixed
                - fixed
                - string
                - bytes
                - hash
                - address

        arg (object): The object to be encoded, it must be a python object
            compatible with the `typ`.

    Raises:
        ValueError: when an invalid `typ` is supplied.
        ValueOutOfBounds: when `arg` cannot be encoded as `typ` because of the
            binary contraints.

    Note:
        This function don't work with array types, for that use the `enc`
        function.
    """
    base, sub, _ = typ

    if base == 'uint':
        sub = int(sub)

        if not (0 < sub <= 256 and sub % 8 == 0):
            raise ValueError(
                'invalid unsigned integer bit length {}'.format(sub))

        try:
            i = decint(arg, signed=False)
        except EncodingError:
            # arg is larger than 2**256
            raise ValueOutOfBounds(repr(arg))

        if not 0 <= i < 2 ** sub:
            raise ValueOutOfBounds(repr(arg))

        value_encoded = int_to_big_endian(i)
        return zpad(value_encoded, 32)

    if base == 'int':
        sub = int(sub)
        bits = sub - 1

        if not (0 < sub <= 256 and sub % 8 == 0):
            raise ValueError('invalid integer bit length {}'.format(sub))

        try:
            i = decint(arg, signed=True)
        except EncodingError:
            # arg is larger than 2**255
            raise ValueOutOfBounds(repr(arg))

        if not -2 ** bits <= i < 2 ** bits:
            raise ValueOutOfBounds(repr(arg))

        value = i % 2 ** 256  # convert negative to "equivalent" positive
        value_encoded = int_to_big_endian(value)
        return zpad(value_encoded, 32)

    if base == 'bool':
        if arg is True:
            value_encoded = int_to_big_endian(1)
        elif arg is False:
            value_encoded = int_to_big_endian(0)
        else:
            raise ValueError('%r is not bool' % arg)

        return zpad(value_encoded, 32)

    if base == 'ufixed':
        sub = str(sub)  # pylint: disable=redefined-variable-type

        high_str, low_str = sub.split('x')
        high = int(high_str)
        low = int(low_str)

        if not (0 < high + low <= 256 and high % 8 == 0 and low % 8 == 0):
            raise ValueError('invalid unsigned fixed length {}'.format(sub))

        if not 0 <= arg < 2 ** high:
            raise ValueOutOfBounds(repr(arg))

        float_point = arg * 2 ** low
        fixed_point = int(float_point)
        return zpad(int_to_big_endian(fixed_point), 32)

    if base == 'fixed':
        sub = str(sub)  # pylint: disable=redefined-variable-type

        high_str, low_str = sub.split('x')
        high = int(high_str)
        low = int(low_str)
        bits = high - 1

        if not (0 < high + low <= 256 and high % 8 == 0 and low % 8 == 0):
            raise ValueError('invalid unsigned fixed length {}'.format(sub))

        if not -2 ** bits <= arg < 2 ** bits:
            raise ValueOutOfBounds(repr(arg))

        float_point = arg * 2 ** low
        fixed_point = int(float_point)
        value = fixed_point % 2 ** 256
        return zpad(int_to_big_endian(value), 32)

    # Decimals
    if base == 'decimal':
        val_to_encode = int(arg * 10**int(sub))
        return zpad(encode_int(val_to_encode % 2**256), 32)

    if base == 'string':
        if isinstance(arg, utils.unicode):
            arg = arg.encode('utf8')
        else:
            try:
                arg.decode('utf8')
            except UnicodeDecodeError:
                raise ValueError('string must be utf8 encoded')

        if len(sub):  # fixed length
            if not 0 <= len(arg) <= int(sub):
                raise ValueError('invalid string length {}'.format(sub))

            if not 0 <= int(sub) <= 32:
                raise ValueError('invalid string length {}'.format(sub))

            return rzpad(arg, 32)

        if not 0 <= len(arg) < TT256:
            raise Exception('Integer invalid or out of range: %r' % arg)

        length_encoded = zpad(int_to_big_endian(len(arg)), 32)
        value_encoded = rzpad(arg, utils.ceil32(len(arg)))

        return length_encoded + value_encoded

    if base == 'bytes':
        if not is_string(arg):
            if isinstance(arg, str):
                arg = bytes(arg, 'utf8')
            else:
                raise EncodingError('Expecting string: %r' % arg)

        arg = utils.to_string(arg)  # py2: force unicode into str

        if len(sub):  # fixed length
            if not 0 <= len(arg) <= int(sub):
                raise ValueError('string must be utf8 encoded')

            if not 0 <= int(sub) <= 32:
                raise ValueError('string must be utf8 encoded')

            return rzpad(arg, 32)

        if not 0 <= len(arg) < TT256:
            raise Exception('Integer invalid or out of range: %r' % arg)

        length_encoded = zpad(int_to_big_endian(len(arg)), 32)
        value_encoded = rzpad(arg, utils.ceil32(len(arg)))

        return length_encoded + value_encoded

    if base == 'hash':
        if not (int(sub) and int(sub) <= 32):
            raise EncodingError('too long: %r' % arg)

        if is_numeric(arg):
            return zpad(encode_int(arg), 32)

        if len(arg) == int(sub):
            return zpad(arg, 32)

        if len(arg) == int(sub) * 2:
            return zpad(decode_hex(arg), 32)

        raise EncodingError('Could not parse hash: %r' % arg)

    if base == 'address':
        assert sub == ''

        if is_numeric(arg):
            return zpad(encode_int(arg), 32)

        if len(arg) == 20:
            return zpad(arg, 32)

        if len(arg) == 40:
            return zpad(decode_hex(arg), 32)

        if len(arg) == 42 and arg[:2] == '0x':
            return zpad(decode_hex(arg[2:]), 32)

        raise EncodingError('Could not parse address: %r' % arg)
    raise EncodingError('Unhandled type: %r %r' % (base, sub))
Beispiel #40
0
    def fetch_hashchain(self):
        log_st.debug('fetching hashchain')
        blockhashes_chain = [self.blockhash]  # youngest to oldest
        # For testing purposes: skip the hash downoading stage
        # import ast
        # blockhashes_chain = ast.literal_eval(open('/home/vub/blockhashes.pyast').read())[:299000]

        blockhash = self.blockhash = blockhashes_chain[-1]
        assert blockhash not in self.chain

        # get block hashes until we found a known one
        max_blockhashes_per_request = self.initial_blockhashes_per_request
        while blockhash not in self.chain:
            # proto with highest_difficulty should be the proto we got the newblock from
            blockhashes_batch = []

            # try with protos
            protocols = self.protocols
            if not protocols:
                log_st.warn('no protocols available')
                return self.exit(success=False)

            for proto in protocols:
                log.debug('syncing with', proto=proto)
                if proto.is_stopped:
                    continue

                # request
                assert proto not in self.requests
                deferred = AsyncResult()
                self.requests[proto] = deferred
                proto.send_getblockhashes(blockhash,
                                          max_blockhashes_per_request)
                try:
                    blockhashes_batch = deferred.get(
                        block=True, timeout=self.blockhashes_request_timeout)
                except gevent.Timeout:
                    log_st.warn('syncing hashchain timed out')
                    continue
                finally:
                    # is also executed 'on the way out' when any other clause of the try statement
                    # is left via a break, continue or return statement.
                    del self.requests[proto]

                if not blockhashes_batch:
                    log_st.warn('empty getblockhashes result')
                    continue
                if not all(isinstance(bh, bytes) for bh in blockhashes_batch):
                    log_st.warn('got wrong data type',
                                expected='bytes',
                                received=type(blockhashes_batch[0]))
                    continue
                break

            if not blockhashes_batch:
                log_st.warn('syncing failed with all peers',
                            num_protos=len(protocols))
                return self.exit(success=False)

            for blockhash in blockhashes_batch:  # youngest to oldest
                assert utils.is_string(blockhash)
                if blockhash not in self.chain:
                    blockhashes_chain.append(blockhash)
                else:
                    log_st.debug(
                        'found known blockhash',
                        blockhash=utils.encode_hex(blockhash),
                        is_genesis=bool(blockhash == self.chain.genesis.hash))
                    break
            log_st.debug('downloaded ' + str(len(blockhashes_chain)) +
                         ' block hashes, ending with %s' %
                         utils.encode_hex(blockhashes_chain[-1]))
            max_blockhashes_per_request = self.max_blockhashes_per_request

        self.fetch_blocks(blockhashes_chain)
Beispiel #41
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    processed_code = preprocess_code(code)
    codelen = len(processed_code)

    op = None
    steps = 0
    _prevop = None  # for trace only

    while 1:
        # stack size limit error
        if compustate.pc >= codelen:
            return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])

        op, in_args, out_args, fee, opcode, pushval = \
            processed_code[compustate.pc]

        # out of gas error
        if fee > compustate.gas:
            return vm_exception('OUT OF GAS')

        # empty stack error
        if in_args > len(compustate.stack):
            return vm_exception('INSUFFICIENT STACK',
                                op=op, needed=to_string(in_args),
                                available=to_string(len(compustate.stack)))

        if len(compustate.stack) - in_args + out_args > 1024:
            return vm_exception('STACK SIZE LIMIT EXCEEDED',
                                op=op,
                                pre_height=to_string(len(compustate.stack)))

        # Apply operation
        compustate.gas -= fee
        compustate.pc += 1

        if trace_vm:
            """
            This diverges from normal logging, as we use the logging namespace
            only to decide which features get logged in 'eth.vm.op'
            i.e. tracing can not be activated by activating a sub
            like 'eth.vm.op.stack'
            """
            trace_data = {}
            trace_data['stack'] = list(map(to_string, list(compustate.stack)))
            if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL',
                           'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY',
                           'EXTCODECOPY'):
                if len(compustate.memory) < 1024:
                    trace_data['memory'] = \
                        b''.join([encode_hex(ascii_chr(x)) for x
                                  in compustate.memory])
                else:
                    trace_data['sha3memory'] = \
                        encode_hex(utils.sha3(''.join([ascii_chr(x) for
                                              x in compustate.memory])))
            if _prevop in ('SSTORE', 'SLOAD') or steps == 0:
                trace_data['storage'] = ext.log_storage(msg.to)
            trace_data['gas'] = to_string(compustate.gas + fee)
            trace_data['inst'] = opcode
            trace_data['pc'] = to_string(compustate.pc - 1)
            if steps == 0:
                trace_data['depth'] = msg.depth
                trace_data['address'] = msg.to
            trace_data['op'] = op
            trace_data['steps'] = steps
            if op[:4] == 'PUSH':
                trace_data['pushvalue'] = pushval
            log_vm_op.trace('vm', **trace_data)
            steps += 1
            _prevop = op

        # Invalid operation
        if op == 'INVALID':
            return vm_exception('INVALID OP', opcode=opcode)

        # Valid operations
        if opcode < 0x10:
            if op == 'STOP':
                return peaceful_exit('STOP', compustate.gas, [])
            elif op == 'ADD':
                stk.append((stk.pop() + stk.pop()) & TT256M1)
            elif op == 'SUB':
                stk.append((stk.pop() - stk.pop()) & TT256M1)
            elif op == 'MUL':
                stk.append((stk.pop() * stk.pop()) & TT256M1)
            elif op == 'DIV':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 // s1)
            elif op == 'MOD':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 % s1)
            elif op == 'SDIV':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                              (-1 if s0 * s1 < 0 else 1)) & TT256M1)
            elif op == 'SMOD':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                              (-1 if s0 < 0 else 1)) & TT256M1)
            elif op == 'ADDMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 + s1) % s2 if s2 else 0)
            elif op == 'MULMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 * s1) % s2 if s2 else 0)
            elif op == 'EXP':
                base, exponent = stk.pop(), stk.pop()
                # fee for exponent is dependent on its bytes
                # calc n bytes to represent exponent
                nbytes = len(utils.encode_int(exponent))
                expfee = nbytes * opcodes.GEXPONENTBYTE
                if compustate.gas < expfee:
                    compustate.gas = 0
                    return vm_exception('OOG EXPONENT')
                compustate.gas -= expfee
                stk.append(pow(base, exponent, TT256))
            elif op == 'SIGNEXTEND':
                s0, s1 = stk.pop(), stk.pop()
                if s0 <= 31:
                    testbit = s0 * 8 + 7
                    if s1 & (1 << testbit):
                        stk.append(s1 | (TT256 - (1 << testbit)))
                    else:
                        stk.append(s1 & ((1 << testbit) - 1))
                else:
                    stk.append(s1)
        elif opcode < 0x20:
            if op == 'LT':
                stk.append(1 if stk.pop() < stk.pop() else 0)
            elif op == 'GT':
                stk.append(1 if stk.pop() > stk.pop() else 0)
            elif op == 'SLT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 < s1 else 0)
            elif op == 'SGT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 > s1 else 0)
            elif op == 'EQ':
                stk.append(1 if stk.pop() == stk.pop() else 0)
            elif op == 'ISZERO':
                stk.append(0 if stk.pop() else 1)
            elif op == 'AND':
                stk.append(stk.pop() & stk.pop())
            elif op == 'OR':
                stk.append(stk.pop() | stk.pop())
            elif op == 'XOR':
                stk.append(stk.pop() ^ stk.pop())
            elif op == 'NOT':
                stk.append(TT256M1 - stk.pop())
            elif op == 'BYTE':
                s0, s1 = stk.pop(), stk.pop()
                if s0 >= 32:
                    stk.append(0)
                else:
                    stk.append((s1 // 256 ** (31 - s0)) % 256)
        elif opcode < 0x40:
            if op == 'SHA3':
                s0, s1 = stk.pop(), stk.pop()
                compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32)
                if compustate.gas < 0:
                    return vm_exception('OOG PAYING FOR SHA3')
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + s1]))
                stk.append(utils.big_endian_to_int(utils.sha3(data)))
            elif op == 'ADDRESS':
                stk.append(utils.coerce_to_int(msg.to))
            elif op == 'BALANCE':
                # EIP150: Increase the gas cost of BALANCE to 400
                if ext.post_anti_dos_hardfork:
                    if not eat_gas(compustate, opcodes.BALANCE_SUPPLEMENTAL_GAS):
                        return vm_exception("OUT OF GAS")
                addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160)
                stk.append(ext.get_balance(addr))
            elif op == 'ORIGIN':
                stk.append(utils.coerce_to_int(ext.tx_origin))
            elif op == 'CALLER':
                stk.append(utils.coerce_to_int(msg.sender))
            elif op == 'CALLVALUE':
                stk.append(msg.value)
            elif op == 'CALLDATALOAD':
                stk.append(msg.data.extract32(stk.pop()))
            elif op == 'CALLDATASIZE':
                stk.append(msg.data.size)
            elif op == 'CALLDATACOPY':
                mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                msg.data.extract_copy(mem, mstart, dstart, size)
            elif op == 'CODESIZE':
                stk.append(len(processed_code))
            elif op == 'CODECOPY':
                start, s1, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s1 + i < len(processed_code):
                        mem[start + i] = processed_code[s1 + i][4]
                    else:
                        mem[start + i] = 0
            elif op == 'GASPRICE':
                stk.append(ext.tx_gasprice)
            elif op == 'EXTCODESIZE':
                # EIP150: Increase the gas cost of EXTCODESIZE to 700
                if ext.post_anti_dos_hardfork:
                    if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                        return vm_exception("OUT OF GAS")
                addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160)
                stk.append(len(ext.get_code(addr) or b''))
            elif op == 'EXTCODECOPY':
                # EIP150: Increase the base gas cost of EXTCODECOPY to 700
                if ext.post_anti_dos_hardfork:
                    if not eat_gas(compustate, opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                        return vm_exception("OUT OF GAS")
                addr = utils.coerce_addr_to_hex(stk.pop() % 2 ** 160)
                start, s2, size = stk.pop(), stk.pop(), stk.pop()
                extcode = ext.get_code(addr) or b''
                assert utils.is_string(extcode)
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s2 + i < len(extcode):
                        mem[start + i] = utils.safe_ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
        elif opcode < 0x50:
            if op == 'BLOCKHASH':
                stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop())))
            elif op == 'COINBASE':
                stk.append(utils.big_endian_to_int(ext.block_coinbase))
            elif op == 'TIMESTAMP':
                stk.append(ext.block_timestamp)
            elif op == 'NUMBER':
                stk.append(ext.block_number)
            elif op == 'DIFFICULTY':
                stk.append(ext.block_difficulty)
            elif op == 'GASLIMIT':
                stk.append(ext.block_gas_limit)
        elif opcode < 0x60:
            if op == 'POP':
                stk.pop()
            elif op == 'MLOAD':
                s0 = stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + 32]))
                stk.append(utils.big_endian_to_int(data))
            elif op == 'MSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                v = s1
                for i in range(31, -1, -1):
                    mem[s0 + i] = v % 256
                    v //= 256
            elif op == 'MSTORE8':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 1):
                    return vm_exception('OOG EXTENDING MEMORY')
                mem[s0] = s1 % 256
            elif op == 'SLOAD':
                # EIP150: Increase the gas cost of SLOAD to 200
                if ext.post_anti_dos_hardfork:
                    if not eat_gas(compustate, opcodes.SLOAD_SUPPLEMENTAL_GAS):
                        return vm_exception("OUT OF GAS")
                stk.append(ext.get_storage_data(msg.to, stk.pop()))
            elif op == 'SSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if ext.get_storage_data(msg.to, s0):
                    gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                    refund = 0 if s1 else opcodes.GSTORAGEREFUND
                else:
                    gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                    refund = 0
                if compustate.gas < gascost:
                    return vm_exception('OUT OF GAS')
                compustate.gas -= gascost
                ext.add_refund(refund)  # adds neg gascost as a refund if below zero
                ext.set_storage_data(msg.to, s0, s1)
            elif op == 'JUMP':
                compustate.pc = stk.pop()
                opnew = processed_code[compustate.pc][0] if \
                    compustate.pc < len(processed_code) else 'STOP'
                if opnew != 'JUMPDEST':
                    return vm_exception('BAD JUMPDEST')
            elif op == 'JUMPI':
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    opnew = processed_code[compustate.pc][0] if \
                        compustate.pc < len(processed_code) else 'STOP'
                    if opnew != 'JUMPDEST':
                        return vm_exception('BAD JUMPDEST')
            elif op == 'PC':
                stk.append(compustate.pc - 1)
            elif op == 'MSIZE':
                stk.append(len(mem))
            elif op == 'GAS':
                stk.append(compustate.gas)  # AFTER subtracting cost 1
        elif op[:4] == 'PUSH':
            pushnum = int(op[4:])
            compustate.pc += pushnum
            stk.append(pushval)
        elif op[:3] == 'DUP':
            depth = int(op[3:])
            stk.append(stk[-depth])
        elif op[:4] == 'SWAP':
            depth = int(op[4:])
            temp = stk[-depth - 1]
            stk[-depth - 1] = stk[-1]
            stk[-1] = temp

        elif op[:3] == 'LOG':
            """
            0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
            a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                    MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
            b. Logs are kept track of during tx execution exactly the same way as suicides
               (except as an ordered list, not a set).
               Each log is in the form [address, [topic1, ... ], data] where:
               * address is what the ADDRESS opcode would output
               * data is mem[MEMSTART: MEMSTART + MEMSZ]
               * topics are as provided by the opcode
            c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
            """
            depth = int(op[3:])
            mstart, msz = stk.pop(), stk.pop()
            topics = [stk.pop() for x in range(depth)]
            compustate.gas -= msz * opcodes.GLOGBYTE
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            data = b''.join(map(ascii_chr, mem[mstart: mstart + msz]))
            ext.log(msg.to, topics, data)
            log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data)))
            # print('LOG', msg.to, topics, list(map(ord, data)))

        elif op == 'CREATE':
            value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                cd = CallData(mem, mstart, msz)
                ingas = compustate.gas
                # EIP150(1b) CREATE only provides all but one 64th of the
                # parent gas to the child call
                if ext.post_anti_dos_hardfork:
                    ingas = max_call_gas(ingas)

                create_msg = Message(msg.to, b'', value, ingas, cd, msg.depth + 1)
                o, gas, addr = ext.create(create_msg)
                if o:
                    stk.append(utils.coerce_to_int(addr))
                    compustate.gas -= (ingas - gas)
                else:
                    stk.append(0)
                    compustate.gas -= ingas
            else:
                stk.append(0)
        elif op == 'CALL':
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            to = utils.encode_int(to)
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                    (value > 0) * opcodes.GCALLVALUETRANSFER + \
                    ext.post_anti_dos_hardfork * opcodes.CALL_SUPPLEMENTAL_GAS
                    # ^ EIP150 Increase the gas cost of CALL to 700

            if ext.post_anti_dos_hardfork:
                # EIP150(1b) if a call asks for more gas than all but one 64th of
                # the maximum allowed amount, call with all but one 64th of the
                # maximum allowed amount of gas
                if compustate.gas < extra_gas:
                    return vm_exception('OUT OF GAS', needed=extra_gas)
                gas = min(gas, max_call_gas(compustate.gas - extra_gas))
            else:
                if compustate.gas < gas + extra_gas:
                    return vm_exception('OUT OF GAS', needed=gas + extra_gas)

            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to, to, value, submsg_gas, cd,
                                   msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'CALLCODE' or op == 'DELEGATECALL':
            if op == 'CALLCODE':
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            else:
                gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                value = 0
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER + \
                    ext.post_anti_dos_hardfork * opcodes.CALL_SUPPLEMENTAL_GAS
                    # ^ EIP150 Increase the gas cost of CALLCODE, DELEGATECALL to 700

            if ext.post_anti_dos_hardfork:
                # EIP150(1b) if a call asks for more gas than all but one 64th of
                # the maximum allowed amount, call with all but one 64th of the
                # maximum allowed amount of gas
                if compustate.gas < extra_gas:
                    return vm_exception('OUT OF GAS', needed=extra_gas)
                gas = min(gas, max_call_gas(compustate.gas - extra_gas))
            else:
                if compustate.gas < gas + extra_gas:
                    return vm_exception('OUT OF GAS', needed=gas + extra_gas)

            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                cd = CallData(mem, meminstart, meminsz)
                if ext.post_homestead_hardfork and op == 'DELEGATECALL':
                    call_msg = Message(msg.sender, msg.to, msg.value, submsg_gas, cd,
                                       msg.depth + 1, code_address=to, transfers_value=False)
                elif op == 'DELEGATECALL':
                    return vm_exception('OPCODE INACTIVE')
                else:
                    call_msg = Message(msg.to, msg.to, value, submsg_gas, cd,
                                       msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'RETURN':
            s0, s1 = stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, s0, s1):
                return vm_exception('OOG EXTENDING MEMORY')
            return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1])
        elif op == 'SUICIDE':
            to = utils.encode_int(stk.pop())
            to = ((b'\x00' * (32 - len(to))) + to)[12:]

            if ext.post_anti_dos_hardfork:
                # EIP150 Increase the gas cost of SUICIDE to 5000
                extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \
                        (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT
                # ^ EIP150(1c) If SUICIDE hits a newly created account, it
                # triggers an additional gas cost of 25000 (similar to CALLs)
                if not eat_gas(compustate, extra_gas):
                    return vm_exception("OUT OF GAS")

            xfer = ext.get_balance(msg.to)
            ext.set_balance(to, ext.get_balance(to) + xfer)
            ext.set_balance(msg.to, 0)
            ext.add_suicide(msg.to)
            # print('suiciding %s %s %d' % (msg.to, to, xfer))
            return 1, compustate.gas, []
Beispiel #42
0
 def encode_node(nd):
     if is_string(nd):
         return encode_hex(nd)
     else:
         return encode_hex(rlp_encode(nd))
Beispiel #43
0
 def has_block(self, blockhash):
     assert is_string(blockhash)
     assert len(blockhash) == 32
     return blockhash in self.blockchain
Beispiel #44
0
 def get(self, blockhash):
     assert is_string(blockhash)
     assert len(blockhash) == 32
     return blocks.get_block(self.env, blockhash)
Beispiel #45
0
 def has_block(self, blockhash):
     assert is_string(blockhash)
     assert len(blockhash) == 32
     return blockhash in self.blockchain
Beispiel #46
0
    def __init__(self, contract_interface):
        if is_string(contract_interface):
            contract_interface = json_decode(contract_interface)

        self.constructor_data = None
        self.function_data = {}
        self.event_data = {}

        for description in contract_interface:
            encode_types = [
                element['type']
                for element in description['inputs']
            ]

            signature = [
                (element['type'], element['name'])
                for element in description['inputs']
            ]

            # type can be omitted, defaulting to function
            if description.get('type', 'function') == 'function':
                normalized_name = _normalize_name(description['name'])

                decode_types = [
                    element['type']
                    for element in description['outputs']
                ]

                self.function_data[normalized_name] = {
                    'prefix': method_id(normalized_name, encode_types),
                    'encode_types': encode_types,
                    'decode_types': decode_types,
                    'is_constant': description.get('constant', False),
                    'signature': signature,
                }

            elif description['type'] == 'event':
                normalized_name = _normalize_name(description['name'])

                indexed = [
                    element['indexed']
                    for element in description['inputs']
                ]
                names = [
                    element['name']
                    for element in description['inputs']
                ]
                self.event_data[event_id(normalized_name, encode_types)] = {
                    'types': encode_types,
                    'name': normalized_name,
                    'names': names,
                    'indexed': indexed,
                    'anonymous': description.get('anonymous', False),
                }

            elif description['type'] == 'constructor':
                if self.constructor_data is not None:
                    raise ValueError('Only one constructor is supported.')

                self.constructor_data = {
                    'encode_types': encode_types,
                    'signature': signature,
                }

            else:
                raise ValueError('Unknown type {}'.format(description['type']))
Beispiel #47
0
def snapshot_form(val):
    if is_numeric(val):
        return str(val)
    elif is_string(val):
        return '0x' + encode_hex(val)
Beispiel #48
0
 def get(self, blockhash):
     assert is_string(blockhash)
     assert len(blockhash) == 32
     return blocks.get_block(self.blockchain, blockhash)
Beispiel #49
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code
        # print(processed_code.keys(), code)

    codelen = len(code)

    s = time.time()
    steps = 0
    _prevop = None  # for trace only

    while compustate.pc in processed_code:
        ops, minstack, maxstack, totgas, nextpos = processed_code[
            compustate.pc]

        if len(compustate.stack) < minstack:
            return vm_exception('INSUFFICIENT STACK')
        if len(compustate.stack) > maxstack:
            return vm_exception('STACK SIZE LIMIT EXCEEDED')
        if totgas > compustate.gas:
            return vm_exception('OUT OF GAS %d %d' % (totgas, compustate.gas))
        jumped = False

        compustate.gas -= totgas
        compustate.pc = nextpos

        # Invalid operation; can only come at the end of a chunk
        if ops[-1][0] == 'INVALID':
            return vm_exception('INVALID OP', opcode=ops[-1][1])

        for op, opcode, pushval in ops:

            if trace_vm:
                """
                This diverges from normal logging, as we use the logging namespace
                only to decide which features get logged in 'eth.vm.op'
                i.e. tracing can not be activated by activating a sub
                like 'eth.vm.op.stack'
                """
                trace_data = {}
                trace_data['stack'] = list(
                    map(to_string, list(compustate.stack)))
                if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL',
                               'CALLCODE', 'CREATE', 'CALLDATACOPY',
                               'CODECOPY', 'EXTCODECOPY'):
                    if len(compustate.memory) < 1024:
                        trace_data['memory'] = \
                            ''.join([encode_hex(ascii_chr(x)) for x
                                     in compustate.memory])
                    else:
                        trace_data['sha3memory'] = \
                            encode_hex(utils.sha3(b''.join([ascii_chr(x) for
                                                            x in compustate.memory])))
                if _prevop in ('SSTORE', ) or steps == 0:
                    trace_data['storage'] = ext.log_storage(msg.to)
                trace_data['gas'] = to_string(compustate.gas + totgas)
                trace_data['inst'] = opcode
                trace_data['pc'] = to_string(compustate.pc - 1)
                if steps == 0:
                    trace_data['depth'] = msg.depth
                    trace_data['address'] = msg.to
                trace_data['steps'] = steps
                trace_data['depth'] = msg.depth
                if op[:4] == 'PUSH':
                    trace_data['pushvalue'] = pushval
                log_vm_op.trace('vm', op=op, **trace_data)
                steps += 1
                _prevop = op

            # Valid operations
            # Pushes first because they are very frequent
            if 0x60 <= opcode <= 0x7f:
                # compustate.pc += opcode - 0x5f # Move 1 byte forward for
                # 0x60, up to 32 bytes for 0x7f
                stk.append(pushval)
            elif opcode < 0x10:
                if op == 'STOP':
                    return peaceful_exit('STOP', compustate.gas, [])
                elif op == 'ADD':
                    stk.append((stk.pop() + stk.pop()) & TT256M1)
                elif op == 'SUB':
                    stk.append((stk.pop() - stk.pop()) & TT256M1)
                elif op == 'MUL':
                    stk.append((stk.pop() * stk.pop()) & TT256M1)
                elif op == 'DIV':
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 // s1)
                elif op == 'MOD':
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 % s1)
                elif op == 'SDIV':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                                  (-1 if s0 * s1 < 0 else 1))
                               & TT256M1)
                elif op == 'SMOD':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                                  (-1 if s0 < 0 else 1))
                               & TT256M1)
                elif op == 'ADDMOD':
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 + s1) % s2 if s2 else 0)
                elif op == 'MULMOD':
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 * s1) % s2 if s2 else 0)
                elif op == 'EXP':
                    base, exponent = stk.pop(), stk.pop()
                    # fee for exponent is dependent on its bytes
                    # calc n bytes to represent exponent
                    nbytes = len(utils.encode_int(exponent))
                    expfee = nbytes * opcodes.GEXPONENTBYTE
                    if ext.post_clearing_hardfork():
                        expfee += opcodes.EXP_SUPPLEMENTAL_GAS * nbytes
                    if compustate.gas < expfee:
                        compustate.gas = 0
                        return vm_exception('OOG EXPONENT')
                    compustate.gas -= expfee
                    stk.append(pow(base, exponent, TT256))
                elif op == 'SIGNEXTEND':
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 <= 31:
                        testbit = s0 * 8 + 7
                        if s1 & (1 << testbit):
                            stk.append(s1 | (TT256 - (1 << testbit)))
                        else:
                            stk.append(s1 & ((1 << testbit) - 1))
                    else:
                        stk.append(s1)
            elif opcode < 0x20:
                if op == 'LT':
                    stk.append(1 if stk.pop() < stk.pop() else 0)
                elif op == 'GT':
                    stk.append(1 if stk.pop() > stk.pop() else 0)
                elif op == 'SLT':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 < s1 else 0)
                elif op == 'SGT':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 > s1 else 0)
                elif op == 'EQ':
                    stk.append(1 if stk.pop() == stk.pop() else 0)
                elif op == 'ISZERO':
                    stk.append(0 if stk.pop() else 1)
                elif op == 'AND':
                    stk.append(stk.pop() & stk.pop())
                elif op == 'OR':
                    stk.append(stk.pop() | stk.pop())
                elif op == 'XOR':
                    stk.append(stk.pop() ^ stk.pop())
                elif op == 'NOT':
                    stk.append(TT256M1 - stk.pop())
                elif op == 'BYTE':
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 >= 32:
                        stk.append(0)
                    else:
                        stk.append((s1 // 256**(31 - s0)) % 256)
            elif opcode < 0x40:
                if op == 'SHA3':
                    s0, s1 = stk.pop(), stk.pop()
                    compustate.gas -= opcodes.GSHA3WORD * \
                        (utils.ceil32(s1) // 32)
                    if compustate.gas < 0:
                        return vm_exception('OOG PAYING FOR SHA3')
                    if not mem_extend(mem, compustate, op, s0, s1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    data = bytearray_to_bytestr(mem[s0:s0 + s1])
                    stk.append(utils.big_endian_to_int(utils.sha3(data)))
                elif op == 'ADDRESS':
                    stk.append(utils.coerce_to_int(msg.to))
                elif op == 'BALANCE':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.BALANCE_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(ext.get_balance(addr))
                elif op == 'ORIGIN':
                    stk.append(utils.coerce_to_int(ext.tx_origin))
                elif op == 'CALLER':
                    stk.append(utils.coerce_to_int(msg.sender))
                elif op == 'CALLVALUE':
                    stk.append(msg.value)
                elif op == 'CALLDATALOAD':
                    stk.append(msg.data.extract32(stk.pop()))
                elif op == 'CALLDATASIZE':
                    stk.append(msg.data.size)
                elif op == 'CALLDATACOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    msg.data.extract_copy(mem, mstart, dstart, size)
                elif op == 'CODESIZE':
                    stk.append(len(code))
                elif op == 'CODECOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if dstart + i < len(code):
                            mem[mstart + i] = utils.safe_ord(code[dstart + i])
                        else:
                            mem[mstart + i] = 0
                elif op == 'RETURNDATACOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    if dstart + size > len(compustate.last_returned):
                        return vm_exception('RETURNDATACOPY out of range')
                    mem[mstart:mstart + size] = compustate.last_returned
                elif op == 'RETURNDATASIZE':
                    stk.append(len(compustate.last_returned))
                elif op == 'GASPRICE':
                    stk.append(ext.tx_gasprice)
                elif op == 'EXTCODESIZE':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(len(ext.get_code(addr) or b''))
                elif op == 'EXTCODECOPY':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    start, s2, size = stk.pop(), stk.pop(), stk.pop()
                    extcode = ext.get_code(addr) or b''
                    assert utils.is_string(extcode)
                    if not mem_extend(mem, compustate, op, start, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if s2 + i < len(extcode):
                            mem[start + i] = utils.safe_ord(extcode[s2 + i])
                        else:
                            mem[start + i] = 0
            elif opcode < 0x50:
                if op == 'BLOCKHASH':
                    if ext.post_metropolis_hardfork() and False:
                        bh_addr = ext.blockhash_store
                        stk.append(ext.get_storage_data(bh_addr, stk.pop()))
                    else:
                        stk.append(
                            utils.big_endian_to_int(ext.block_hash(stk.pop())))
                elif op == 'COINBASE':
                    stk.append(utils.big_endian_to_int(ext.block_coinbase))
                elif op == 'TIMESTAMP':
                    stk.append(ext.block_timestamp)
                elif op == 'NUMBER':
                    stk.append(ext.block_number)
                elif op == 'DIFFICULTY':
                    stk.append(ext.block_difficulty)
                elif op == 'GASLIMIT':
                    stk.append(ext.block_gas_limit)
            elif opcode < 0x60:
                if op == 'POP':
                    stk.pop()
                elif op == 'MLOAD':
                    s0 = stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    stk.append(utils.bytes_to_int(mem[s0:s0 + 32]))
                elif op == 'MSTORE':
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0:s0 + 32] = utils.encode_int32(s1)
                elif op == 'MSTORE8':
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0] = s1 % 256
                elif op == 'SLOAD':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.SLOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    stk.append(ext.get_storage_data(msg.to, stk.pop()))
                elif op == 'SSTORE':
                    s0, s1 = stk.pop(), stk.pop()
                    if msg.static:
                        return vm_exception(
                            'Cannot SSTORE inside a static context')
                    if ext.get_storage_data(msg.to, s0):
                        gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                        refund = 0 if s1 else opcodes.GSTORAGEREFUND
                    else:
                        gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                        refund = 0
                    if compustate.gas < gascost:
                        return vm_exception('OUT OF GAS')
                    compustate.gas -= gascost
                    # adds neg gascost as a refund if below zero
                    ext.add_refund(refund)
                    ext.set_storage_data(msg.to, s0, s1)
                elif op == 'JUMP':
                    compustate.pc = stk.pop()
                    opnew = code[
                        compustate.pc] if compustate.pc < codelen else 0
                    jumped = True
                    if opnew != JUMPDEST:
                        return vm_exception('BAD JUMPDEST')
                elif op == 'JUMPI':
                    s0, s1 = stk.pop(), stk.pop()
                    if s1:
                        compustate.pc = s0
                        opnew = code[
                            compustate.pc] if compustate.pc < codelen else 0
                        jumped = True
                        if opnew != JUMPDEST:
                            return vm_exception('BAD JUMPDEST')
                elif op == 'PC':
                    stk.append(compustate.pc - 1)
                elif op == 'MSIZE':
                    stk.append(len(mem))
                elif op == 'GAS':
                    stk.append(compustate.gas)  # AFTER subtracting cost 1
            elif op[:3] == 'DUP':
                # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for
                # 0x8f
                stk.append(stk[0x7f - opcode])
            elif op[:4] == 'SWAP':
                # 0x8e - opcode is a negative number, -2 for 0x90 ... -17 for
                # 0x9f
                temp = stk[0x8e - opcode]
                stk[0x8e - opcode] = stk[-1]
                stk[-1] = temp

            elif op[:3] == 'LOG':
                """
                0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
                a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                        MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
                b. Logs are kept track of during tx execution exactly the same way as suicides
                   (except as an ordered list, not a set).
                   Each log is in the form [address, [topic1, ... ], data] where:
                   * address is what the ADDRESS opcode would output
                   * data is mem[MEMSTART: MEMSTART + MEMSZ]
                   * topics are as provided by the opcode
                c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
                """
                depth = int(op[3:])
                mstart, msz = stk.pop(), stk.pop()
                topics = [stk.pop() for x in range(depth)]
                compustate.gas -= msz * opcodes.GLOGBYTE
                if msg.static:
                    return vm_exception('Cannot LOG inside a static context')
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = bytearray_to_bytestr(mem[mstart:mstart + msz])
                ext.log(msg.to, topics, data)
                log_log.trace('LOG',
                              to=msg.to,
                              topics=topics,
                              data=list(map(utils.safe_ord, data)))
                # print('LOG', msg.to, topics, list(map(ord, data)))

            elif op == 'CREATE':
                value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                if msg.static:
                    return vm_exception(
                        'Cannot CREATE inside a static context')
                if ext.get_balance(msg.to) >= value and msg.depth < MAX_DEPTH:
                    cd = CallData(mem, mstart, msz)
                    ingas = compustate.gas
                    if ext.post_anti_dos_hardfork():
                        ingas = all_but_1n(ingas,
                                           opcodes.CALL_CHILD_LIMIT_DENOM)
                    create_msg = Message(msg.to, b'', value, ingas, cd,
                                         msg.depth + 1)
                    o, gas, addr = ext.create(create_msg)
                    if o:
                        stk.append(utils.coerce_to_int(addr))
                    else:
                        stk.append(0)
                    compustate.gas = compustate.gas - ingas + gas
                else:
                    stk.append(0)
            elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'):
                # Pull arguments from the stack
                if op in ('CALL', 'CALLCODE'):
                    gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                        stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                else:
                    gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                        stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                    value = 0
                # Static context prohibition
                if msg.static and value > 0:
                    return vm_exception(
                        'Cannot make a non-zero-value call inside a static context'
                    )
                # Expand memory
                if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                        not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                    return vm_exception('OOG EXTENDING MEMORY')
                to = utils.int_to_addr(to)
                # Extra gas costs based on hard fork-dependent factors
                extra_gas = (not ext.account_exists(to)) * (op == 'CALL') * (value > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT + \
                    (value > 0) * opcodes.GCALLVALUETRANSFER + \
                    ext.post_anti_dos_hardfork() * opcodes.CALL_SUPPLEMENTAL_GAS
                # Compute child gas limit
                if ext.post_anti_dos_hardfork():
                    if compustate.gas < extra_gas:
                        return vm_exception('OUT OF GAS', needed=extra_gas)
                    gas = min(
                        gas,
                        all_but_1n(compustate.gas - extra_gas,
                                   opcodes.CALL_CHILD_LIMIT_DENOM))
                else:
                    if compustate.gas < gas + extra_gas:
                        return vm_exception('OUT OF GAS',
                                            needed=gas + extra_gas)
                submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
                # Verify that there is sufficient balance and depth
                if ext.get_balance(msg.to) < value or msg.depth >= MAX_DEPTH:
                    compustate.gas -= (gas + extra_gas - submsg_gas)
                    stk.append(0)
                else:
                    # Subtract gas from parent
                    compustate.gas -= (gas + extra_gas)
                    assert compustate.gas >= 0
                    cd = CallData(mem, meminstart, meminsz)
                    # Generate the message
                    if op == 'CALL':
                        call_msg = Message(msg.to,
                                           to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=msg.static)
                    elif ext.post_homestead_hardfork(
                    ) and op == 'DELEGATECALL':
                        call_msg = Message(msg.sender,
                                           msg.to,
                                           msg.value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           transfers_value=False,
                                           static=msg.static)
                    elif op == 'DELEGATECALL':
                        return vm_exception('OPCODE INACTIVE')
                    elif op == 'CALLCODE':
                        call_msg = Message(msg.to,
                                           msg.to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=msg.static)
                    elif op == 'STATICCALL':
                        call_msg = Message(msg.to,
                                           to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=True)
                    else:
                        raise Exception("Lolwut")
                    # Get result
                    result, gas, data = ext.msg(call_msg)
                    if result == 0:
                        stk.append(0)
                    else:
                        stk.append(1)
                    # Set output memory
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
                    compustate.gas += gas
                    compustate.last_returned = bytearray(data)
            elif op == 'RETURN':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1])
            elif op == 'REVERT':
                if not ext.post_metropolis_hardfork():
                    return vm_exception('Opcode not yet enabled')
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return revert(compustate.gas, mem[s0:s0 + s1])
            elif op == 'SUICIDE':
                if msg.static:
                    return vm_exception(
                        'Cannot SUICIDE inside a static context')
                to = utils.encode_int(stk.pop())
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                xfer = ext.get_balance(msg.to)
                if ext.post_anti_dos_hardfork():
                    extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \
                        (not ext.account_exists(
                            to)) * (xfer > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT
                    if not eat_gas(compustate, extra_gas):
                        return vm_exception("OUT OF GAS")
                ext.set_balance(to, ext.get_balance(to) + xfer)
                ext.set_balance(msg.to, 0)
                ext.add_suicide(msg.to)
                log_msg.debug('SUICIDING',
                              addr=utils.checksum_encode(msg.to),
                              to=utils.checksum_encode(to),
                              xferring=xfer)
                return 1, compustate.gas, []

            # assert utils.is_numeric(compustate.gas)
            # this is slow!
            # for a in stk:
            #     assert is_numeric(a), (op, stk)
            #     assert a >= 0 and a < 2**256, (a, op, stk)
        # if not jumped:
        #    assert compustate.pc == nextpos
        #    compustate.pc = nextpos
    if compustate.pc >= codelen:
        return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])
    return vm_exception('INVALID JUMP')
Beispiel #50
0
def encode_single(typ, arg):
    base, sub, _ = typ
    # Unsigned integers: uint<sz>
    if base == 'uint':
        sub = int(sub)
        i = decint(arg)

        if not 0 <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i), 32)
    # bool: int<sz>
    elif base == 'bool':
        assert isinstance(arg, bool)
        return zpad(encode_int(int(arg)), 32)
    # Signed integers: int<sz>
    elif base == 'int':
        sub = int(sub)
        i = decint(arg)
        if not -2**(sub - 1) <= i < 2**sub:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(i % 2**sub), 32)
    # Unsigned reals: ureal<high>x<low>
    elif base == 'ureal':
        high, low = [int(x) for x in sub.split('x')]
        if not 0 <= arg < 2**high:
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int(arg * 2**low), 32)
    # Signed reals: real<high>x<low>
    elif base == 'real':
        high, low = [int(x) for x in sub.split('x')]
        if not -2**(high - 1) <= arg < 2**(high - 1):
            raise ValueOutOfBounds(repr(arg))
        return zpad(encode_int((arg % 2**high) * 2**low), 32)
    # Strings
    elif base == 'string' or base == 'bytes':
        if not is_string(arg):
            raise EncodingError("Expecting string: %r" % arg)
        # Fixed length: string<sz>
        if len(sub):
            assert int(sub) <= 32
            assert len(arg) <= int(sub)
            return arg + b'\x00' * (32 - len(arg))
        # Variable length: string
        else:
            return zpad(encode_int(len(arg)), 32) + \
                arg + \
                b'\x00' * (utils.ceil32(len(arg)) - len(arg))
    # Hashes: hash<sz>
    elif base == 'hash':
        if not (int(sub) and int(sub) <= 32):
            raise EncodingError("too long: %r" % arg)
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == len(sub):
            return zpad(arg, 32)
        elif len(arg) == len(sub) * 2:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse hash: %r" % arg)
    # Addresses: address (== hash160)
    elif base == 'address':
        assert sub == ''
        if isnumeric(arg):
            return zpad(encode_int(arg), 32)
        elif len(arg) == 20:
            return zpad(arg, 32)
        elif len(arg) == 40:
            return zpad(decode_hex(arg), 32)
        else:
            raise EncodingError("Could not parse address: %r" % arg)
    raise EncodingError("Unhandled type: %r %r" % (base, sub))
Beispiel #51
0
    def __init__(self, contract_interface):
        if is_string(contract_interface):
            contract_interface = json_decode(contract_interface)

        self.fallback_data = None
        self.constructor_data = None
        self.function_data = {}
        self.event_data = {}

        for description in contract_interface:
            entry_type = description.get('type', 'function')
            encode_types = []
            signature = []

            # If it's a function/constructor/event
            if entry_type != 'fallback' and 'inputs' in description:
                encode_types = [
                    element['type']
                    for element in description.get('inputs', [])
                ]

                signature = [
                    (element['type'], element['name'])
                    for element in description.get('inputs', [])
                ]

            if entry_type == 'function':
                normalized_name = normalize_name(description['name'])
                prefix = method_id(normalized_name, encode_types)

                decode_types = [
                    element['type']
                    for element in description.get('outputs', [])
                ]

                self.function_data[normalized_name] = {
                    'prefix': prefix,
                    'encode_types': encode_types,
                    'decode_types': decode_types,
                    'is_constant': description.get('constant', False),
                    'signature': signature,
                    'payable': description.get('payable', False),
                }

            elif entry_type == 'event':
                normalized_name = normalize_name(description['name'])

                indexed = [
                    element['indexed']
                    for element in description['inputs']
                ]
                names = [
                    element['name']
                    for element in description['inputs']
                ]
                # event_id == topics[0]
                self.event_data[event_id(normalized_name, encode_types)] = {
                    'types': encode_types,
                    'name': normalized_name,
                    'names': names,
                    'indexed': indexed,
                    'anonymous': description.get('anonymous', False),
                }

            elif entry_type == 'constructor':
                if self.constructor_data is not None:
                    raise ValueError('Only one constructor is supported.')

                self.constructor_data = {
                    'encode_types': encode_types,
                    'signature': signature,
                }

            elif entry_type == 'fallback':
                if self.fallback_data is not None:
                    raise ValueError(
                        'Only one fallback function is supported.')
                self.fallback_data = {'payable': description['payable']}

            else:
                raise ValueError('Unknown type {}'.format(description['type']))
Beispiel #52
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    processed_code = preprocess_code(code)

    s = time.time()
    op = None
    steps = 0
    _prevop = None  # for trace only

    while True:
        # print('op: ', op, time.time() - s)
        # s = time.time()
        # stack size limit error
        if compustate.pc not in processed_code:
            return vm_exception('INVALID START POINT')

        _data = processed_code[compustate.pc]
        gas, min_stack, max_stack, compustate.pc = _data[:4]
        ops = _data[4:]

        # out of gas error
        if gas > compustate.gas:
            return vm_exception('OUT OF GAS')

        # insufficient stack error
        if not (min_stack <= len(compustate.stack) <= max_stack):
            return vm_exception('INCOMPATIBLE STACK LENGTH',
                                min_stack=min_stack,
                                have=len(compustate.stack),
                                max_stack=max_stack)

        # Apply operation
        compustate.gas -= gas

        for op in ops:

            if trace_vm:
                """
                This diverges from normal logging, as we use the logging namespace
                only to decide which features get logged in 'eth.vm.op'
                i.e. tracing can not be activated by activating a sub
                like 'eth.vm.op.stack'
                """
                trace_data = {}
                trace_data['stack'] = list(
                    map(to_string, list(compustate.stack)))
                if _prevop in (op_MLOAD, op_MSTORE, op_MSTORE8, op_SHA3,
                               op_CALL, op_CALLCODE, op_CREATE,
                               op_CALLDATACOPY, op_CODECOPY, op_EXTCODECOPY):
                    if len(compustate.memory) < 1024:
                        trace_data['memory'] = \
                            b''.join([encode_hex(ascii_chr(x)) for x
                                      in compustate.memory])
                    else:
                        trace_data['sha3memory'] = \
                            encode_hex(utils.sha3(''.join([ascii_chr(x) for
                                                           x in compustate.memory])))
                if _prevop in (op_SSTORE, op_SLOAD) or steps == 0:
                    trace_data['storage'] = ext.log_storage(msg.to)
                # trace_data['gas'] = to_string(compustate.gas + fee)
                trace_data['inst'] = op
                trace_data['pc'] = to_string(compustate.pc - 1)
                if steps == 0:
                    trace_data['depth'] = msg.depth
                    trace_data['address'] = msg.to
                trace_data['op'] = op
                trace_data['steps'] = steps
                # if op[:4] == 'PUSH':
                #     trace_data['pushvalue'] = pushval
                log_vm_op.trace('vm', **trace_data)
                steps += 1
                _prevop = op

            # Invalid operation
            if op == INVALID:
                return vm_exception('INVALID OP', opcode=op)

            # Valid operations
            if op < 0x10:
                if op == op_STOP:
                    return peaceful_exit('STOP', compustate.gas, [])
                elif op == op_ADD:
                    stk.append((stk.pop() + stk.pop()) & TT256M1)
                elif op == op_SUB:
                    stk.append((stk.pop() - stk.pop()) & TT256M1)
                elif op == op_MUL:
                    stk.append((stk.pop() * stk.pop()) & TT256M1)
                elif op == op_DIV:
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 // s1)
                elif op == op_MOD:
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 % s1)
                elif op == op_SDIV:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                                  (-1 if s0 * s1 < 0 else 1))
                               & TT256M1)
                elif op == op_SMOD:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                                  (-1 if s0 < 0 else 1))
                               & TT256M1)
                elif op == op_ADDMOD:
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 + s1) % s2 if s2 else 0)
                elif op == op_MULMOD:
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 * s1) % s2 if s2 else 0)
                elif op == op_EXP:
                    base, exponent = stk.pop(), stk.pop()
                    # fee for exponent is dependent on its bytes
                    # calc n bytes to represent exponent
                    nbytes = len(utils.encode_int(exponent))
                    expfee = nbytes * opcodes.GEXPONENTBYTE
                    if compustate.gas < expfee:
                        compustate.gas = 0
                        return vm_exception('OOG EXPONENT')
                    compustate.gas -= expfee
                    stk.append(pow(base, exponent, TT256))
                elif op == op_SIGNEXTEND:
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 <= 31:
                        testbit = s0 * 8 + 7
                        if s1 & (1 << testbit):
                            stk.append(s1 | (TT256 - (1 << testbit)))
                        else:
                            stk.append(s1 & ((1 << testbit) - 1))
                    else:
                        stk.append(s1)
            elif op < 0x20:
                if op == op_LT:
                    stk.append(1 if stk.pop() < stk.pop() else 0)
                elif op == op_GT:
                    stk.append(1 if stk.pop() > stk.pop() else 0)
                elif op == op_SLT:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 < s1 else 0)
                elif op == op_SGT:
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 > s1 else 0)
                elif op == op_EQ:
                    stk.append(1 if stk.pop() == stk.pop() else 0)
                elif op == op_ISZERO:
                    stk.append(0 if stk.pop() else 1)
                elif op == op_AND:
                    stk.append(stk.pop() & stk.pop())
                elif op == op_OR:
                    stk.append(stk.pop() | stk.pop())
                elif op == op_XOR:
                    stk.append(stk.pop() ^ stk.pop())
                elif op == op_NOT:
                    stk.append(TT256M1 - stk.pop())
                elif op == op_BYTE:
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 >= 32:
                        stk.append(0)
                    else:
                        stk.append((s1 // 256**(31 - s0)) % 256)
            elif op < 0x40:
                if op == op_SHA3:
                    s0, s1 = stk.pop(), stk.pop()
                    compustate.gas -= opcodes.GSHA3WORD * \
                        (utils.ceil32(s1) // 32)
                    if compustate.gas < 0:
                        return vm_exception('OOG PAYING FOR SHA3')
                    if not mem_extend(mem, compustate, op, s0, s1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    data = b''.join(map(ascii_chr, mem[s0:s0 + s1]))
                    stk.append(utils.big_endian_to_int(utils.sha3(data)))
                elif op == op_ADDRESS:
                    stk.append(utils.coerce_to_int(msg.to))
                elif op == op_BALANCE:
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(ext.get_balance(addr))
                elif op == op_ORIGIN:
                    stk.append(utils.coerce_to_int(ext.tx_origin))
                elif op == op_CALLER:
                    stk.append(utils.coerce_to_int(msg.sender))
                elif op == op_CALLVALUE:
                    stk.append(msg.value)
                elif op == op_CALLDATALOAD:
                    stk.append(msg.data.extract32(stk.pop()))
                elif op == op_CALLDATASIZE:
                    stk.append(msg.data.size)
                elif op == op_CALLDATACOPY:
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    msg.data.extract_copy(mem, mstart, dstart, size)
                elif op == op_CODESIZE:
                    stk.append(len(code))
                elif op == op_CODECOPY:
                    start, s1, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, start, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if s1 + i < len(code):
                            mem[start + i] = utils.safe_ord(code[s1 + i])
                        else:
                            mem[start + i] = 0
                elif op == op_GASPRICE:
                    stk.append(ext.tx_gasprice)
                elif op == op_EXTCODESIZE:
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(len(ext.get_code(addr) or b''))
                elif op == op_EXTCODECOPY:
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    start, s2, size = stk.pop(), stk.pop(), stk.pop()
                    extcode = ext.get_code(addr) or b''
                    assert utils.is_string(extcode)
                    if not mem_extend(mem, compustate, op, start, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if s2 + i < len(extcode):
                            mem[start + i] = utils.safe_ord(extcode[s2 + i])
                        else:
                            mem[start + i] = 0
            elif op < 0x50:
                if op == op_BLOCKHASH:
                    stk.append(
                        utils.big_endian_to_int(ext.block_hash(stk.pop())))
                elif op == op_COINBASE:
                    stk.append(utils.big_endian_to_int(ext.block_coinbase))
                elif op == op_TIMESTAMP:
                    stk.append(ext.block_timestamp)
                elif op == op_NUMBER:
                    stk.append(ext.block_number)
                elif op == op_DIFFICULTY:
                    stk.append(ext.block_difficulty)
                elif op == op_GASLIMIT:
                    stk.append(ext.block_gas_limit)
            elif op < 0x60:
                if op == op_POP:
                    stk.pop()
                elif op == op_MLOAD:
                    s0 = stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    data = 0
                    for c in mem[s0:s0 + 32]:
                        data = (data << 8) + c
                    stk.append(data)
                elif op == op_MSTORE:
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    v = s1
                    for i in range(31, -1, -1):
                        mem[s0 + i] = v % 256
                        v //= 256
                elif op == op_MSTORE8:
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0] = s1 % 256
                elif op == op_SLOAD:
                    stk.append(ext.get_storage_data(msg.to, stk.pop()))
                elif op == op_SSTORE:
                    s0, s1 = stk.pop(), stk.pop()
                    if ext.get_storage_data(msg.to, s0):
                        gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                        refund = 0 if s1 else opcodes.GSTORAGEREFUND
                    else:
                        gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                        refund = 0
                    if compustate.gas < gascost:
                        return vm_exception('OUT OF GAS')
                    compustate.gas -= gascost
                    # adds neg gascost as a refund if below zero
                    ext.add_refund(refund)
                    ext.set_storage_data(msg.to, s0, s1)
                elif op == op_JUMP:
                    compustate.pc = stk.pop()
                    opnew = processed_code[compustate.pc][4] if \
                        compustate.pc in processed_code else op_STOP
                    if opnew != op_JUMPDEST:
                        return vm_exception('BAD JUMPDEST')
                elif op == op_JUMPI:
                    s0, s1 = stk.pop(), stk.pop()
                    if s1:
                        compustate.pc = s0
                        opnew = processed_code[compustate.pc][4] if \
                            compustate.pc in processed_code else op_STOP
                        if opnew != op_JUMPDEST:
                            return vm_exception('BAD JUMPDEST')
                elif op == op_PC:
                    stk.append(compustate.pc - 1)
                elif op == op_MSIZE:
                    stk.append(len(mem))
                elif op == op_GAS:
                    stk.append(compustate.gas)  # AFTER subtracting cost 1
            elif op_PUSH1 <= (op & 255) <= op_PUSH32:
                # Hide push value in high-order bytes of op
                stk.append(op >> 8)
            elif op_DUP1 <= op <= op_DUP16:
                depth = op - op_DUP1 + 1
                stk.append(stk[-depth])
            elif op_SWAP1 <= op <= op_SWAP16:
                depth = op - op_SWAP1 + 1
                temp = stk[-depth - 1]
                stk[-depth - 1] = stk[-1]
                stk[-1] = temp

            elif op_LOG0 <= op <= op_LOG4:
                """
                0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
                a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                        MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
                b. Logs are kept track of during tx execution exactly the same way as suicides
                   (except as an ordered list, not a set).
                   Each log is in the form [address, [topic1, ... ], data] where:
                   * address is what the ADDRESS opcode would output
                   * data is mem[MEMSTART: MEMSTART + MEMSZ]
                   * topics are as provided by the opcode
                c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
                """
                depth = op - op_LOG0
                mstart, msz = stk.pop(), stk.pop()
                topics = [stk.pop() for x in range(depth)]
                compustate.gas -= msz * opcodes.GLOGBYTE
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[mstart:mstart + msz]))
                ext.log(msg.to, topics, data)
                log_log.trace('LOG',
                              to=msg.to,
                              topics=topics,
                              data=list(map(utils.safe_ord, data)))
                # print('LOG', msg.to, topics, list(map(ord, data)))

            elif op == op_CREATE:
                value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                    cd = CallData(mem, mstart, msz)
                    create_msg = Message(msg.to, b'', value, compustate.gas,
                                         cd, msg.depth + 1)
                    o, gas, addr = ext.create(create_msg)
                    if o:
                        stk.append(utils.coerce_to_int(addr))
                        compustate.gas = gas
                    else:
                        stk.append(0)
                        compustate.gas = 0
                else:
                    stk.append(0)
            elif op == op_CALL:
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                        not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                    return vm_exception('OOG EXTENDING MEMORY')
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                    (value > 0) * opcodes.GCALLVALUETRANSFER
                submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
                if compustate.gas < gas + extra_gas:
                    return vm_exception('OUT OF GAS', needed=gas + extra_gas)
                if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                    compustate.gas -= (gas + extra_gas)
                    cd = CallData(mem, meminstart, meminsz)
                    call_msg = Message(msg.to,
                                       to,
                                       value,
                                       submsg_gas,
                                       cd,
                                       msg.depth + 1,
                                       code_address=to)
                    result, gas, data = ext.msg(call_msg)
                    if result == 0:
                        stk.append(0)
                    else:
                        stk.append(1)
                        compustate.gas += gas
                        for i in range(min(len(data), memoutsz)):
                            mem[memoutstart + i] = data[i]
                else:
                    compustate.gas -= (gas + extra_gas - submsg_gas)
                    stk.append(0)
            elif op == op_CALLCODE:
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                        not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                    return vm_exception('OOG EXTENDING MEMORY')
                extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER
                submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
                if compustate.gas < gas + extra_gas:
                    return vm_exception('OUT OF GAS', needed=gas + extra_gas)
                if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                    compustate.gas -= (gas + extra_gas)
                    to = utils.encode_int(to)
                    to = ((b'\x00' * (32 - len(to))) + to)[12:]
                    cd = CallData(mem, meminstart, meminsz)
                    call_msg = Message(msg.to,
                                       msg.to,
                                       value,
                                       submsg_gas,
                                       cd,
                                       msg.depth + 1,
                                       code_address=to)
                    result, gas, data = ext.msg(call_msg)
                    if result == 0:
                        stk.append(0)
                    else:
                        stk.append(1)
                        compustate.gas += gas
                        for i in range(min(len(data), memoutsz)):
                            mem[memoutstart + i] = data[i]
                else:
                    compustate.gas -= (gas + extra_gas - submsg_gas)
                    stk.append(0)
            elif op == op_RETURN:
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1])
            elif op == op_SUICIDE:
                to = utils.encode_int(stk.pop())
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                xfer = ext.get_balance(msg.to)
                ext.set_balance(to, ext.get_balance(to) + xfer)
                ext.set_balance(msg.to, 0)
                ext.add_suicide(msg.to)
                # print('suiciding %s %s %d' % (msg.to, to, xfer))
                return 1, compustate.gas, []
Beispiel #53
0
 def _dec(x):
     if utils.is_string(x) and x.startswith(b'0x'):
         return utils.decode_hex(x[2:])
     return x
Beispiel #54
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code

    codelen = len(processed_code)

    s = time.time()
    op = None
    steps = 0
    _prevop = None  # for trace only

    while 1:
        # print('op: ', op, time.time() - s)
        # s = time.time()
        # stack size limit error
        if compustate.pc >= codelen:
            return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])

        op, in_args, out_args, fee, opcode, pushval = \
            processed_code[compustate.pc]

        # out of gas error
        if fee > compustate.gas:
            return vm_exception('OUT OF GAS')

        # empty stack error
        if in_args > len(compustate.stack):
            return vm_exception('INSUFFICIENT STACK',
                                op=op, needed=to_string(in_args),
                                available=to_string(len(compustate.stack)))

        if len(compustate.stack) - in_args + out_args > 1024:
            return vm_exception('STACK SIZE LIMIT EXCEEDED',
                                op=op,
                                pre_height=to_string(len(compustate.stack)))

        # Apply operation
        compustate.gas -= fee
        compustate.pc += 1

        if trace_vm:
            """
            This diverges from normal logging, as we use the logging namespace
            only to decide which features get logged in 'eth.vm.op'
            i.e. tracing can not be activated by activating a sub
            like 'eth.vm.op.stack'
            """
            trace_data = {}
            trace_data['stack'] = list(map(to_string, list(compustate.stack)))
            if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL',
                           'CALLCODE', 'CREATE', 'CALLDATACOPY', 'CODECOPY',
                           'EXTCODECOPY'):
                if len(compustate.memory) < 1024:
                    trace_data['memory'] = \
                        b''.join([encode_hex(ascii_chr(x)) for x
                                  in compustate.memory])
                else:
                    trace_data['sha3memory'] = \
                        encode_hex(utils.sha3(''.join([ascii_chr(x) for
                                              x in compustate.memory])))
            if _prevop in ('SSTORE', 'SLOAD') or steps == 0:
                trace_data['storage'] = ext.log_storage(msg.to)
            trace_data['gas'] = to_string(compustate.gas + fee)
            trace_data['inst'] = opcode
            trace_data['pc'] = to_string(compustate.pc - 1)
            if steps == 0:
                trace_data['depth'] = msg.depth
                trace_data['address'] = msg.to
            trace_data['op'] = op
            trace_data['steps'] = steps
            if op[:4] == 'PUSH':
                trace_data['pushvalue'] = pushval
            log_vm_op.trace('vm', **trace_data)
            steps += 1
            _prevop = op

        # Invalid operation
        if op == 'INVALID':
            return vm_exception('INVALID OP', opcode=opcode)

        # Valid operations
        if opcode < 0x10:
            if op == 'STOP':
                return peaceful_exit('STOP', compustate.gas, [])
            elif op == 'ADD':
                stk.append((stk.pop() + stk.pop()) & TT256M1)
            elif op == 'SUB':
                stk.append((stk.pop() - stk.pop()) & TT256M1)
            elif op == 'MUL':
                stk.append((stk.pop() * stk.pop()) & TT256M1)
            elif op == 'DIV':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 // s1)
            elif op == 'MOD':
                s0, s1 = stk.pop(), stk.pop()
                stk.append(0 if s1 == 0 else s0 % s1)
            elif op == 'SDIV':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                              (-1 if s0 * s1 < 0 else 1)) & TT256M1)
            elif op == 'SMOD':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                              (-1 if s0 < 0 else 1)) & TT256M1)
            elif op == 'ADDMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 + s1) % s2 if s2 else 0)
            elif op == 'MULMOD':
                s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                stk.append((s0 * s1) % s2 if s2 else 0)
            elif op == 'EXP':
                base, exponent = stk.pop(), stk.pop()
                # fee for exponent is dependent on its bytes
                # calc n bytes to represent exponent
                nbytes = len(utils.encode_int(exponent))
                expfee = nbytes * opcodes.GEXPONENTBYTE
                if compustate.gas < expfee:
                    compustate.gas = 0
                    return vm_exception('OOG EXPONENT')
                compustate.gas -= expfee
                stk.append(pow(base, exponent, TT256))
            elif op == 'SIGNEXTEND':
                s0, s1 = stk.pop(), stk.pop()
                if s0 <= 31:
                    testbit = s0 * 8 + 7
                    if s1 & (1 << testbit):
                        stk.append(s1 | (TT256 - (1 << testbit)))
                    else:
                        stk.append(s1 & ((1 << testbit) - 1))
                else:
                    stk.append(s1)
        elif opcode < 0x20:
            if op == 'LT':
                stk.append(1 if stk.pop() < stk.pop() else 0)
            elif op == 'GT':
                stk.append(1 if stk.pop() > stk.pop() else 0)
            elif op == 'SLT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 < s1 else 0)
            elif op == 'SGT':
                s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop())
                stk.append(1 if s0 > s1 else 0)
            elif op == 'EQ':
                stk.append(1 if stk.pop() == stk.pop() else 0)
            elif op == 'ISZERO':
                stk.append(0 if stk.pop() else 1)
            elif op == 'AND':
                stk.append(stk.pop() & stk.pop())
            elif op == 'OR':
                stk.append(stk.pop() | stk.pop())
            elif op == 'XOR':
                stk.append(stk.pop() ^ stk.pop())
            elif op == 'NOT':
                stk.append(TT256M1 - stk.pop())
            elif op == 'BYTE':
                s0, s1 = stk.pop(), stk.pop()
                if s0 >= 32:
                    stk.append(0)
                else:
                    stk.append((s1 // 256 ** (31 - s0)) % 256)
        elif opcode < 0x40:
            if op == 'SHA3':
                s0, s1 = stk.pop(), stk.pop()
                compustate.gas -= opcodes.GSHA3WORD * (utils.ceil32(s1) // 32)
                if compustate.gas < 0:
                    return vm_exception('OOG PAYING FOR SHA3')
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + s1]))
                stk.append(utils.big_endian_to_int(utils.sha3(data)))
            elif op == 'ADDRESS':
                stk.append(utils.coerce_to_int(msg.to))
            elif op == 'BALANCE':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(ext.get_balance(addr))
            elif op == 'ORIGIN':
                stk.append(utils.coerce_to_int(ext.tx_origin))
            elif op == 'CALLER':
                stk.append(utils.coerce_to_int(msg.sender))
            elif op == 'CALLVALUE':
                stk.append(msg.value)
            elif op == 'CALLDATALOAD':
                stk.append(msg.data.extract32(stk.pop()))
            elif op == 'CALLDATASIZE':
                stk.append(msg.data.size)
            elif op == 'CALLDATACOPY':
                mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                msg.data.extract_copy(mem, mstart, dstart, size)
            elif op == 'CODESIZE':
                stk.append(len(processed_code))
            elif op == 'CODECOPY':
                start, s1, size = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s1 + i < len(processed_code):
                        mem[start + i] = processed_code[s1 + i][4]
                    else:
                        mem[start + i] = 0
            elif op == 'GASPRICE':
                stk.append(ext.tx_gasprice)
            elif op == 'EXTCODESIZE':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                stk.append(len(ext.get_code(addr) or b''))
            elif op == 'EXTCODECOPY':
                addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                start, s2, size = stk.pop(), stk.pop(), stk.pop()
                extcode = ext.get_code(addr) or b''
                assert utils.is_string(extcode)
                if not mem_extend(mem, compustate, op, start, size):
                    return vm_exception('OOG EXTENDING MEMORY')
                if not data_copy(compustate, size):
                    return vm_exception('OOG COPY DATA')
                for i in range(size):
                    if s2 + i < len(extcode):
                        mem[start + i] = utils.safe_ord(extcode[s2 + i])
                    else:
                        mem[start + i] = 0
        elif opcode < 0x50:
            if op == 'BLOCKHASH':
                stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop())))
            elif op == 'COINBASE':
                stk.append(utils.big_endian_to_int(ext.block_coinbase))
            elif op == 'TIMESTAMP':
                stk.append(ext.block_timestamp)
            elif op == 'NUMBER':
                stk.append(ext.block_number)
            elif op == 'DIFFICULTY':
                stk.append(ext.block_difficulty)
            elif op == 'GASLIMIT':
                stk.append(ext.block_gas_limit)
        elif opcode < 0x60:
            if op == 'POP':
                stk.pop()
            elif op == 'MLOAD':
                s0 = stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = b''.join(map(ascii_chr, mem[s0: s0 + 32]))
                stk.append(utils.big_endian_to_int(data))
            elif op == 'MSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 32):
                    return vm_exception('OOG EXTENDING MEMORY')
                v = s1
                for i in range(31, -1, -1):
                    mem[s0 + i] = v % 256
                    v //= 256
            elif op == 'MSTORE8':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, 1):
                    return vm_exception('OOG EXTENDING MEMORY')
                mem[s0] = s1 % 256
            elif op == 'SLOAD':
                stk.append(ext.get_storage_data(msg.to, stk.pop()))
            elif op == 'SSTORE':
                s0, s1 = stk.pop(), stk.pop()
                if ext.get_storage_data(msg.to, s0):
                    gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                    refund = 0 if s1 else opcodes.GSTORAGEREFUND
                else:
                    gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                    refund = 0
                if compustate.gas < gascost:
                    return vm_exception('OUT OF GAS')
                compustate.gas -= gascost
                ext.add_refund(refund)  # adds neg gascost as a refund if below zero
                ext.set_storage_data(msg.to, s0, s1)
            elif op == 'JUMP':
                compustate.pc = stk.pop()
                opnew = processed_code[compustate.pc][0] if \
                    compustate.pc < len(processed_code) else 'STOP'
                if opnew != 'JUMPDEST':
                    return vm_exception('BAD JUMPDEST')
            elif op == 'JUMPI':
                s0, s1 = stk.pop(), stk.pop()
                if s1:
                    compustate.pc = s0
                    opnew = processed_code[compustate.pc][0] if \
                        compustate.pc < len(processed_code) else 'STOP'
                    if opnew != 'JUMPDEST':
                        return vm_exception('BAD JUMPDEST')
            elif op == 'PC':
                stk.append(compustate.pc - 1)
            elif op == 'MSIZE':
                stk.append(len(mem))
            elif op == 'GAS':
                stk.append(compustate.gas)  # AFTER subtracting cost 1
        elif op[:4] == 'PUSH':
            pushnum = int(op[4:])
            compustate.pc += pushnum
            stk.append(pushval)
        elif op[:3] == 'DUP':
            depth = int(op[3:])
            stk.append(stk[-depth])
        elif op[:4] == 'SWAP':
            depth = int(op[4:])
            temp = stk[-depth - 1]
            stk[-depth - 1] = stk[-1]
            stk[-1] = temp

        elif op[:3] == 'LOG':
            """
            0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
            a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                    MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
            b. Logs are kept track of during tx execution exactly the same way as suicides
               (except as an ordered list, not a set).
               Each log is in the form [address, [topic1, ... ], data] where:
               * address is what the ADDRESS opcode would output
               * data is mem[MEMSTART: MEMSTART + MEMSZ]
               * topics are as provided by the opcode
            c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
            """
            depth = int(op[3:])
            mstart, msz = stk.pop(), stk.pop()
            topics = [stk.pop() for x in range(depth)]
            compustate.gas -= msz * opcodes.GLOGBYTE
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            data = b''.join(map(ascii_chr, mem[mstart: mstart + msz]))
            ext.log(msg.to, topics, data)
            log_log.trace('LOG', to=msg.to, topics=topics, data=list(map(utils.safe_ord, data)))
            # print('LOG', msg.to, topics, list(map(ord, data)))

        elif op == 'CREATE':
            value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, mstart, msz):
                return vm_exception('OOG EXTENDING MEMORY')
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                cd = CallData(mem, mstart, msz)
                create_msg = Message(msg.to, b'', value, compustate.gas, cd, msg.depth + 1)
                o, gas, addr = ext.create(create_msg)
                if o:
                    stk.append(utils.coerce_to_int(addr))
                    compustate.gas = gas
                else:
                    stk.append(0)
                    compustate.gas = 0
            else:
                stk.append(0)
        elif op == 'CALL':
            gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            to = utils.encode_int(to)
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \
                (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                cd = CallData(mem, meminstart, meminsz)
                call_msg = Message(msg.to, to, value, submsg_gas, cd,
                                   msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'CALLCODE' or op == 'DELEGATECALL':
            if op == 'CALLCODE':
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
            else:
                gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                    stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                value = 0
            if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                    not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                return vm_exception('OOG EXTENDING MEMORY')
            extra_gas = (value > 0) * opcodes.GCALLVALUETRANSFER
            submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
            if compustate.gas < gas + extra_gas:
                return vm_exception('OUT OF GAS', needed=gas+extra_gas)
            if ext.get_balance(msg.to) >= value and msg.depth < 1024:
                compustate.gas -= (gas + extra_gas)
                to = utils.encode_int(to)
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                cd = CallData(mem, meminstart, meminsz)
                if ext.post_homestead_hardfork() and op == 'DELEGATECALL':
                    call_msg = Message(msg.sender, msg.to, msg.value, submsg_gas, cd,
                                       msg.depth + 1, code_address=to, transfers_value=False)
                elif op == 'DELEGATECALL':
                    return vm_exception('OPCODE INACTIVE')
                else:
                    call_msg = Message(msg.to, msg.to, value, submsg_gas, cd,
                                       msg.depth + 1, code_address=to)
                result, gas, data = ext.msg(call_msg)
                if result == 0:
                    stk.append(0)
                else:
                    stk.append(1)
                    compustate.gas += gas
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
            else:
                compustate.gas -= (gas + extra_gas - submsg_gas)
                stk.append(0)
        elif op == 'RETURN':
            s0, s1 = stk.pop(), stk.pop()
            if not mem_extend(mem, compustate, op, s0, s1):
                return vm_exception('OOG EXTENDING MEMORY')
            return peaceful_exit('RETURN', compustate.gas, mem[s0: s0 + s1])
        elif op == 'SUICIDE':
            to = utils.encode_int(stk.pop())
            to = ((b'\x00' * (32 - len(to))) + to)[12:]
            xfer = ext.get_balance(msg.to)
            ext.set_balance(to, ext.get_balance(to) + xfer)
            ext.set_balance(msg.to, 0)
            ext.add_suicide(msg.to)
            # print('suiciding %s %s %d' % (msg.to, to, xfer))
            return 1, compustate.gas, []