Skip to content

Commit

Permalink
Merge pull request #153 from sorpaas/master
Browse files Browse the repository at this point in the history
VM trait/struct refactoring
  • Loading branch information
sjmackenzie committed Jun 1, 2017
2 parents 865d83a + 6cf0186 commit 62c6667
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 65 deletions.
12 changes: 6 additions & 6 deletions jsontests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use serde_json::Value;
use std::str::FromStr;
use sputnikvm::{Gas, M256, U256, Address, read_hex};
use sputnikvm::vm::errors::RequireError;
use sputnikvm::vm::{VM, SeqVM, AccountCommitment, Context, Account, Storage, Patch, VMStatus, VMTEST_PATCH};
use sputnikvm::vm::{VM, SeqContextVM, AccountCommitment, Context, Account, Storage, Patch, VMStatus, VMTEST_PATCH};

pub fn fire_with_block(machine: &mut SeqVM, block: &JSONBlock) {
pub fn fire_with_block(machine: &mut SeqContextVM, block: &JSONBlock) {
loop {
match machine.fire() {
Err(RequireError::Account(address)) => {
Expand Down Expand Up @@ -50,7 +50,7 @@ pub fn fire_with_block(machine: &mut SeqVM, block: &JSONBlock) {
}
}

pub fn apply_to_block(machine: &SeqVM, block: &mut JSONBlock) {
pub fn apply_to_block(machine: &SeqContextVM, block: &mut JSONBlock) {
for account in machine.accounts() {
let account = (*account).clone();
block.apply_account(account);
Expand All @@ -61,13 +61,13 @@ pub fn apply_to_block(machine: &SeqVM, block: &mut JSONBlock) {
}
}

pub fn create_machine(v: &Value, block: &JSONBlock) -> SeqVM {
pub fn create_machine(v: &Value, block: &JSONBlock) -> SeqContextVM {
let transaction = create_context(v);

SeqVM::new(transaction, block.block_header(), &VMTEST_PATCH)
SeqContextVM::new(transaction, block.block_header(), &VMTEST_PATCH)
}

pub fn test_machine(v: &Value, machine: &SeqVM, block: &JSONBlock, debug: bool) -> bool {
pub fn test_machine(v: &Value, machine: &SeqContextVM, block: &JSONBlock, debug: bool) -> bool {
let ref callcreates = v["callcreates"];

if callcreates.as_array().is_some() {
Expand Down
4 changes: 2 additions & 2 deletions regtests/src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::collections::HashMap;

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqVM, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::{BlockHeader, Context, SeqContextVM, VM, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction};

Expand Down Expand Up @@ -88,7 +88,7 @@ fn main() {

let context = from_rpc_transaction_and_code(&transaction, &code);

let mut vm = SeqVM::new(context.clone(), block_header.clone(), &FRONTIER_PATCH);
let mut vm = SeqContextVM::new(context.clone(), block_header.clone(), &FRONTIER_PATCH);
loop {
match vm.fire() {
Ok(()) => {
Expand Down
13 changes: 0 additions & 13 deletions sputnikvm/src/vm/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,6 @@ impl From<MachineError> for EvalError {
}
}

#[derive(Debug, Clone)]
/// Errors returned by the VM.
pub enum VMError {
/// VM runtime error.
Machine(MachineError),
}

impl From<MachineError> for VMError {
fn from(val: MachineError) -> VMError {
VMError::Machine(val)
}
}

#[derive(Debug, Clone)]
/// Errors stating that the VM requires additional information to
/// continue running.
Expand Down
123 changes: 79 additions & 44 deletions sputnikvm/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod params;
mod eval;
mod commit;
mod patch;
mod transaction;
pub mod errors;

pub use self::memory::{Memory, SeqMemory};
Expand All @@ -28,20 +29,13 @@ pub use self::storage::Storage;
pub use self::params::*;
pub use self::patch::*;
pub use self::eval::{State, Machine, MachineStatus};
pub use self::commit::{AccountCommitment, Account};
pub use self::commit::{AccountCommitment, Account, AccountState, BlockhashState};

use std::collections::hash_map;
use utils::bigint::M256;
use utils::gas::Gas;
use utils::address::Address;
use self::errors::{RequireError, CommitError, VMError};

/// A sequencial VM. It uses sequencial memory representation and hash
/// map storage for accounts.
pub type SeqVM = VM<SeqMemory>;

/// A VM that executes using a context and block information.
pub struct VM<M>(Vec<Machine<M>>, Vec<Context>);
use self::errors::{RequireError, CommitError, MachineError};

#[derive(Debug, Clone)]
/// VM Status
Expand All @@ -52,38 +46,79 @@ pub enum VMStatus {
ExitedOk,
/// VM is stopped due to an error. The state of the VM is before
/// the last failing instruction.
ExitedErr(VMError),
ExitedErr(MachineError),
}

pub trait VM {
fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError>;
fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError>;
fn status(&self) -> VMStatus;
fn step(&mut self) -> Result<(), RequireError>;
fn fire(&mut self) -> Result<(), RequireError> {
loop {
match self.status() {
VMStatus::Running => self.step()?,
VMStatus::ExitedOk | VMStatus::ExitedErr(_) => return Ok(()),
}
}
}
fn accounts(&self) -> hash_map::Values<Address, Account>;
fn out(&self) -> &[u8];
fn available_gas(&self) -> Gas;
fn used_gas(&self) -> Gas;
fn refunded_gas(&self) -> Gas;
fn logs(&self) -> &[Log];
}

/// A sequencial VM. It uses sequencial memory representation and hash
/// map storage for accounts.
pub type SeqContextVM = ContextVM<SeqMemory>;

/// A VM that executes using a context and block information.
pub struct ContextVM<M> {
machines: Vec<Machine<M>>,
history: Vec<Context>
}

impl<M: Memory + Default> VM<M> {
impl<M: Memory + Default> ContextVM<M> {
/// Create a new VM using the given context, block header and patch.
pub fn new(context: Context, block: BlockHeader, patch: &'static Patch) -> VM<M> {
pub fn new(context: Context, block: BlockHeader, patch: &'static Patch) -> Self {
let mut machines = Vec::new();
machines.push(Machine::new(context, block, patch, 1));
VM(machines, Vec::new())
ContextVM {
machines,
history: Vec::new()
}
}

/// Returns the call create history. Only used in testing.
pub fn history(&self) -> &[Context] {
self.history.as_slice()
}
}

impl<M: Memory + Default> VM for ContextVM<M> {
/// Commit an account information to this VM. This should only be
/// used when receiving `RequireError`.
pub fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError> {
for machine in &mut self.0 {
fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError> {
for machine in &mut self.machines {
machine.commit_account(commitment.clone())?;
}
Ok(())
}

/// Commit a block hash to this VM. This should only be used when
/// receiving `RequireError`.
pub fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> {
for machine in &mut self.0 {
fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> {
for machine in &mut self.machines {
machine.commit_blockhash(number, hash)?;
}
Ok(())
}

/// Returns the current status of the VM.
pub fn status(&self) -> VMStatus {
match self.0[0].status() {
fn status(&self) -> VMStatus {
match self.machines[0].status() {
MachineStatus::Running | MachineStatus::InvokeCreate(_) | MachineStatus::InvokeCall(_, _) => VMStatus::Running,
MachineStatus::ExitedOk => VMStatus::ExitedOk,
MachineStatus::ExitedErr(err) => VMStatus::ExitedErr(err.into()),
Expand All @@ -94,24 +129,24 @@ impl<M: Memory + Default> VM<M> {
/// still be `Running`. If the call stack has more than one items,
/// this will only executes the last items' one single
/// instruction.
pub fn step(&mut self) -> Result<(), RequireError> {
match self.0.last().unwrap().status().clone() {
fn step(&mut self) -> Result<(), RequireError> {
match self.machines.last().unwrap().status().clone() {
MachineStatus::Running => {
self.0.last_mut().unwrap().step()
self.machines.last_mut().unwrap().step()
},
MachineStatus::ExitedOk | MachineStatus::ExitedErr(_) => {
if self.0.len() <= 1 {
if self.machines.len() <= 1 {
Ok(())
} else {
let finished = self.0.pop().unwrap();
self.0.last_mut().unwrap().apply_sub(finished);
let finished = self.machines.pop().unwrap();
self.machines.last_mut().unwrap().apply_sub(finished);
Ok(())
}
},
MachineStatus::InvokeCall(context, _) | MachineStatus::InvokeCreate(context) => {
self.1.push(context.clone());
let sub = self.0.last().unwrap().derive(context);
self.0.push(sub);
self.history.push(context.clone());
let sub = self.machines.last().unwrap().derive(context);
self.machines.push(sub);
Ok(())
},
}
Expand All @@ -120,7 +155,7 @@ impl<M: Memory + Default> VM<M> {
/// Run instructions until it reaches a `RequireError` or
/// exits. If this function succeeds, the VM status can only be
/// either `ExitedOk` or `ExitedErr`.
pub fn fire(&mut self) -> Result<(), RequireError> {
fn fire(&mut self) -> Result<(), RequireError> {
loop {
match self.status() {
VMStatus::Running => self.step()?,
Expand All @@ -131,33 +166,33 @@ impl<M: Memory + Default> VM<M> {

/// Returns the changed or committed accounts information up to
/// current execution status.
pub fn accounts(&self) -> hash_map::Values<Address, Account> {
self.0[0].state().account_state.accounts()
fn accounts(&self) -> hash_map::Values<Address, Account> {
self.machines[0].state().account_state.accounts()
}

/// Returns the out value, if any.
pub fn out(&self) -> &[u8] {
self.0[0].state().out.as_slice()
fn out(&self) -> &[u8] {
self.machines[0].state().out.as_slice()
}

/// Returns the available gas of this VM.
pub fn available_gas(&self) -> Gas {
self.0[0].state().available_gas()
fn available_gas(&self) -> Gas {
self.machines[0].state().available_gas()
}

/// Returns the used gas of this VM.
fn used_gas(&self) -> Gas {
self.machines[0].state().used_gas
}

/// Returns the refunded gas of this VM.
pub fn refunded_gas(&self) -> Gas {
self.0[0].state().refunded_gas
fn refunded_gas(&self) -> Gas {
self.machines[0].state().refunded_gas
}

/// Returns logs to be appended to the current block if the user
/// decided to accept the running status of this VM.
pub fn logs(&self) -> &[Log] {
self.0[0].state().logs.as_slice()
}

/// Returns the call create history. Only used in testing.
pub fn history(&self) -> &[Context] {
self.1.as_slice()
fn logs(&self) -> &[Log] {
self.machines[0].state().logs.as_slice()
}
}
37 changes: 37 additions & 0 deletions sputnikvm/src/vm/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use utils::gas::Gas;
use utils::address::Address;
use utils::bigint::U256;

use super::errors::RequireError;
use super::{Context, AccountState};

pub enum Transaction {
MessageCall {
address: Address,
caller: Address,
gas_price: Gas,
gas_limit: Gas,
value: U256,
data: Vec<u8>,
},
ContractCreation {
caller: Address,
gas_price: Gas,
gas_limit: Gas,
value: U256,
init: Vec<u8>,
},
}

impl Transaction {
#[allow(unused_variables)]
pub fn intrinsic_gas(&self) -> Gas {
unimplemented!()
}

#[allow(unused_variables)]
pub fn into_context(self, origin: Option<Address>,
account_state: AccountState) -> Result<Context, RequireError> {
unimplemented!()
}
}

0 comments on commit 62c6667

Please sign in to comment.