Exemplo n.º 1
0
Arquivo: chain.py Projeto: ofek/py-evm
    def from_genesis(cls,
                     db,
                     genesis_params,
                     genesis_state=None):
        """
        Initialize the Chain from a genesis state.
        """
        state_db = State(db)

        if genesis_state is None:
            genesis_state = {}

        for account, account_data in genesis_state.items():
            state_db.set_balance(account, account_data['balance'])
            state_db.set_nonce(account, account_data['nonce'])
            state_db.set_code(account, account_data['code'])

            for slot, value in account_data['storage'].items():
                state_db.set_storage(account, slot, value)

        genesis_header = BlockHeader(**genesis_params)
        if genesis_header.state_root != state_db.root_hash:
            raise ValidationError(
                "The provided genesis state root does not match the computed "
                "genesis state root.  Got {0}.  Expected {1}".format(
                    state_db.root_hash,
                    genesis_header.state_root,
                )
            )

        genesis_chain = cls(db, genesis_header)
        persist_block_to_db(db, genesis_chain.get_block())
        add_block_number_to_hash_lookup(db, genesis_chain.get_block())

        return cls(db, genesis_chain.create_header_from_parent(genesis_header))
Exemplo n.º 2
0
    def from_genesis(cls, genesis_params, genesis_state=None):
        """
        Initialize the EVM from a genesis state.
        """
        if cls.db is None:
            raise ValueError("MetaEVM class must have a db")

        state_db = State(cls.db)

        if genesis_state is None:
            genesis_state = {}

        for account, account_data in genesis_state.items():
            state_db.set_balance(account, account_data['balance'])
            state_db.set_nonce(account, account_data['nonce'])
            state_db.set_code(account, account_data['code'])

            for slot, value in account_data['storage']:
                state_db.set_storage(account, slot, value)

        genesis_header = BlockHeader(**genesis_params)
        if genesis_header.state_root != state_db.root_hash:
            raise ValidationError(
                "The provided genesis state root does not match the computed "
                "genesis state root.  Got {0}.  Expected {1}".format(
                    state_db.root_hash,
                    genesis_header.state_root,
                ))

        meta_evm = cls(header=genesis_header)
        evm = meta_evm.get_evm()
        persist_block_to_db(meta_evm.db, evm.block)

        meta_evm.header = evm.create_header_from_parent(genesis_header)
        return meta_evm
Exemplo n.º 3
0
    def from_genesis(cls, db, genesis_params, genesis_state=None):
        """
        Initialize the EVM from a genesis state.
        """
        state_db = State(db)

        if genesis_state is None:
            genesis_state = {}

        for account, account_data in genesis_state.items():
            state_db.set_balance(account, account_data['balance'])
            state_db.set_nonce(account, account_data['nonce'])
            state_db.set_code(account, account_data['code'])

            for slot, value in account_data['storage'].items():
                state_db.set_storage(account, slot, value)

        genesis_header = BlockHeader(**genesis_params)
        if genesis_header.state_root != state_db.root_hash:
            raise ValidationError(
                "The provided genesis state root does not match the computed "
                "genesis state root.  Got {0}.  Expected {1}".format(
                    state_db.root_hash,
                    genesis_header.state_root,
                ))

        evm = cls(db, genesis_header)
        persist_block_to_db(evm.db, evm.get_block())

        # XXX: It doesn't feel right to overwrite evm.header here given that
        # it is set by EVM.__init__, which we called above.
        evm.header = evm.create_header_from_parent(genesis_header)
        return evm
Exemplo n.º 4
0
    def __init__(self, evm, db):
        if db is None:
            raise ValueError("VM classes must have a `db`")

        self.db = db
        self.evm = evm

        block_class = self.get_block_class()
        self.block = block_class.from_header(header=self.evm.header, db=db)
        self.state_db = State(db=self.db, root_hash=self.evm.header.state_root)
Exemplo n.º 5
0
    def __init__(self, header, db=None):
        if db is not None:
            self.db = db

        if self.db is None:
            raise ValueError("EVM classes must have a `db`")

        self.header = header

        block_class = self.get_block_class()
        self.block = block_class.from_header(header=self.header)
        self.state_db = State(db=self.db, root_hash=self.header.state_root)
Exemplo n.º 6
0
    def __init__(self, header, transactions=None, uncles=None, db=None):
        if transactions is None:
            transactions = []
        if uncles is None:
            uncles = []

        self.db = db

        if self.db is None:
            raise TypeError("Block must have a db")

        super(FrontierBlock, self).__init__(
            header=header,
            transactions=transactions,
            uncles=uncles,
        )

        self.state_db = State(self.db, root_hash=self.header.state_root)
        self.transaction_db = Trie(self.db,
                                   root_hash=self.header.transaction_root)
        self.receipt_db = Trie(self.db, root_hash=self.header.receipts_root)
Exemplo n.º 7
0
 def block(self, value):
     self._block = value
     self.state_db = State(db=self.db, root_hash=value.header.state_root)
Exemplo n.º 8
0
class VM(object):
    """
    The VM class represents the EVM for a specific protocol definition.
    Defining an EVM for an ethereum network involves defining individual VM
    classes for each protocol fork within that network.
    """
    db = None

    opcodes = None
    block_class = None

    def __init__(self, header, db):
        if db is None:
            raise ValueError("VM classes must have a `db`")

        self.db = db

        block_class = self.get_block_class()
        self.block = block_class.from_header(header=header, db=db)

    @classmethod
    def configure(cls,
                  name=None,
                  **overrides):
        if name is None:
            name = cls.__name__

        for key in overrides:
            if not hasattr(cls, key):
                raise TypeError(
                    "The VM.configure cannot set attributes that are not "
                    "already present on the base class.  The attribute `{0}` was "
                    "not found on the base class `{1}`".format(key, cls)
                )
        return type(name, (cls,), overrides)

    _block = None
    state_db = None

    @property
    def block(self):
        if self._block is None:
            raise AttributeError("No block property set")
        return self._block

    @block.setter
    def block(self, value):
        self._block = value
        self.state_db = State(db=self.db, root_hash=value.header.state_root)

    #
    # Logging
    #
    @property
    def logger(self):
        return logging.getLogger('evm.vm.base.VM.{0}'.format(self.__class__.__name__))

    #
    # Execution
    #
    def apply_transaction(self, transaction):
        """
        Apply the transaction to the vm in the current block.
        """
        computation = self.execute_transaction(transaction)
        # NOTE: mutation
        self.block = self.block.add_transaction(
            transaction=transaction,
            computation=computation,
        )
        return computation

    def execute_transaction(self, transaction):
        """
        Execute the transaction in the vm.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_create_message(self, message):
        """
        Execution of an VM message to create a new contract.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_message(self, message):
        """
        Execution of an VM message.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_computation(self, message):
        """
        Perform the computation that would be triggered by the VM message.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Mining
    #
    def get_block_reward(self, block_number):
        return BLOCK_REWARD

    def get_nephew_reward(self, block_number):
        return NEPHEW_REWARD

    def import_block(self, block):
        self.configure_header(
            coinbase=block.header.coinbase,
            gas_limit=block.header.gas_limit,
            timestamp=block.header.timestamp,
            extra_data=block.header.extra_data,
            mix_hash=block.header.mix_hash,
            nonce=block.header.nonce,
        )

        for transaction in block.transactions:
            self.apply_transaction(transaction)

        for uncle in block.uncles:
            self.block.add_uncle(uncle)

        mined_block = self.mine_block()
        if mined_block != block:
            diff = diff_rlp_object(mined_block, block)
            longest_field_name = max(len(field_name) for field_name, _, _ in diff)
            error_message = (
                "Mismatch between block and imported block on {0} fields:\n - {1}".format(
                    len(diff),
                    "\n - ".join(tuple(
                        "{0}:\n    (actual)  : {1}\n    (expected): {2}".format(
                            pad_right(field_name, longest_field_name, ' '),
                            actual,
                            expected,
                        )
                        for field_name, actual, expected
                        in diff
                    )),
                )
            )
            raise ValidationError(error_message)

        return mined_block

    def mine_block(self, *args, **kwargs):
        """
        Mine the current block.
        """
        block = self.block
        block.mine(*args, **kwargs)

        if block.number > 0:
            block_reward = self.get_block_reward(block.number) + (
                len(block.uncles) * self.get_nephew_reward(block.number)
            )

            self.state_db.delta_balance(block.header.coinbase, block_reward)
            self.logger.debug(
                "BLOCK REWARD: %s -> %s",
                block_reward,
                block.header.coinbase,
            )

            for uncle in block.uncles:
                uncle_reward = BLOCK_REWARD * (
                    UNCLE_DEPTH_PENALTY_FACTOR + uncle.block_number - block.number
                ) // UNCLE_DEPTH_PENALTY_FACTOR
                self.state_db.delta_balance(uncle.coinbase, uncle_reward)
                self.logger.debug(
                    "UNCLE REWARD REWARD: %s -> %s",
                    uncle_reward,
                    uncle.coinbase,
                )

            self.logger.debug('BEFORE ROOT: %s', block.header.state_root)
            block.header.state_root = self.state_db.root_hash
            self.logger.debug('STATE_ROOT: %s', block.header.state_root)

        return block

    #
    # Transactions
    #
    def get_transaction_class(self):
        """
        Return the class that this VM uses for transactions.
        """
        return self.get_block_class().get_transaction_class()

    def create_transaction(self, *args, **kwargs):
        """
        Proxy for instantiating a transaction for this VM.
        """
        return self.get_transaction_class()(*args, **kwargs)

    def create_unsigned_transaction(self, *args, **kwargs):
        """
        Proxy for instantiating a transaction for this VM.
        """
        return self.get_transaction_class().create_unsigned_transaction(*args, **kwargs)

    def validate_transaction(self, transaction):
        """
        Perform evm-aware validation checks on the transaction.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Blocks
    #
    _block_class = None

    def get_block_class(self):
        """
        Return the class that this VM uses for blocks.
        """
        if self._block_class is None:
            raise AttributeError("No `_block_class` has been set for this VM")

        return self._block_class

    def get_block_by_header(self, block_header):
        return self.get_block_class().from_header(block_header, self.db)

    def get_block_hash(self, block_number):
        """
        For getting block hash for any block number in the the last 256 blocks.
        """
        ancestor_depth = self.block.number - block_number
        if 1 <= ancestor_depth <= 256:
            return lookup_block_hash(self.db, block_number)
        else:
            return b''

    #
    # Headers
    #
    @classmethod
    def create_header_from_parent(cls, parent_header, **header_params):
        """
        Creates and initializes a new block header from the provided
        `parent_header`.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def configure_header(self, **header_params):
        """
        Setup the current header with the provided parameters.  This can be
        used to set fields like the gas limit or timestamp to value different
        than their computed defaults.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Snapshot and Revert
    #
    def snapshot(self):
        """
        Perform a full snapshot of the current state of the VM.

        TODO: This needs to do more than just snapshot the state_db but this is a start.
        """
        return self.state_db.snapshot()

    def revert(self, snapshot):
        """
        Revert the VM to the state

        TODO: This needs to do more than just snapshot the state_db but this is a start.
        """
        return self.state_db.revert(snapshot)

    #
    # Opcode API
    #
    def get_opcode_fn(self, opcode):
        try:
            return self.opcodes[opcode]
        except KeyError:
            return InvalidOpcode(opcode)
Exemplo n.º 9
0
class VM(object):
    """
    The VM class represents the Chain rules for a specific protocol definition
    such as the Frontier or Homestead network.  Defining an Chain  defining
    individual VM classes for each fork of the protocol rules within that
    network.
    """
    db = None

    opcodes = None
    _block_class = None

    def __init__(self, header, db):
        if db is None:
            raise ValueError("VM classes must have a `db`")

        self.db = db

        block_class = self.get_block_class()
        self.block = block_class.from_header(header=header, db=db)

    @classmethod
    def configure(cls, name=None, **overrides):
        if name is None:
            name = cls.__name__

        for key in overrides:
            if not hasattr(cls, key):
                raise TypeError(
                    "The VM.configure cannot set attributes that are not "
                    "already present on the base class.  The attribute `{0}` was "
                    "not found on the base class `{1}`".format(key, cls))
        return type(name, (cls, ), overrides)

    _block = None
    state_db = None

    @property
    def block(self):
        if self._block is None:
            raise AttributeError("No block property set")
        return self._block

    @block.setter
    def block(self, value):
        self._block = value
        self.state_db = State(db=self.db, root_hash=value.header.state_root)

    #
    # Logging
    #
    @property
    def logger(self):
        return logging.getLogger('evm.vm.base.VM.{0}'.format(
            self.__class__.__name__))

    #
    # Execution
    #
    def apply_transaction(self, transaction):
        """
        Apply the transaction to the vm in the current block.
        """
        computation = self.execute_transaction(transaction)
        # NOTE: mutation. Needed in order to update self.state_db, so we should be able to get rid
        # of this once we fix https://github.com/pipermerriam/py-evm/issues/67
        self.block = self.block.add_transaction(
            transaction=transaction,
            computation=computation,
        )
        return computation

    def execute_transaction(self, transaction):
        """
        Execute the transaction in the vm.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_create_message(self, message):
        """
        Execution of an VM message to create a new contract.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_message(self, message):
        """
        Execution of an VM message.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_computation(self, message):
        """
        Perform the computation that would be triggered by the VM message.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Mining
    #
    def get_block_reward(self, block_number):
        return BLOCK_REWARD

    def get_nephew_reward(self, block_number):
        return NEPHEW_REWARD

    def import_block(self, block):
        self.configure_header(
            coinbase=block.header.coinbase,
            gas_limit=block.header.gas_limit,
            timestamp=block.header.timestamp,
            extra_data=block.header.extra_data,
            mix_hash=block.header.mix_hash,
            nonce=block.header.nonce,
        )

        for transaction in block.transactions:
            self.apply_transaction(transaction)

        for uncle in block.uncles:
            self.block.add_uncle(uncle)

        return self.mine_block()

    def mine_block(self, *args, **kwargs):
        """
        Mine the current block.
        """
        block = self.block
        block.mine(*args, **kwargs)

        if block.number > 0:
            block_reward = self.get_block_reward(block.number) + (
                len(block.uncles) * self.get_nephew_reward(block.number))

            self.state_db.delta_balance(block.header.coinbase, block_reward)
            self.logger.debug(
                "BLOCK REWARD: %s -> %s",
                block_reward,
                block.header.coinbase,
            )

            for uncle in block.uncles:
                uncle_reward = BLOCK_REWARD * (
                    UNCLE_DEPTH_PENALTY_FACTOR + uncle.block_number -
                    block.number) // UNCLE_DEPTH_PENALTY_FACTOR
                self.state_db.delta_balance(uncle.coinbase, uncle_reward)
                self.logger.debug(
                    "UNCLE REWARD REWARD: %s -> %s",
                    uncle_reward,
                    uncle.coinbase,
                )

            self.logger.debug('BEFORE ROOT: %s', block.header.state_root)
            block.header.state_root = self.state_db.root_hash
            self.logger.debug('STATE_ROOT: %s', block.header.state_root)

        return block

    #
    # Transactions
    #
    def get_transaction_class(self):
        """
        Return the class that this VM uses for transactions.
        """
        return self.get_block_class().get_transaction_class()

    def create_transaction(self, *args, **kwargs):
        """
        Proxy for instantiating a transaction for this VM.
        """
        return self.get_transaction_class()(*args, **kwargs)

    def create_unsigned_transaction(self, *args, **kwargs):
        """
        Proxy for instantiating a transaction for this VM.
        """
        return self.get_transaction_class().create_unsigned_transaction(
            *args, **kwargs)

    def validate_transaction(self, transaction):
        """
        Perform chain-aware validation checks on the transaction.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Blocks
    #

    @classmethod
    def get_block_class(cls):
        """
        Return the class that this VM uses for blocks.
        """
        if cls._block_class is None:
            raise AttributeError("No `_block_class` has been set for this VM")

        return cls._block_class

    def get_block_by_header(self, block_header):
        return self.get_block_class().from_header(block_header, self.db)

    def get_ancestor_hash(self, block_number):
        """
        Return the hash for the ancestor with the given number
        """
        ancestor_depth = self.block.number - block_number
        if ancestor_depth > 256 or ancestor_depth < 1:
            return b''
        h = get_block_header_by_hash(self.db, self.block.header.parent_hash)
        while h.block_number != block_number:
            h = get_block_header_by_hash(self.db, h.parent_hash)
        return h.hash

    #
    # Headers
    #
    @classmethod
    def create_header_from_parent(cls, parent_header, **header_params):
        """
        Creates and initializes a new block header from the provided
        `parent_header`.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def configure_header(self, **header_params):
        """
        Setup the current header with the provided parameters.  This can be
        used to set fields like the gas limit or timestamp to value different
        than their computed defaults.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Snapshot and Revert
    #
    def snapshot(self):
        """
        Perform a full snapshot of the current state of the VM.

        TODO: This needs to do more than just snapshot the state_db but this is a start.
        """
        return self.state_db.snapshot()

    def revert(self, snapshot):
        """
        Revert the VM to the state

        TODO: This needs to do more than just snapshot the state_db but this is a start.
        """
        return self.state_db.revert(snapshot)

    #
    # Opcode API
    #
    def get_opcode_fn(self, opcode):
        try:
            return self.opcodes[opcode]
        except KeyError:
            return InvalidOpcode(opcode)
Exemplo n.º 10
0
class BaseEVM(object):
    """
    The EVM class is... TODO:
    """
    db = None

    block = None

    opcodes = None
    block_class = None

    def __init__(self, header, db=None):
        if db is not None:
            self.db = db

        if self.db is None:
            raise ValueError("EVM classes must have a `db`")

        self.header = header

        block_class = self.get_block_class()
        self.block = block_class.from_header(header=self.header)
        self.state_db = State(db=self.db, root_hash=self.header.state_root)

    @classmethod
    def configure(cls, name=None, **overrides):
        if name is None:
            name = cls.__name__

        for key in overrides:
            if not hasattr(cls, key):
                raise TypeError(
                    "The EVM.configure cannot set attributes that are not "
                    "already present on the base class.  The attribute `{0}` was "
                    "not found on the base class `{1}`".format(key, cls))
        return type(name, (cls, ), overrides)

    #
    # Logging
    #
    @property
    def logger(self):
        return logging.getLogger('evm.vm.evm.EVM.{0}'.format(
            self.__class__.__name__))

    #
    # Execution
    #
    def apply_transaction(self, transaction):
        """
        Execution of a transaction in the EVM.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_create_message(self, message):
        """
        Execution of an EVM message to create a new contract.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_message(self, message):
        """
        Execution of an EVM message.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def apply_computation(self, message):
        """
        Perform the computation that would be triggered by the EVM message.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Mining
    #
    def get_block_reward(self, block_number):
        return BLOCK_REWARD

    def get_nephew_reward(self, block_number):
        return NEPHEW_REWARD

    def mine_block(self, *args, **kwargs):
        """
        Mine the current block.
        """
        block = self.block.mine(*args, **kwargs)

        if block.number > 0:
            block_reward = self.get_block_reward(block.number) + (
                len(block.uncles) * self.get_nephew_reward(block.number))

            self.state_db.delta_balance(block.header.coinbase, block_reward)

            for uncle in block.uncles:
                uncle_reward = block_reward * (
                    UNCLE_DEPTH_PENALTY_FACTOR + uncle.block_number -
                    block.number) // UNCLE_DEPTH_PENALTY_FACTOR
                self.state_db.delta_balance(uncle.coinbase, uncle_reward)

            block.header.state_root = self.state_db.root_hash

        return block

    #
    # Transactions
    #
    @classmethod
    def get_transaction_class(cls):
        """
        Return the class that this EVM uses for transactions.
        """
        return cls.get_block_class().get_transaction_class()

    def create_transaction(self, *args, **kwargs):
        """
        Proxy for instantiating a transaction for this EVM.
        """
        return self.get_transaction_class()(*args, **kwargs)

    def validate_transaction(self, transaction):
        """
        Perform evm-aware validation checks on the transaction.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Blocks
    #
    _block_class = None

    @classmethod
    def get_block_class(cls):
        """
        Return the class that this EVM uses for blocks.
        """
        if cls._block_class is None:
            raise AttributeError("No `_block_class` has been set for this EVM")

        block_class = cls._block_class.configure(db=cls.db)
        return block_class

    def get_block_by_hash(self, block_hash):
        block_class = self.get_block_class()
        block = rlp.decode(self.db.get(block_hash),
                           sedes=block_class,
                           db=self.db)
        return block

    def get_block_hash(self, block_number):
        """
        For getting block hash for any block number in the the last 256 blocks.
        """
        raise NotImplementedError("Not yet implemented")

    #
    # Headers
    #
    def create_header_from_parent(self, parent_header, **header_params):
        """
        Creates and initializes a new block header from the provided
        `parent_header`.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    def configure_header(self, **header_params):
        """
        Setup the current header with the provided parameters.  This can be
        used to set fields like the gas limit or timestamp to value different
        than their computed defaults.
        """
        raise NotImplementedError("Must be implemented by subclasses")

    #
    # Snapshot and Revert
    #
    def snapshot(self):
        """
        Perform a full snapshot of the current state of the EVM.

        TODO: This needs to do more than just snapshot the state_db but this is a start.
        """
        return self.state_db.snapshot()

    def revert(self, snapshot):
        """
        Revert the EVM to the state

        TODO: This needs to do more than just snapshot the state_db but this is a start.
        """
        return self.state_db.revert(snapshot)

    #
    # Opcode API
    #
    def get_opcode_fn(self, opcode):
        try:
            return self.opcodes[opcode]
        except KeyError:
            return InvalidOpcode(opcode)