Crate sputnikvm

source ·
Expand description

SputnikVM implementation, traits and structs.

SputnikVM works on two different levels. It handles:

  1. a transaction, or
  2. an Ethereum execution context.

To interact with the virtual machine, you usually only need to work with VM methods.

A SputnikVM’s Lifecycle

A VM can be started after it is given a Transaction (or Context) and a BlockHeader. The user can then fire or step to run it. fire runs the EVM code (given in field code of the transaction) until it finishes or cannot continue. However step only runs at most one instruction. If the virtual machine needs some information (accounts in the current block, or block hashes of previous blocks) it fails, returning a RequireError enumeration. With the data returned in the RequireError enumeration, one can use the methods commit_account and commit_blockhash to commit the information to the VM. fire or step can be subsequently called to restart from that point. The current VM status can always be obtained using the status function. Again, see VM for a list of methods that can be applied.

Patch: Specifying a Network and Hard-fork

Every VM is associated with a Patch. This patch tells the VM which Ethereum network and which hard fork it is on. You will need to specify the patch as the type parameter. To interact with multiple patches at the same time, it is recommended that you use trait objects.

The example below creates a new SputnikVM and stores the object in vm which can be used to fire, step or get status on. To do this, it must first create a transaction and a block header. The patch associated with the VM is either EmbeddedPatch or VMTestPatch depending on an arbitrary block number value set at the beginning of the program.

extern crate bigint;
extern crate sputnikvm;

use sputnikvm::{EmbeddedPatch, VMTestPatch,
                HeaderParams, ValidTransaction, TransactionAction,
                VM, SeqTransactionVM};
use bigint::{Gas, U256, Address};
use std::rc::Rc;

fn main() {
  let block_number = 1000;
  let transaction = ValidTransaction {
    caller: Some(Address::default()),
    gas_price: Gas::zero(),
    gas_limit: Gas::max_value(),
    action: TransactionAction::Create,
    value: U256::zero(),
    input: Rc::new(Vec::new()),
    nonce: U256::zero()
  };
  let header = HeaderParams {
    beneficiary: Address::default(),
    timestamp: 0,
    number: U256::zero(),
    difficulty: U256::zero(),
    gas_limit: Gas::zero()
  };
  let vm = if block_number < 500 {
    SeqTransactionVM::<VMTestPatch>::new(
      transaction, header);
  } else {
    SeqTransactionVM::<EmbeddedPatch>::new(
      transaction, header);
  };
}

Transaction Execution

To start a VM on the Transaction level, use the TransactionVM struct. Usually, you want to use the sequential memory module which can be done using the type definition SeqTransactionVM.

Calling TransactionVM::new or SeqTransactionVM::new requires the transaction passed in to be valid (according to the rules for an Ethereum transaction). If the transaction is invalid, the VM will probably panic. If you want to handle untrusted transactions, you should use SeqTransactionVM::new_untrusted, which will not panic but instead return an error if the transaction is invalid.

Context Execution

To start a VM on the Context level, use the ContextVM struct. Usually, you use the sequential memory module with the type definition SeqContextVM. Context execution, as with other EVM implementations, will not handle transaction-level gas reductions.

Re-exports

pub use self::errors::OnChainError;
pub use self::errors::NotSupportedError;
pub use self::errors::RequireError;
pub use self::errors::CommitError;
pub use self::errors::PreExecutionError;

Modules

VM errors

Structs

A struct that manages the current account state for one EVM.
A struct that manages the current blockhash state for one EVM.
A VM context. See the Yellow Paper for more information.
A VM that executes using a context and block information.
ECREC precompiled contract.
Mainnet account patch
Mainnet account patch
Embedded patch.
Block header.
ID precompiled contract.
A VM state with PC.
Represents a program counter in EVM.
Represents a mutable program counter in EVM.
RIP160 precompiled contract.
A VM runtime. Only available in eval.
SHA256 precompiled contract.
A sequencial memory. It uses Rust’s Vec for internal representation.
Represents an EVM stack.
A VM state without PC.
Internal representation of an account storage. It will return a RequireError if trying to access non-existing storage.
A VM that executes using a transaction and block information.
Represents an untrusted Ethereum transaction.
Patch sepcific for the jsontests crate.
Represents an Ethereum transaction.
Mapping of valid jump destination from code.

Enums

Represents an account. This is usually returned by the EVM.
A single account commitment.
Instructions for the program counter. This is the same as Opcode except PUSH, which might take longer length.
Represents the current runtime status.
Opcode enum. One-to-one corresponding to an u8 value.
VM Status

Statics

Static value of ECREC precompiled contract.
Default precompiled collections.
Static value of ID precompiled contract.
Static value of RIP160 precompiled contract.
Static value of SHA256 precompiled contract.

Traits

Account patch for account related variables.
Represent a memory in EVM. Read should always succeed. Write can fall.
Represents different block range context.
Represent a precompiled contract.
Represents an EVM. This is usually the main interface for clients to interact with.

Type Definitions

A sequential VM. It uses sequential memory representation and hash map storage for accounts.
A sequential transaction VM. This is same as SeqContextVM except it runs at transaction level.