diff --git a/src/account.rs b/src/account.rs index 4641c5bf1..7b5bd1908 100644 --- a/src/account.rs +++ b/src/account.rs @@ -41,10 +41,10 @@ impl Account { } /// Create a new account with the given balance. - pub fn new_basic(balance: U256) -> Account { + pub fn new_basic(balance: U256, nonce: U256) -> Account { Account { balance: balance, - nonce: U256::from(0u8), + nonce: nonce, storage_root: SHA3_NULL_RLP, storage_overlay: RefCell::new(HashMap::new()), code_hash: Some(SHA3_EMPTY), diff --git a/src/engine.rs b/src/engine.rs new file mode 100644 index 000000000..509c0d6db --- /dev/null +++ b/src/engine.rs @@ -0,0 +1,109 @@ +use util::uint::*; +use util::hash::*; +use util::bytes::*; +use util::semantic_version::*; +use header::Header; +use std::collections::hash_map::*; +use util::error::*; + +/// Definition of the cost schedule and other parameterisations for the EVM. +pub struct EvmSchedule { + pub exceptional_failed_code_deposit: bool, + pub have_delegate_call: bool, + pub stack_limit: U256, + pub tier_step_gas: [U256; 8], + pub exp_gas: U256, + pub exp_byte_gas: U256, + pub sha3_gas: U256, + pub sha3_word_gas: U256, + pub sload_gas: U256, + pub sstore_set_gas: U256, + pub sstore_reset_gas: U256, + pub sstore_refund_gas: U256, + pub jumpdest_gas: U256, + pub log_gas: U256, + pub log_data_gas: U256, + pub log_topic_gas: U256, + pub create_gas: U256, + pub call_gas: U256, + pub call_stipend: U256, + pub call_value_transfer_gas: U256, + pub call_new_account_gas: U256, + pub suicide_refund_gas: U256, + pub memory_gas: U256, + pub quad_coeff_div: U256, + pub create_data_gas: U256, + pub tx_gas: U256, + pub tx_create_gas: U256, + pub tx_data_zero_gas: U256, + pub tx_data_non_zero_gas: U256, + pub copy_gas: U256, +} + +/// Definition of a contract whose implementation is built-in. +pub struct Builtin { + /// The gas cost of running this built-in for the given size of input data. + pub cost: Box U256>, // TODO: U256 should be bignum. + /// Run this built-in function with the input being the first argument and the output + /// being placed into the second. + pub execute: Box, +} + +/// Parameters for a block chain; includes both those intrinsic to the design of the +/// chain and those to be interpreted by the active chain engine. +pub struct Params { + /* + TODO: std::unordered_map precompiled; + */ + pub block_reward: U256, + pub maximum_extra_data_size: U256, + pub account_start_nonce: U256, + pub evm_schedule: EvmSchedule, + pub builtins: HashMap, + pub misc: HashMap, +} + +/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. +/// Provides hooks into each of the major parts of block import. +pub trait Engine { + /// The name of this engine. + fn name(&self) -> &str; + /// The version of this engine. Should be of the form + fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0 ,0) } + + /// The number of additional header fields required for this engine. + fn seal_fields(&self) -> u32 { 0 } + /// Default values of the additional fields RLP-encoded in a raw (non-list) harness. + fn seal_rlp(&self) -> Bytes { vec![] } + + /// Additional engine-specific information for the user/developer concerning `header`. + fn extra_info(&self, _header: &Header) -> HashMap { HashMap::new() } + + /// Verify that `header` is valid. + /// `parent` (the parent header) and `block` (the header's full block) may be provided for additional + /// checks. Returns either a null `Ok` or a general error detailing the problem with import. + fn verify(&self, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), EthcoreError> { Ok(()) } +/* + virtual void verify(Strictness _s, BlockHeader const& _bi, BlockHeader const& _parent = BlockHeader(), bytesConstRef _block = bytesConstRef()) const; + /// Additional verification for transactions in blocks. + virtual void verifyTransaction(ImportRequirements::value _ir, TransactionBase const& _t, BlockHeader const& _bi) const; + /// Don't forget to call Super::populateFromParent when subclassing & overriding. + virtual void populateFromParent(BlockHeader& _bi, BlockHeader const& _parent) const; +*/ + + /// Get the general parameters of the chain. + fn params(&self) -> &Params; + /// Set the general parameters of the chain. + fn set_params(&mut self, p: Params); +} + +/// An engine which does not provide any consensus mechanism. +pub struct NullEngine { + params: Params, +} + +impl Engine for NullEngine { + fn name(&self) -> &str { "NullEngine" } + fn params(&self) -> &Params { &self.params } + fn set_params(&mut self, params: Params) { self.params = params; } +} \ No newline at end of file diff --git a/src/env_info.rs b/src/env_info.rs new file mode 100644 index 000000000..dec8069ac --- /dev/null +++ b/src/env_info.rs @@ -0,0 +1,24 @@ +use util::uint::*; +use util::hash::*; + +/// Simple vector of hashes, should be at most 256 items large, can be smaller if being used +/// for a block whose number is less than 257. +pub type LastHashes = Vec; + +/// Information concerning the execution environment for a message-call/contract-creation. +pub struct EnvInfo { + /// The block number. + pub number: U256, + /// The block author. + pub author: Address, + /// The block timestamp. + pub timestamp: U256, + /// The block difficulty. + pub difficulty: U256, + /// The block gas limit. + pub gas_limit: U256, + /// The last 256 block hashes. + pub last_hashes: LastHashes, + /// The gas used. + pub gas_used: U256, +} \ No newline at end of file diff --git a/src/blockheader.rs b/src/header.rs similarity index 100% rename from src/blockheader.rs rename to src/header.rs diff --git a/src/lib.rs b/src/lib.rs index d7f354423..071c3bf06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(cell_extras)] + //! Ethcore's ethereum implementation //! //! ### Rust version @@ -82,10 +84,13 @@ pub use util::hash::*; pub use util::uint::*; pub use util::bytes::*; +pub mod env_info; +pub mod engine; pub mod state; pub mod account; -pub mod blockheader; +pub mod header; pub mod transaction; +pub mod receipt; pub mod networkparams; pub mod denominations; diff --git a/src/receipt.rs b/src/receipt.rs new file mode 100644 index 000000000..e860970ac --- /dev/null +++ b/src/receipt.rs @@ -0,0 +1,6 @@ +use util::hash::*; + +pub struct Receipt { + // TODO + pub state_root: H256, +} diff --git a/src/state.rs b/src/state.rs index 936a41baf..dbe349d77 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,42 +1,29 @@ +use std::cell::*; +use std::ops::*; use std::collections::HashMap; use util::hash::*; use util::hashdb::*; use util::overlaydb::*; use util::trie::*; +use util::bytes::*; use util::rlp::*; use util::uint::*; -//use std::cell::*; -//use std::ops::*; use account::Account; -/* -enum ValueOrRef<'self, 'db: 'self> { - Value(OverlayDB), - Ref(&'db mut OverlayDB) -} +use transaction::Transaction; +use receipt::Receipt; +use env_info::EnvInfo; +use engine::Engine; -impl<'self, 'db> ValueOrRef<'self, 'db: 'self> { - pub fn get_mut(&mut self) -> &mut OverlayDB { - match self { - Value(ref mut x) => x, - Ref(x) => x, - } - } - pub fn get(&self) -> &OverlayDB { - match self { - Value(ref x) => x, - Ref(x) => x, - } - } -} -*/ +/// Information concerning the result of the `State::apply` operation. +pub struct ApplyResult; // TODO /// Representation of the entire state of all accounts in the system. pub struct State { db: OverlayDB, root: H256, - cache: HashMap>, + cache: RefCell>>, - _account_start_nonce: U256, + account_start_nonce: U256, } impl State { @@ -51,8 +38,8 @@ impl State { State { db: db, root: root, - cache: HashMap::new(), - _account_start_nonce: account_start_nonce, + cache: RefCell::new(HashMap::new()), + account_start_nonce: account_start_nonce, } } @@ -66,8 +53,8 @@ impl State { State { db: db, root: root, - cache: HashMap::new(), - _account_start_nonce: account_start_nonce, + cache: RefCell::new(HashMap::new()), + account_start_nonce: account_start_nonce, } } @@ -76,27 +63,41 @@ impl State { Self::new(OverlayDB::new_temp(), U256::from(0u8)) } - /// Return reference to root - pub fn root(&self) -> &H256 { - &self.root - } - /// Destroy the current object and return root and database. pub fn drop(self) -> (H256, OverlayDB) { (self.root, self.db) } + /// Return reference to root + pub fn root(&self) -> &H256 { + &self.root + } + /// Expose the underlying database; good to use for calling `state.db().commit()`. pub fn db(&mut self) -> &mut OverlayDB { &mut self.db } /// Get the balance of account `a`. - // TODO: make immutable - pub fn balance(&mut self, a: &Address) -> U256 { + pub fn balance(&self, a: &Address) -> U256 { self.get(a, false).as_ref().map(|account| account.balance().clone()).unwrap_or(U256::from(0u8)) } + /// Get the nonce of account `a`. + pub fn nonce(&self, a: &Address) -> U256 { + self.get(a, false).as_ref().map(|account| account.nonce().clone()).unwrap_or(U256::from(0u8)) + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn storage_at(&self, a: &Address, key: &H256) -> H256 { + self.get(a, false).as_ref().map(|a|a.storage_at(&self.db, key)).unwrap_or(H256::new()) + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn code(&self, a: &Address) -> Option> { + self.get(a, true).as_ref().map(|a|a.code().map(|x|x.to_vec())).unwrap_or(None) + } + /// Add `incr` to the balance of account `a`. pub fn add_balance(&mut self, a: &Address, incr: &U256) { self.require(a, false).add_balance(incr) @@ -107,10 +108,10 @@ impl State { self.require(a, false).sub_balance(decr) } - /// Get the nonce of account `a`. - // TODO: make immutable - pub fn nonce(&mut self, a: &Address) -> U256 { - self.get(a, false).as_ref().map(|account| account.nonce().clone()).unwrap_or(U256::from(0u8)) + /// Subtracts `by` from the balance of `from` and adds it to that of `to`. + pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) { + self.sub_balance(from, by); + self.add_balance(to, by); } /// Increment the nonce of account `a` by 1. @@ -118,23 +119,27 @@ impl State { self.require(a, false).inc_nonce() } - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn storage_at(&mut self, a: &Address, key: &H256) -> H256 { - self.ensure_cached(a, false); - self.try_get(a).map(|a|a.storage_at(&self.db, key)).unwrap_or(H256::new()) - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn code(&mut self, a: &Address) -> Option<&[u8]> { - self.ensure_cached(a, true); - self.try_get(a).map(|a|a.code()).unwrap_or(None) - } - /// Mutate storage of account `a` so that it is `value` for `key`. pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) { self.require(a, false).set_storage(key, value); } + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn set_code(&mut self, a: &Address, code: Bytes) { + self.require_or_from(a, true, || Account::new_contract(U256::from(0u8))).set_code(code); + } + + /// Execute a given transaction. + /// This will change the state accordingly. + pub fn apply(_env_info: &EnvInfo, _engine: &Engine, _t: &Transaction, _is_permanent: bool) -> (ApplyResult, Receipt) { + unimplemented!(); + } + + /// Convert into a JSON representation. + pub fn as_json(&self) -> String { + unimplemented!(); + } + /// Commit accounts to TrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. /// `accounts` is mutable because we may need to commit the code or storage and record that. pub fn commit_into(db: &mut HashDB, mut root: H256, accounts: &mut HashMap>) -> H256 { @@ -165,69 +170,49 @@ impl State { /// Commits our cached account changes into the trie. pub fn commit(&mut self) { let r = self.root.clone(); // would prefer not to do this, really. - self.root = Self::commit_into(&mut self.db, r, &mut self.cache); + self.root = Self::commit_into(&mut self.db, r, self.cache.borrow_mut().deref_mut()); + } + + /// Populate the state from `accounts`. Just uses `commit_into`. + pub fn populate_from(&mut self, _accounts: &mut HashMap>) { + unimplemented!(); } /// Pull account `a` in our cache from the trie DB and return it. /// `require_code` requires that the code be cached, too. - // TODO: make immutable through returning an Option> - fn get(&mut self, a: &Address, require_code: bool) -> Option<&Account> { - self.ensure_cached(a, require_code); - self.try_get(a) - } - - /// Return account `a` from our cache, or None if it doesn't exist in the cache or - /// the account is empty. - /// Call `ensure_cached` before if you want to avoid the "it doesn't exist in the cache" - /// possibility. - fn try_get(&self, a: &Address) -> Option<&Account> { - self.cache.get(a).map(|x| x.as_ref()).unwrap_or(None) - } - - /// Ensure account `a` exists in our cache. - /// `require_code` requires that the code be cached, too. - fn ensure_cached(&mut self, a: &Address, require_code: bool) { - if self.cache.get(a).is_none() { - // load from trie. - let act = TrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp)); - println!("Loaded {:?} from trie: {:?}", a, act); - self.cache.insert(a.clone(), act); - } - let db = &self.db; + fn get(&self, a: &Address, require_code: bool) -> Ref> { + self.cache.borrow_mut().entry(a.clone()).or_insert_with(|| + TrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); if require_code { - if let Some(ref mut account) = self.cache.get_mut(a).unwrap().as_mut() { - println!("Caching code"); - account.cache_code(db); - println!("Now: {:?}", account); + if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() { + account.cache_code(&self.db); } } + Ref::map(self.cache.borrow(), |m| m.get(a).unwrap()) } /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// `force_create` creates a new, empty basic account if there is not currently an active account. - fn require(&mut self, a: &Address, require_code: bool) -> &mut Account { - self.require_or_from(a, require_code, || Account::new_basic(U256::from(0u8))) + fn require(&self, a: &Address, require_code: bool) -> RefMut { + self.require_or_from(a, require_code, || Account::new_basic(U256::from(0u8), self.account_start_nonce)) } /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// `force_create` creates a new, empty basic account if there is not currently an active account. - fn require_or_from Account>(&mut self, a: &Address, require_code: bool, default: F) -> &mut Account { - if self.cache.get(a).is_none() { - // load from trie. - self.cache.insert(a.clone(), TrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); + fn require_or_from Account>(&self, a: &Address, require_code: bool, default: F) -> RefMut { + self.cache.borrow_mut().entry(a.clone()).or_insert_with(|| + TrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); + if self.cache.borrow().get(a).unwrap().is_none() { + self.cache.borrow_mut().insert(a.clone(), Some(default())); } - if self.cache.get(a).unwrap().is_none() { - self.cache.insert(a.clone(), Some(default())); - } - - let db = &self.db; - self.cache.get_mut(a).unwrap().as_mut().map(|account| { + let b = self.cache.borrow_mut(); + RefMut::map(b, |m| m.get_mut(a).unwrap().as_mut().map(|account| { if require_code { - account.cache_code(db); + account.cache_code(&self.db); } account - }).unwrap() + }).unwrap()) } } @@ -247,15 +232,16 @@ fn code_from_database() { let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); let (r, db) = { let mut s = State::new_temp(); - s.require_or_from(&a, false, ||Account::new_contract(U256::from(42u32))).set_code(vec![1, 2, 3]); - assert_eq!(s.code(&a), Some(&[1u8, 2, 3][..])); + s.require_or_from(&a, false, ||Account::new_contract(U256::from(42u32))); + s.set_code(&a, vec![1, 2, 3]); + assert_eq!(s.code(&a), Some([1u8, 2, 3].to_vec())); s.commit(); - assert_eq!(s.code(&a), Some(&[1u8, 2, 3][..])); + assert_eq!(s.code(&a), Some([1u8, 2, 3].to_vec())); s.drop() }; - let mut s = State::new_existing(db, r, U256::from(0u8)); - assert_eq!(s.code(&a), Some(&[1u8, 2, 3][..])); + let s = State::new_existing(db, r, U256::from(0u8)); + assert_eq!(s.code(&a), Some([1u8, 2, 3].to_vec())); } #[test] @@ -268,7 +254,7 @@ fn storage_at_from_database() { s.drop() }; - let mut s = State::new_existing(db, r, U256::from(0u8)); + let s = State::new_existing(db, r, U256::from(0u8)); assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64))); } @@ -284,7 +270,7 @@ fn get_from_database() { s.drop() }; - let mut s = State::new_existing(db, r, U256::from(0u8)); + let s = State::new_existing(db, r, U256::from(0u8)); assert_eq!(s.balance(&a), U256::from(69u64)); assert_eq!(s.nonce(&a), U256::from(1u64)); } @@ -293,6 +279,7 @@ fn get_from_database() { fn alter_balance() { let mut s = State::new_temp(); let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let b = Address::from_str("0000000000000000000000000000000000000001").unwrap(); s.add_balance(&a, &U256::from(69u64)); assert_eq!(s.balance(&a), U256::from(69u64)); s.commit(); @@ -301,6 +288,12 @@ fn alter_balance() { assert_eq!(s.balance(&a), U256::from(27u64)); s.commit(); assert_eq!(s.balance(&a), U256::from(27u64)); + s.transfer_balance(&a, &b, &U256::from(18u64)); + assert_eq!(s.balance(&a), U256::from(9u64)); + assert_eq!(s.balance(&b), U256::from(18u64)); + s.commit(); + assert_eq!(s.balance(&a), U256::from(9u64)); + assert_eq!(s.balance(&b), U256::from(18u64)); } #[test] diff --git a/src/transaction.rs b/src/transaction.rs index 4fbd9588b..82fa9ccb6 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -3,20 +3,23 @@ use util::bytes::*; use util::uint::*; use util::rlp::*; +/// A set of information describing an externally-originating message call +/// or contract creation operation. pub struct Transaction { nonce: U256, gas_price: U256, gas: U256, - receive_address: Option
, + to: Option
, value: U256, data: Bytes, } impl Transaction { + /// Is this transaction meant to create a contract? pub fn is_contract_creation(&self) -> bool { - self.receive_address.is_none() + self.to.is_none() } - + /// Is this transaction meant to send a message? pub fn is_message_call(&self) -> bool { !self.is_contract_creation() } @@ -28,7 +31,7 @@ impl Encodable for Transaction { self.nonce.encode(e); self.gas_price.encode(e); self.gas.encode(e); - self.receive_address.encode(e); + self.to.encode(e); self.value.encode(e); self.data.encode(e); }) @@ -36,14 +39,14 @@ impl Encodable for Transaction { } impl Decodable for Transaction { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(decoder: &D) -> Result where D: Decoder { let d = try!(decoder.as_list()); let transaction = Transaction { nonce: try!(Decodable::decode(&d[0])), gas_price: try!(Decodable::decode(&d[1])), gas: try!(Decodable::decode(&d[2])), - receive_address: try!(Decodable::decode(&d[3])), + to: try!(Decodable::decode(&d[3])), value: try!(Decodable::decode(&d[4])), data: try!(Decodable::decode(&d[5])), };