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 index b764251c7..edc1cddf7 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,82 +1,12 @@ -use util::uint::*; -use util::hash::*; +use std::collections::hash_map::*; use util::bytes::*; use util::semantic_version::*; -use std::collections::hash_map::*; use util::error::*; use header::Header; -use account::Account; use transaction::Transaction; - -/// 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 { - pub engine_name: String, - - 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, - - // 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, - // Only pre-populate if known equivalent to genesis_state's root. If they're different Bad Things Will Happen, - pub state_root: Option, - pub seal_fields: usize, - pub seal_rlp: Bytes, -} +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. @@ -95,12 +25,10 @@ pub trait Engine { fn extra_info(&self, _header: &Header) -> HashMap { HashMap::new() } /// 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); + fn spec(&self) -> &Spec; /// Get the EVM schedule for - fn evm_schedule(&self) -> &EvmSchedule { &self.params().evm_schedule } + 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 @@ -113,32 +41,11 @@ pub trait Engine { /// 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]); +*/ } - -/// 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; } -} - -impl Params { - /// Convert this object into a boxed Engine of the right underlying type. - pub fn to_engine(self) -> Box { Box::new(NullEngine{params: self}) } - - /// Determine the state root for the - pub fn calculate_state_root(&self) -> H256 { - // TODO: use the trie_root to calculate. - unimplemented!(); - } - - pub fn genesis_block(&self) -> Bytes { - // TODO - unimplemented!(); - } -} - 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/lib.rs b/src/lib.rs index 071c3bf06..aa4f0b335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,9 +91,8 @@ pub mod account; pub mod header; pub mod transaction; pub mod receipt; -pub mod networkparams; pub mod denominations; - -#[test] -fn it_works() { -} +pub mod null_engine; +pub mod evm_schedule; +pub mod builtin; +pub mod spec; 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 index e860970ac..f3f2d25e9 100644 --- a/src/receipt.rs +++ b/src/receipt.rs @@ -1,5 +1,6 @@ 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), + } + } +} +