diff --git a/src/account.rs b/src/account.rs index 037e21965..7b5bd1908 100644 --- a/src/account.rs +++ b/src/account.rs @@ -6,10 +6,12 @@ use util::bytes::*; use util::trie::*; use util::rlp::*; use util::uint::*; +use std::cell::*; pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] ); /// Single account in the system. +#[derive(Debug)] pub struct Account { // Balance of the account. balance: U256, @@ -18,7 +20,7 @@ pub struct Account { // Trie-backed storage. storage_root: H256, // Overlay on trie-backed storage. - storage_overlay: HashMap, + storage_overlay: RefCell>, // Code hash of the account. If None, means that it's a contract whose code has not yet been set. code_hash: Option, // Code cache of the account. @@ -32,19 +34,19 @@ impl Account { balance: balance, nonce: nonce, storage_root: SHA3_NULL_RLP, - storage_overlay: storage, + storage_overlay: RefCell::new(storage), code_hash: Some(code.sha3()), code_cache: code } } /// 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: HashMap::new(), + storage_overlay: RefCell::new(HashMap::new()), code_hash: Some(SHA3_EMPTY), code_cache: vec![], } @@ -57,7 +59,7 @@ impl Account { nonce: r.val_at(0), balance: r.val_at(1), storage_root: r.val_at(2), - storage_overlay: HashMap::new(), + storage_overlay: RefCell::new(HashMap::new()), code_hash: Some(r.val_at(3)), code_cache: vec![], } @@ -70,7 +72,7 @@ impl Account { balance: balance, nonce: U256::from(0u8), storage_root: SHA3_NULL_RLP, - storage_overlay: HashMap::new(), + storage_overlay: RefCell::new(HashMap::new()), code_hash: None, code_cache: vec![], } @@ -85,20 +87,14 @@ impl Account { /// Set (and cache) the contents of the trie's storage at `key` to `value`. pub fn set_storage(&mut self, key: H256, value: H256) { - self.storage_overlay.insert(key, value); + self.storage_overlay.borrow_mut().insert(key, value); } /// Get (and cache) the contents of the trie's storage at `key`. - pub fn storage_at(&mut self, db: &mut HashDB, key: H256) -> H256 { - match self.storage_overlay.get(&key) { - Some(x) => { return x.clone() }, - _ => {} - } - // fetch - cannot be done in match because of the borrow rules. - let t = TrieDB::new_existing(db, &mut self.storage_root); - let r = H256::from_slice(t.at(key.bytes()).unwrap_or(&[0u8;32][..])); - self.storage_overlay.insert(key, r.clone()); - r + pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 { + self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{ + H256::from_slice(TrieDB::new(db, &self.storage_root).get(key.bytes()).unwrap_or(&[0u8;32][..])) + }).clone() } /// return the balance associated with this account. @@ -118,6 +114,7 @@ impl Account { match self.code_hash { Some(SHA3_EMPTY) | None if self.code_cache.is_empty() => Some(&self.code_cache), Some(_) if !self.code_cache.is_empty() => Some(&self.code_cache), + None => Some(&self.code_cache), _ => None, } } @@ -140,7 +137,7 @@ impl Account { } /// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code. - pub fn ensure_cached(&mut self, db: &HashDB) -> bool { + pub fn cache_code(&mut self, db: &HashDB) -> bool { // TODO: fill out self.code_cache; /* return !self.is_cached() || match db.lookup(&self.code_hash.unwrap()) { // why doesn't this work? unwrap causes move?! @@ -160,10 +157,10 @@ impl Account { pub fn base_root(&self) -> &H256 { &self.storage_root } /// return the storage root associated with this account or None if it has been altered via the overlay. - pub fn storage_root(&self) -> Option<&H256> { if self.storage_overlay.is_empty() {Some(&self.storage_root)} else {None} } + pub fn storage_root(&self) -> Option<&H256> { if self.storage_overlay.borrow().is_empty() {Some(&self.storage_root)} else {None} } /// rturn the storage overlay. - pub fn storage_overlay(&self) -> &HashMap { &self.storage_overlay } + pub fn storage_overlay(&self) -> Ref> { self.storage_overlay.borrow() } /// Increment the nonce of the account by one. pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); } @@ -176,20 +173,24 @@ impl Account { /// Commit the `storage_overlay` to the backing DB and update `storage_root`. pub fn commit_storage(&mut self, db: &mut HashDB) { - let mut t = TrieDB::new(db, &mut self.storage_root); - for (k, v) in self.storage_overlay.iter() { + let mut t = TrieDBMut::new(db, &mut self.storage_root); + for (k, v) in self.storage_overlay.borrow().iter() { // cast key and value to trait type, // so we can call overloaded `to_bytes` method t.insert(k, v); } - self.storage_overlay.clear(); + self.storage_overlay.borrow_mut().clear(); } /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. pub fn commit_code(&mut self, db: &mut HashDB) { + println!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty()); match (self.code_hash.is_none(), self.code_cache.is_empty()) { - (true, true) => self.code_hash = Some(self.code_cache.sha3()), - (true, false) => self.code_hash = Some(db.insert(&self.code_cache)), + (true, true) => self.code_hash = Some(SHA3_EMPTY), + (true, false) => { + println!("Writing into DB {:?}", self.code_cache); + self.code_hash = Some(db.insert(&self.code_cache)); + }, (false, _) => {}, } } @@ -212,7 +213,6 @@ use super::*; use std::collections::HashMap; use util::hash::*; use util::bytes::*; -use util::trie::*; use util::rlp::*; use util::uint::*; use util::overlaydb::*; @@ -229,10 +229,10 @@ fn storage_at() { a.rlp() }; - let mut a = Account::from_rlp(&rlp); + let a = Account::from_rlp(&rlp); assert_eq!(a.storage_root().unwrap().hex(), "3541f181d6dad5c504371884684d08c29a8bad04926f8ceddf5e279dbc3cc769"); - assert_eq!(a.storage_at(&mut db, H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64))); - assert_eq!(a.storage_at(&mut db, H256::from(&U256::from(0x01u64))), H256::new()); + assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64))); + assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x01u64))), H256::new()); } #[test] @@ -247,7 +247,7 @@ fn note_code() { }; let mut a = Account::from_rlp(&rlp); - assert_eq!(a.ensure_cached(&db), true); + assert_eq!(a.cache_code(&db), true); let mut a = Account::from_rlp(&rlp); assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); diff --git a/src/blockchain.rs b/src/blockchain.rs index 7669cbdbb..d92cfbb8b 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -13,7 +13,7 @@ use util::hashdb::*; use util::sha3::*; use util::bytes::*; use util::squeeze::*; -use blockheader::*; +use header::*; use extras::*; use transaction::*; use views::*; diff --git a/src/builtin.rs b/src/builtin.rs new file mode 100644 index 000000000..12ed610a6 --- /dev/null +++ b/src/builtin.rs @@ -0,0 +1,10 @@ +use util::uint::*; + +/// 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, +} diff --git a/src/engine.rs b/src/engine.rs new file mode 100644 index 000000000..edc1cddf7 --- /dev/null +++ b/src/engine.rs @@ -0,0 +1,51 @@ +use std::collections::hash_map::*; +use util::bytes::*; +use util::semantic_version::*; +use util::error::*; +use header::Header; +use transaction::Transaction; +use spec::Spec; +use evm_schedule::EvmSchedule; +use env_info::EnvInfo; + +/// 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() } + + /// Get the general parameters of the chain. + fn spec(&self) -> &Spec; + + /// Get the EVM schedule for + fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule; + + /// 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(()) } + + /// Additional verification for transactions in blocks. + // TODO: Add flags for which bits of the transaction to check. + fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), EthcoreError> { Ok(()) } + + /// Don't forget to call Super::populateFromParent when subclassing & overriding. + fn populate_from_parent(&self, _header: &mut Header, _parent: &Header) -> Result<(), EthcoreError> { Ok(()) } + + // TODO: buildin contract routing - this will require removing the built-in configuration reading logic from Spec + // into here and removing the Spec::builtins field. It's a big job. +/* fn is_builtin(&self, a: Address) -> bool; + fn cost_of_builtin(&self, a: Address, in: &[u8]) -> bignum; + fn execute_builtin(&self, a: Address, in: &[u8], out: &mut [u8]); +*/ +} 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/evm_schedule.rs b/src/evm_schedule.rs new file mode 100644 index 000000000..dbe2fc670 --- /dev/null +++ b/src/evm_schedule.rs @@ -0,0 +1,80 @@ +/// 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: usize, + pub tier_step_gas: [usize; 8], + pub exp_gas: usize, + pub exp_byte_gas: usize, + pub sha3_gas: usize, + pub sha3_word_gas: usize, + pub sload_gas: usize, + pub sstore_set_gas: usize, + pub sstore_reset_gas: usize, + pub sstore_refund_gas: usize, + pub jumpdest_gas: usize, + pub log_gas: usize, + pub log_data_gas: usize, + pub log_topic_gas: usize, + pub create_gas: usize, + pub call_gas: usize, + pub call_stipend: usize, + pub call_value_transfer_gas: usize, + pub call_new_account_gas: usize, + pub suicide_refund_gas: usize, + pub memory_gas: usize, + pub quad_coeff_div: usize, + pub create_data_gas: usize, + pub tx_gas: usize, + pub tx_create_gas: usize, + pub tx_data_zero_gas: usize, + pub tx_data_non_zero_gas: usize, + pub copy_gas: usize, +} + +impl EvmSchedule { + /// Schedule for the Frontier-era of the Ethereum main net. + pub fn new_frontier() -> EvmSchedule { + Self::new(false, false, 21000) + } + + /// Schedule for the Homestead-era of the Ethereum main net. + pub fn new_homestead() -> EvmSchedule { + Self::new(true, true, 53000) + } + + fn new(efcd: bool, hdc: bool, tcg: usize) -> EvmSchedule { + EvmSchedule{ + exceptional_failed_code_deposit: efcd, + have_delegate_call: hdc, + stack_limit: 1024, + tier_step_gas: [0usize, 2, 3, 4, 5, 6, 10, 20], + exp_gas: 10, + exp_byte_gas: 10, + sha3_gas: 30, + sha3_word_gas: 6, + sload_gas: 50, + sstore_set_gas: 20000, + sstore_reset_gas: 5000, + sstore_refund_gas: 15000, + jumpdest_gas: 1, + log_gas: 375, + log_data_gas: 8, + log_topic_gas: 375, + create_gas: 32000, + call_gas: 40, + call_stipend: 2300, + call_value_transfer_gas: 9000, + call_new_account_gas: 25000, + suicide_refund_gas: 24000, + memory_gas: 3, + quad_coeff_div: 512, + create_data_gas: 200, + tx_gas: 21000, + tx_create_gas: tcg, + tx_data_zero_gas: 4, + tx_data_non_zero_gas: 68, + copy_gas: 3, + } + } +} \ No newline at end of file diff --git a/src/genesis.rs b/src/genesis.rs index 547398f01..a69683cdf 100644 --- a/src/genesis.rs +++ b/src/genesis.rs @@ -10,7 +10,7 @@ use util::hash::*; use util::uint::*; use util::sha3::*; use account::*; -use blockheader::*; +use header::*; /// Converts file from base64 gzipped bytes to json fn base_to_json(source: &[u8]) -> Json { @@ -88,7 +88,7 @@ impl Genesis { let addr = Address::from_str(address).unwrap(); let o = acc.as_object().unwrap(); let balance = U256::from_dec_str(o["balance"].as_string().unwrap()).unwrap(); - state.insert(addr, Account::new_basic(balance)); + state.insert(addr, Account::new_basic(balance, U256::from(0))); } (header, state) 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 7facaa21a..41cdc9515 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(cell_extras)] + //! Ethcore's ethereum implementation //! //! ### Rust version @@ -86,19 +88,19 @@ pub use util::hash::*; pub use util::uint::*; pub use util::bytes::*; -pub mod account; -pub mod blockchain; +pub mod env_info; +pub mod engine; pub mod state; -pub mod blockheader; +pub mod account; +pub mod header; pub mod transaction; -pub mod extras; +pub mod receipt; +pub mod denominations; +pub mod null_engine; +pub mod evm_schedule; +pub mod builtin; +pub mod spec; pub mod genesis; pub mod views; - -pub mod networkparams; -pub mod denominations; - - -#[test] -fn it_works() { -} +pub mod blockchain; +pub mod extras; diff --git a/src/networkparams.rs b/src/networkparams.rs deleted file mode 100644 index aedadf701..000000000 --- a/src/networkparams.rs +++ /dev/null @@ -1,71 +0,0 @@ -use util::uint::*; -use denominations::*; - -/// Network related const params -/// TODO: make it configurable from json file -pub struct NetworkParams { - maximum_extra_data_size: U256, - min_gas_limit: U256, - gas_limit_bounds_divisor: U256, - minimum_difficulty: U256, - difficulty_bound_divisor: U256, - duration_limit: U256, - block_reward: U256, - gas_floor_target: U256, - account_start_nonce: U256 -} - -impl NetworkParams { - pub fn olympic() -> NetworkParams { - NetworkParams { - maximum_extra_data_size: U256::from(1024u64), - min_gas_limit: U256::from(125_000u64), - gas_floor_target: U256::from(3_141_592u64), - gas_limit_bounds_divisor: U256::from(1024u64), - minimum_difficulty: U256::from(131_072u64), - difficulty_bound_divisor: U256::from(2048u64), - duration_limit: U256::from(8u64), - block_reward: finney() * U256::from(1500u64), - account_start_nonce: U256::from(0u64) - } - } - - pub fn frontier() -> NetworkParams { - NetworkParams { - maximum_extra_data_size: U256::from(32u64), - min_gas_limit: U256::from(5000u64), - gas_floor_target: U256::from(3_141_592u64), - gas_limit_bounds_divisor: U256::from(1024u64), - minimum_difficulty: U256::from(131_072u64), - difficulty_bound_divisor: U256::from(2048u64), - duration_limit: U256::from(13u64), - block_reward: ether() * U256::from(5u64), - account_start_nonce: U256::from(0u64) - } - } - - pub fn morden() -> NetworkParams { - NetworkParams { - maximum_extra_data_size: U256::from(32u64), - min_gas_limit: U256::from(5000u64), - gas_floor_target: U256::from(3_141_592u64), - gas_limit_bounds_divisor: U256::from(1024u64), - minimum_difficulty: U256::from(131_072u64), - difficulty_bound_divisor: U256::from(2048u64), - duration_limit: U256::from(13u64), - block_reward: ether() * U256::from(5u64), - account_start_nonce: U256::from(1u64) << 20 - } - } - - pub fn maximum_extra_data_size(&self) -> U256 { self.maximum_extra_data_size } - pub fn min_gas_limit(&self) -> U256 { self.min_gas_limit } - pub fn gas_limit_bounds_divisor(&self) -> U256 { self.gas_limit_bounds_divisor } - pub fn minimum_difficulty(&self) -> U256 { self.minimum_difficulty } - pub fn difficulty_bound_divisor(&self) -> U256 { self.difficulty_bound_divisor } - pub fn duration_limit(&self) -> U256 { self.duration_limit } - pub fn block_reward(&self) -> U256 { self.block_reward } - pub fn gas_floor_target(&self) -> U256 { self.gas_floor_target } - pub fn account_start_nonce(&self) -> U256 { self.account_start_nonce } -} - diff --git a/src/null_engine.rs b/src/null_engine.rs new file mode 100644 index 000000000..da6334839 --- /dev/null +++ b/src/null_engine.rs @@ -0,0 +1,21 @@ +use engine::Engine; +use spec::Spec; +use evm_schedule::EvmSchedule; +use env_info::EnvInfo; + +/// An engine which does not provide any consensus mechanism. +pub struct NullEngine { + spec: Spec, +} + +impl NullEngine { + pub fn new_boxed(spec: Spec) -> Box { + Box::new(NullEngine{spec: spec}) + } +} + +impl Engine for NullEngine { + fn name(&self) -> &str { "NullEngine" } + fn spec(&self) -> &Spec { &self.spec } + fn evm_schedule(&self, _env_info: &EnvInfo) -> EvmSchedule { EvmSchedule::new_frontier() } +} diff --git a/src/receipt.rs b/src/receipt.rs new file mode 100644 index 000000000..f3f2d25e9 --- /dev/null +++ b/src/receipt.rs @@ -0,0 +1,7 @@ +use util::hash::*; + +/// Information describing execution of a transaction. +pub struct Receipt { + // TODO + pub state_root: H256, +} diff --git a/src/spec.rs b/src/spec.rs new file mode 100644 index 000000000..2b0eb9496 --- /dev/null +++ b/src/spec.rs @@ -0,0 +1,189 @@ +use std::collections::hash_map::*; +use std::cell::*; +use std::str::FromStr; +use util::uint::*; +use util::hash::*; +use util::bytes::*; +use util::triehash::*; +use util::error::*; +use util::rlp::*; +use account::Account; +use engine::Engine; +use builtin::Builtin; +use null_engine::NullEngine; +use denominations::*; + +/// 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 Spec { + // What engine are we using for this? + pub engine_name: String, + + // Various parameters for the chain operation. + pub block_reward: U256, + pub maximum_extra_data_size: U256, + pub account_start_nonce: U256, + pub misc: HashMap, + + // Builtin-contracts are here for now but would like to abstract into Engine API eventually. + pub builtins: HashMap, + + // Genesis params. + pub parent_hash: H256, + pub author: Address, + pub difficulty: U256, + pub gas_limit: U256, + pub gas_used: U256, + pub timestamp: U256, + pub extra_data: Bytes, + pub genesis_state: HashMap, + pub seal_fields: usize, + pub seal_rlp: Bytes, + + // May be prepopulated if we know this in advance. + state_root_memo: RefCell>, +} + +impl Spec { + /// Convert this object into a boxed Engine of the right underlying type. + // TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. + pub fn to_engine(self) -> Result, EthcoreError> { + match self.engine_name.as_ref() { + "NullEngine" => Ok(NullEngine::new_boxed(self)), + _ => Err(EthcoreError::UnknownName) + } + } + + /// Return the state root for the genesis state, memoising accordingly. + pub fn state_root(&self) -> Ref { + if self.state_root_memo.borrow().is_none() { + *self.state_root_memo.borrow_mut() = Some(trie_root(self.genesis_state.iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect())); + } + Ref::map(self.state_root_memo.borrow(), |x|x.as_ref().unwrap()) + } + + pub fn block_reward(&self) -> U256 { self.block_reward } + pub fn maximum_extra_data_size(&self) -> U256 { self.maximum_extra_data_size } + pub fn account_start_nonce(&self) -> U256 { self.account_start_nonce } + + /// Compose the genesis block for this chain. + pub fn genesis_block(&self) -> Bytes { + // TODO + unimplemented!(); + } +} + + +impl Spec { + pub fn olympic() -> Spec { + Spec { + engine_name: "Ethash".to_string(), + block_reward: finney() * U256::from(1500u64), + maximum_extra_data_size: U256::from(1024u64), + account_start_nonce: U256::from(0u64), + misc: vec![ + ("gas_limit_bounds_divisor", encode(&1024u64)), + ("minimum_difficulty", encode(&131_072u64)), + ("difficulty_bound_divisor", encode(&2048u64)), + ("duration_limit", encode(&8u64)), + ("min_gas_limit", encode(&125_000u64)), + ("gas_floor_target", encode(&3_141_592u64)), + ].into_iter().fold(HashMap::new(), | mut acc, vec | { + acc.insert(vec.0.to_string(), vec.1); + acc + }), + builtins: HashMap::new(), // TODO: make correct + parent_hash: H256::new(), + author: Address::new(), + difficulty: U256::from(131_072u64), + gas_limit: U256::from(0u64), + gas_used: U256::from(0u64), + timestamp: U256::from(0u64), + extra_data: vec![], + genesis_state: vec![ // TODO: make correct + (Address::new(), Account::new_basic(U256::from(1) << 200, U256::from(0))) + ].into_iter().fold(HashMap::new(), | mut acc, vec | { + acc.insert(vec.0, vec.1); + acc + }), + seal_fields: 2, + seal_rlp: { let mut r = RlpStream::new_list(2); r.append(&0x2au64); r.append(&H256::new()); r.out() }, // TODO: make correct + state_root_memo: RefCell::new(None), + } + } + + pub fn frontier() -> Spec { + Spec { + engine_name: "Ethash".to_string(), + block_reward: ether() * U256::from(5u64), + maximum_extra_data_size: U256::from(32u64), + account_start_nonce: U256::from(0u64), + misc: vec![ + ("gas_limit_bounds_divisor", encode(&1024u64)), + ("minimum_difficulty", encode(&131_072u64)), + ("difficulty_bound_divisor", encode(&2048u64)), + ("duration_limit", encode(&13u64)), + ("min_gas_limit", encode(&5000u64)), + ("gas_floor_target", encode(&3_141_592u64)), + ].into_iter().fold(HashMap::new(), | mut acc, vec | { + acc.insert(vec.0.to_string(), vec.1); + acc + }), + builtins: HashMap::new(), // TODO: make correct + parent_hash: H256::new(), + author: Address::new(), + difficulty: U256::from(131_072u64), + gas_limit: U256::from(0u64), + gas_used: U256::from(0u64), + timestamp: U256::from(0u64), + extra_data: vec![], + genesis_state: vec![ // TODO: make correct + (Address::new(), Account::new_basic(U256::from(1) << 200, U256::from(0))) + ].into_iter().fold(HashMap::new(), | mut acc, vec | { + acc.insert(vec.0, vec.1); + acc + }), + seal_fields: 2, + seal_rlp: { let mut r = RlpStream::new_list(2); r.append(&0x42u64); r.append(&H256::new()); r.out() }, + state_root_memo: RefCell::new(None), + } + } + + pub fn morden() -> Spec { + Spec { + engine_name: "Ethash".to_string(), + block_reward: ether() * U256::from(5u64), + maximum_extra_data_size: U256::from(32u64), + account_start_nonce: U256::from(1u64) << 20, + misc: vec![ + ("gas_limit_bounds_divisor", encode(&1024u64)), + ("minimum_difficulty", encode(&131_072u64)), + ("difficulty_bound_divisor", encode(&2048u64)), + ("duration_limit", encode(&13u64)), + ("min_gas_limit", encode(&5000u64)), + ("gas_floor_target", encode(&3_141_592u64)), + ].into_iter().fold(HashMap::new(), | mut acc, vec | { + acc.insert(vec.0.to_string(), vec.1); + acc + }), + builtins: HashMap::new(), // TODO: make correct + parent_hash: H256::new(), + author: Address::new(), + difficulty: U256::from(131_072u64), + gas_limit: U256::from(0u64), + gas_used: U256::from(0u64), + timestamp: U256::from(0u64), + extra_data: vec![], + genesis_state: vec![ // TODO: make correct + (Address::new(), Account::new_basic(U256::from(1) << 200, U256::from(0))) + ].into_iter().fold(HashMap::new(), | mut acc, vec | { + acc.insert(vec.0, vec.1); + acc + }), + seal_fields: 2, + seal_rlp: { let mut r = RlpStream::new_list(2); r.append(&0x00006d6f7264656eu64); r.append(&H256::from_str("00000000000000000000000000000000000000647572616c65787365646c6578").unwrap()); r.out() }, // TODO: make correct + state_root_memo: RefCell::new(None), + } + } +} + diff --git a/src/state.rs b/src/state.rs index 5674035bf..dbe349d77 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,19 +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 account::Account; +use transaction::Transaction; +use receipt::Receipt; +use env_info::EnvInfo; +use engine::Engine; + +/// 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 { @@ -22,14 +32,14 @@ impl State { let mut root = H256::new(); { // init trie and reset root too null - let _ = TrieDB::new(&mut db, &mut root); + let _ = TrieDBMut::new(&mut db, &mut root); } 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, } } @@ -37,14 +47,14 @@ impl State { pub fn new_existing(mut db: OverlayDB, mut root: H256, account_start_nonce: U256) -> State { { // trie should panic! if root does not exist - let _ = TrieDB::new_existing(&mut db, &mut root); + let _ = TrieDB::new(&mut db, &mut root); } 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, } } @@ -53,6 +63,11 @@ impl State { Self::new(OverlayDB::new_temp(), U256::from(0u8)) } + /// 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 @@ -63,7 +78,69 @@ impl State { &mut self.db } - /// Commit accounts to TrieDB. This is similar to cpp-ethereum's dev::eth::commit. + /// Get the balance of account `a`. + 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) + } + + /// Subtract `decr` from the balance of account `a`. + pub fn sub_balance(&mut self, a: &Address, decr: &U256) { + self.require(a, false).sub_balance(decr) + } + + /// 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. + pub fn inc_nonce(&mut self, a: &Address) { + self.require(a, false).inc_nonce() + } + + /// 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 { // first, commit the sub trees. @@ -79,7 +156,7 @@ impl State { } { - let mut trie = TrieDB::new_existing(db, &mut root); + let mut trie = TrieDBMut::new_existing(db, &mut root); for (address, ref a) in accounts.iter() { match a { &&Some(ref account) => trie.insert(address, &account.rlp()), @@ -93,28 +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. + 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.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 ensure_cached(&mut self, a: &Address, require_code: bool, force_create: bool) { - if self.cache.get(a).is_none() { - // load from trie. - self.cache.insert(a.clone(), TrieDB::new(&mut self.db, &mut self.root).at(&a).map(|rlp| Account::from_rlp(rlp))); + 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>(&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() { - if !force_create { return; } - // create a new account - self.cache.insert(a.clone(), Some(Account::new_basic(U256::from(0u8)))); - } - - if require_code { - if let &mut Some(ref mut account) = self.cache.get_mut(a).unwrap() { - account.ensure_cached(&self.db); + 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(&self.db); } - } + account + }).unwrap()) } } @@ -125,17 +223,111 @@ use super::*; use util::hash::*; use util::trie::*; use util::rlp::*; +use util::uint::*; use std::str::FromStr; +use account::*; #[test] -fn playpen() { +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))); + 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].to_vec())); + s.drop() + }; + + let s = State::new_existing(db, r, U256::from(0u8)); + assert_eq!(s.code(&a), Some([1u8, 2, 3].to_vec())); +} + +#[test] +fn storage_at_from_database() { + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let (r, db) = { + let mut s = State::new_temp(); + s.set_storage(&a, H256::from(&U256::from(01u64)), H256::from(&U256::from(69u64))); + s.commit(); + s.drop() + }; + + 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))); +} + +#[test] +fn get_from_database() { + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let (r, db) = { + let mut s = State::new_temp(); + s.inc_nonce(&a); + s.add_balance(&a, &U256::from(69u64)); + s.commit(); + assert_eq!(s.balance(&a), U256::from(69u64)); + s.drop() + }; + + 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)); +} + +#[test] +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(); + assert_eq!(s.balance(&a), U256::from(69u64)); + s.sub_balance(&a, &U256::from(42u64)); + 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] +fn alter_nonce() { + let mut s = State::new_temp(); + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + s.inc_nonce(&a); + assert_eq!(s.nonce(&a), U256::from(1u64)); + s.inc_nonce(&a); + assert_eq!(s.nonce(&a), U256::from(2u64)); + s.commit(); + assert_eq!(s.nonce(&a), U256::from(2u64)); + s.inc_nonce(&a); + assert_eq!(s.nonce(&a), U256::from(3u64)); + s.commit(); + assert_eq!(s.nonce(&a), U256::from(3u64)); +} + +#[test] +fn balance_nonce() { + let mut s = State::new_temp(); + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + assert_eq!(s.balance(&a), U256::from(0u64)); + assert_eq!(s.nonce(&a), U256::from(0u64)); + s.commit(); + assert_eq!(s.balance(&a), U256::from(0u64)); + assert_eq!(s.nonce(&a), U256::from(0u64)); } #[test] fn ensure_cached() { let mut s = State::new_temp(); let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); - s.ensure_cached(&a, false, true); + s.require(&a, false); s.commit(); assert_eq!(s.root().hex(), "ec68b85fa2e0526dc0e821a5b33135459114f19173ce0479f5c09b21cc25b9a4"); } 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])), }; diff --git a/src/views.rs b/src/views.rs index 562f6c690..e8425cc17 100644 --- a/src/views.rs +++ b/src/views.rs @@ -4,7 +4,7 @@ use util::hash::*; use util::uint::*; use util::rlp::*; use util::sha3::*; -use blockheader::*; +use header::*; use transaction::*; /// View onto block rlp.