From c4cdcb8a9311c225ca386c901445819acc5dd9e6 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 1 Jun 2017 17:00:09 +0800 Subject: [PATCH 1/5] Problem: missing used_gas fn in VM Solution: add used_gas fn --- sputnikvm/src/vm/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sputnikvm/src/vm/mod.rs b/sputnikvm/src/vm/mod.rs index d7db0919..65f7833c 100644 --- a/sputnikvm/src/vm/mod.rs +++ b/sputnikvm/src/vm/mod.rs @@ -145,6 +145,11 @@ impl VM { self.0[0].state().available_gas() } + /// Returns the used gas of this VM. + pub fn used_gas(&self) -> Gas { + self.0[0].state().used_gas + } + /// Returns the refunded gas of this VM. pub fn refunded_gas(&self) -> Gas { self.0[0].state().refunded_gas From aa4ca04de4627ac1c3b5586f46620945264c6fa3 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 1 Jun 2017 17:03:11 +0800 Subject: [PATCH 2/5] Problem: VM's self.0 is not totally clear Solution: give it a name: machines --- sputnikvm/src/vm/mod.rs | 46 +++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/sputnikvm/src/vm/mod.rs b/sputnikvm/src/vm/mod.rs index 65f7833c..5472565c 100644 --- a/sputnikvm/src/vm/mod.rs +++ b/sputnikvm/src/vm/mod.rs @@ -41,7 +41,10 @@ use self::errors::{RequireError, CommitError, VMError}; pub type SeqVM = VM; /// A VM that executes using a context and block information. -pub struct VM(Vec>, Vec); +pub struct VM { + machines: Vec>, + history: Vec +} #[derive(Debug, Clone)] /// VM Status @@ -60,13 +63,16 @@ impl VM { pub fn new(context: Context, block: BlockHeader, patch: &'static Patch) -> VM { let mut machines = Vec::new(); machines.push(Machine::new(context, block, patch, 1)); - VM(machines, Vec::new()) + VM { + machines, + history: Vec::new() + } } /// 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 { + for machine in &mut self.machines { machine.commit_account(commitment.clone())?; } Ok(()) @@ -75,7 +81,7 @@ impl VM { /// 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 { + for machine in &mut self.machines { machine.commit_blockhash(number, hash)?; } Ok(()) @@ -83,7 +89,7 @@ impl VM { /// Returns the current status of the VM. pub fn status(&self) -> VMStatus { - match self.0[0].status() { + match self.machines[0].status() { MachineStatus::Running | MachineStatus::InvokeCreate(_) | MachineStatus::InvokeCall(_, _) => VMStatus::Running, MachineStatus::ExitedOk => VMStatus::ExitedOk, MachineStatus::ExitedErr(err) => VMStatus::ExitedErr(err.into()), @@ -95,23 +101,23 @@ impl VM { /// this will only executes the last items' one single /// instruction. pub fn step(&mut self) -> Result<(), RequireError> { - match self.0.last().unwrap().status().clone() { + 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(()) }, } @@ -132,37 +138,37 @@ impl VM { /// Returns the changed or committed accounts information up to /// current execution status. pub fn accounts(&self) -> hash_map::Values { - self.0[0].state().account_state.accounts() + 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() + 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() + self.machines[0].state().available_gas() } /// Returns the used gas of this VM. pub fn used_gas(&self) -> Gas { - self.0[0].state().used_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 + 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() + self.machines[0].state().logs.as_slice() } /// Returns the call create history. Only used in testing. pub fn history(&self) -> &[Context] { - self.1.as_slice() + self.history.as_slice() } } From bc37b0998fd30fa44307eefb9117abc86fa165cd Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 1 Jun 2017 17:04:49 +0800 Subject: [PATCH 3/5] Problem: VMError does not provide additional abstraction to MachineError Solution: remove VMError, use MachineError directly --- sputnikvm/src/vm/errors.rs | 13 ------------- sputnikvm/src/vm/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/sputnikvm/src/vm/errors.rs b/sputnikvm/src/vm/errors.rs index c6db8b19..cc610724 100644 --- a/sputnikvm/src/vm/errors.rs +++ b/sputnikvm/src/vm/errors.rs @@ -96,19 +96,6 @@ impl From for EvalError { } } -#[derive(Debug, Clone)] -/// Errors returned by the VM. -pub enum VMError { - /// VM runtime error. - Machine(MachineError), -} - -impl From for VMError { - fn from(val: MachineError) -> VMError { - VMError::Machine(val) - } -} - #[derive(Debug, Clone)] /// Errors stating that the VM requires additional information to /// continue running. diff --git a/sputnikvm/src/vm/mod.rs b/sputnikvm/src/vm/mod.rs index 5472565c..4703152d 100644 --- a/sputnikvm/src/vm/mod.rs +++ b/sputnikvm/src/vm/mod.rs @@ -34,7 +34,7 @@ use std::collections::hash_map; use utils::bigint::M256; use utils::gas::Gas; use utils::address::Address; -use self::errors::{RequireError, CommitError, VMError}; +use self::errors::{RequireError, CommitError, MachineError}; /// A sequencial VM. It uses sequencial memory representation and hash /// map storage for accounts. @@ -55,7 +55,7 @@ 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), } impl VM { From 38209f802ba622c896b43e6db66168bcf3ccc36e Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 1 Jun 2017 17:32:34 +0800 Subject: [PATCH 4/5] Problem: #152 wrapping transactions for VM Solution: add back transaction enum --- sputnikvm/src/vm/mod.rs | 3 ++- sputnikvm/src/vm/transaction.rs | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 sputnikvm/src/vm/transaction.rs diff --git a/sputnikvm/src/vm/mod.rs b/sputnikvm/src/vm/mod.rs index 4703152d..11ba0ebe 100644 --- a/sputnikvm/src/vm/mod.rs +++ b/sputnikvm/src/vm/mod.rs @@ -19,6 +19,7 @@ mod params; mod eval; mod commit; mod patch; +mod transaction; pub mod errors; pub use self::memory::{Memory, SeqMemory}; @@ -28,7 +29,7 @@ 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; diff --git a/sputnikvm/src/vm/transaction.rs b/sputnikvm/src/vm/transaction.rs new file mode 100644 index 00000000..d0bd5460 --- /dev/null +++ b/sputnikvm/src/vm/transaction.rs @@ -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, + }, + ContractCreation { + caller: Address, + gas_price: Gas, + gas_limit: Gas, + value: U256, + init: Vec, + }, +} + +impl Transaction { + #[allow(unused_variables)] + pub fn intrinsic_gas(&self) -> Gas { + unimplemented!() + } + + #[allow(unused_variables)] + pub fn into_context(self, origin: Option
, + account_state: AccountState) -> Result { + unimplemented!() + } +} From 05dc87d04e4f653020ae3cf5433476f0b2a47e98 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 1 Jun 2017 17:44:29 +0800 Subject: [PATCH 5/5] Problem: #152 wrapping transactions for VM Solution: make VM a trait, and change the original VM to ContextVM --- jsontests/src/lib.rs | 12 +++--- regtests/src/bin/main.rs | 4 +- sputnikvm/src/vm/mod.rs | 81 ++++++++++++++++++++++++++-------------- 3 files changed, 60 insertions(+), 37 deletions(-) diff --git a/jsontests/src/lib.rs b/jsontests/src/lib.rs index 6b017ebc..72164d33 100644 --- a/jsontests/src/lib.rs +++ b/jsontests/src/lib.rs @@ -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)) => { @@ -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); @@ -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() { diff --git a/regtests/src/bin/main.rs b/regtests/src/bin/main.rs index 2c7aed0c..1e9ec4f4 100644 --- a/regtests/src/bin/main.rs +++ b/regtests/src/bin/main.rs @@ -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}; @@ -80,7 +80,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(()) => { diff --git a/sputnikvm/src/vm/mod.rs b/sputnikvm/src/vm/mod.rs index 11ba0ebe..55b5826d 100644 --- a/sputnikvm/src/vm/mod.rs +++ b/sputnikvm/src/vm/mod.rs @@ -37,16 +37,6 @@ use utils::gas::Gas; use utils::address::Address; use self::errors::{RequireError, CommitError, MachineError}; -/// A sequencial VM. It uses sequencial memory representation and hash -/// map storage for accounts. -pub type SeqVM = VM; - -/// A VM that executes using a context and block information. -pub struct VM { - machines: Vec>, - history: Vec -} - #[derive(Debug, Clone)] /// VM Status pub enum VMStatus { @@ -59,20 +49,58 @@ pub enum VMStatus { ExitedErr(MachineError), } -impl VM { +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; + 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; + +/// A VM that executes using a context and block information. +pub struct ContextVM { + machines: Vec>, + history: Vec +} + +impl ContextVM { /// Create a new VM using the given context, block header and patch. - pub fn new(context: Context, block: BlockHeader, patch: &'static Patch) -> VM { + 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 { + ContextVM { machines, history: Vec::new() } } + /// Returns the call create history. Only used in testing. + pub fn history(&self) -> &[Context] { + self.history.as_slice() + } +} + +impl VM for ContextVM { /// 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> { + fn commit_account(&mut self, commitment: AccountCommitment) -> Result<(), CommitError> { for machine in &mut self.machines { machine.commit_account(commitment.clone())?; } @@ -81,7 +109,7 @@ impl VM { /// 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> { + fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> { for machine in &mut self.machines { machine.commit_blockhash(number, hash)?; } @@ -89,7 +117,7 @@ impl VM { } /// Returns the current status of the VM. - pub fn status(&self) -> VMStatus { + fn status(&self) -> VMStatus { match self.machines[0].status() { MachineStatus::Running | MachineStatus::InvokeCreate(_) | MachineStatus::InvokeCall(_, _) => VMStatus::Running, MachineStatus::ExitedOk => VMStatus::ExitedOk, @@ -101,7 +129,7 @@ impl VM { /// 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> { + fn step(&mut self) -> Result<(), RequireError> { match self.machines.last().unwrap().status().clone() { MachineStatus::Running => { self.machines.last_mut().unwrap().step() @@ -127,7 +155,7 @@ impl VM { /// 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()?, @@ -138,38 +166,33 @@ impl VM { /// Returns the changed or committed accounts information up to /// current execution status. - pub fn accounts(&self) -> hash_map::Values { + fn accounts(&self) -> hash_map::Values { self.machines[0].state().account_state.accounts() } /// Returns the out value, if any. - pub fn out(&self) -> &[u8] { + fn out(&self) -> &[u8] { self.machines[0].state().out.as_slice() } /// Returns the available gas of this VM. - pub fn available_gas(&self) -> Gas { + fn available_gas(&self) -> Gas { self.machines[0].state().available_gas() } /// Returns the used gas of this VM. - pub fn used_gas(&self) -> Gas { + fn used_gas(&self) -> Gas { self.machines[0].state().used_gas } /// Returns the refunded gas of this VM. - pub fn refunded_gas(&self) -> 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] { + fn logs(&self) -> &[Log] { self.machines[0].state().logs.as_slice() } - - /// Returns the call create history. Only used in testing. - pub fn history(&self) -> &[Context] { - self.history.as_slice() - } }