diff --git a/ b/ deleted file mode 100644 index e69de29bb..000000000 diff --git a/Cargo.toml b/Cargo.toml index 1c7085b03..bffab3ac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ rocksdb = "0.2" heapsize = "0.2.0" rust-crypto = "0.2.34" time = "0.1" - +#interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" } evmjit = { path = "rust-evmjit", optional = true } [features] diff --git a/src/blockchain.rs b/src/blockchain.rs index c102ba1b4..abd6db203 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -47,6 +47,55 @@ impl BestBlock { } } +/// Interface for querying blocks by hash and by number. +pub trait BlockProvider { + /// Returns true if the given block is known + /// (though not necessarily a part of the canon chain). + fn is_known(&self, hash: &H256) -> bool; + + /// Get raw block data + fn block(&self, hash: &H256) -> Option; + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option; + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option; + + /// Get the partial-header of a block. + fn block_header(&self, hash: &H256) -> Option
{ + self.block(hash).map(|bytes| BlockView::new(&bytes).header()) + } + + /// Get a list of uncles for a given block. + /// Returns None if block deos not exist. + fn uncles(&self, hash: &H256) -> Option> { + self.block(hash).map(|bytes| BlockView::new(&bytes).uncles()) + } + + /// Get a list of uncle hashes for a given block. + /// Returns None if block does not exist. + fn uncle_hashes(&self, hash: &H256) -> Option> { + self.block(hash).map(|bytes| BlockView::new(&bytes).uncle_hashes()) + } + + /// Get the number of given block's hash. + fn block_number(&self, hash: &H256) -> Option { + self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number()) + } + + /// Get a list of transactions for a given block. + /// Returns None if block deos not exist. + fn transactions(&self, hash: &H256) -> Option> { + self.block(hash).map(|bytes| BlockView::new(&bytes).transactions()) + } + + /// Returns reference to genesis hash. + fn genesis_hash(&self) -> H256 { + self.block_hash(0).expect("Genesis hash should always exist") + } +} + /// Structure providing fast access to blockchain data. /// /// **Does not do input data verification.** @@ -67,6 +116,48 @@ pub struct BlockChain { blocks_db: DB } +impl BlockProvider for BlockChain { + /// Returns true if the given block is known + /// (though not necessarily a part of the canon chain). + fn is_known(&self, hash: &H256) -> bool { + self.query_extras_exist(hash, &self.block_details) + } + + /// Get raw block data + fn block(&self, hash: &H256) -> Option { + { + let read = self.blocks.read().unwrap(); + match read.get(hash) { + Some(v) => return Some(v.clone()), + None => () + } + } + + let opt = self.blocks_db.get(hash) + .expect("Low level database error. Some issue with disk?"); + + match opt { + Some(b) => { + let bytes: Bytes = b.to_vec(); + let mut write = self.blocks.write().unwrap(); + write.insert(hash.clone(), bytes.clone()); + Some(bytes) + }, + None => None + } + } + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option { + self.query_extras(hash, &self.block_details) + } + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option { + self.query_extras(&index, &self.block_hashes) + } +} + impl BlockChain { /// Create new instance of blockchain from given Genesis /// @@ -363,61 +454,11 @@ impl BlockChain { (batch, Some(best_block)) } - /// Returns true if the given block is known - /// (though not necessarily a part of the canon chain). - pub fn is_known(&self, hash: &H256) -> bool { - self.query_extras_exist(hash, &self.block_details) - } - /// Returns true if transaction is known. pub fn is_known_transaction(&self, hash: &H256) -> bool { self.query_extras_exist(hash, &self.transaction_addresses) } - /// Returns reference to genesis hash. - pub fn genesis_hash(&self) -> H256 { - self.block_hash(0).expect("Genesis hash should always exist") - } - - /// Get the partial-header of a block. - pub fn block_header(&self, hash: &H256) -> Option
{ - self.block(hash).map(|bytes| BlockView::new(&bytes).header()) - } - - /// Get a list of transactions for a given block. - /// Returns None if block deos not exist. - pub fn transactions(&self, hash: &H256) -> Option> { - self.block(hash).map(|bytes| BlockView::new(&bytes).transactions()) - } - - /// Get a list of transaction hashes for a given block. - /// Returns None if block does not exist. - pub fn transaction_hashes(&self, hash: &H256) -> Option> { - self.block(hash).map(|bytes| BlockView::new(&bytes).transaction_hashes()) - } - - /// Get a list of uncles for a given block. - /// Returns None if block deos not exist. - pub fn uncles(&self, hash: &H256) -> Option> { - self.block(hash).map(|bytes| BlockView::new(&bytes).uncles()) - } - - /// Get a list of uncle hashes for a given block. - /// Returns None if block does not exist. - pub fn uncle_hashes(&self, hash: &H256) -> Option> { - self.block(hash).map(|bytes| BlockView::new(&bytes).uncle_hashes()) - } - - /// Get the familial details concerning a block. - pub fn block_details(&self, hash: &H256) -> Option { - self.query_extras(hash, &self.block_details) - } - - /// Get the hash of given block's number. - pub fn block_hash(&self, index: BlockNumber) -> Option { - self.query_extras(&index, &self.block_hashes) - } - /// Get best block hash. pub fn best_block_hash(&self) -> H256 { self.best_block.read().unwrap().hash.clone() @@ -433,40 +474,11 @@ impl BlockChain { self.best_block.read().unwrap().total_difficulty } - /// Get the number of given block's hash. - pub fn block_number(&self, hash: &H256) -> Option { - self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number()) - } - /// Get the transactions' log blooms of a block. pub fn log_blooms(&self, hash: &H256) -> Option { self.query_extras(hash, &self.block_logs) } - /// Get raw block data - pub fn block(&self, hash: &H256) -> Option { - { - let read = self.blocks.read().unwrap(); - match read.get(hash) { - Some(v) => return Some(v.clone()), - None => () - } - } - - let opt = self.blocks_db.get(hash) - .expect("Low level database error. Some issue with disk?"); - - match opt { - Some(b) => { - let bytes: Bytes = b.to_vec(); - let mut write = self.blocks.write().unwrap(); - write.insert(hash.clone(), bytes.clone()); - Some(bytes) - }, - None => None - } - } - fn query_extras(&self, hash: &K, cache: &RwLock>) -> Option where T: Clone + Decodable + ExtrasIndexable, K: ExtrasSliceConvertable + Eq + Hash + Clone { diff --git a/src/client.rs b/src/client.rs index 018f99b6f..100ac6bd8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,5 @@ use util::*; -use blockchain::BlockChain; +use blockchain::{BlockChain, BlockProvider}; use views::BlockView; use error::*; use header::BlockNumber; diff --git a/src/error.rs b/src/error.rs index 85349c25c..01618c66c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,13 +3,13 @@ use util::*; use header::BlockNumber; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct Mismatch { pub expected: T, pub found: T, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct OutOfBounds { pub min: Option, pub max: Option, @@ -28,7 +28,7 @@ pub enum ExecutionError { InvalidNonce { expected: U256, is: U256 }, /// Returned when cost of transaction (value + gas_price * gas) exceeds /// current sender balance. - NotEnoughCash { required: U256, is: U256 }, + NotEnoughCash { required: U512, is: U512 }, /// Returned when internal evm error occurs. Internal } @@ -38,7 +38,7 @@ pub enum TransactionError { InvalidGasLimit(OutOfBounds), } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum BlockError { TooManyUncles(OutOfBounds), UncleWrongGeneration, diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index f3c4ad170..f2f5eeafd 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -51,14 +51,6 @@ impl Engine for Ethash { if header.difficulty < min_difficulty { return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty }))) } - let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap()); - if header.gas_limit < min_gas_limit { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }))); - } - let maximum_extra_data_size = self.maximum_extra_data_size(); - if header.number != 0 && header.extra_data.len() > maximum_extra_data_size { - return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data.len() }))); - } // TODO: Verify seal (quick) Ok(()) } diff --git a/src/executive.rs b/src/executive.rs index 5a5a835cf..262695a7c 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -21,6 +21,8 @@ struct Substate { logs: Vec, /// Refund counter of SSTORE nonzero->zero. refunds_count: U256, + /// Created contracts. + contracts_created: Vec
} impl Substate { @@ -30,11 +32,13 @@ impl Substate { suicides: HashSet::new(), logs: vec![], refunds_count: U256::zero(), + contracts_created: vec![] } } } /// Transaction execution receipt. +#[derive(Debug)] pub struct Executed { /// Gas paid up front for execution of transaction. pub gas: U256, @@ -51,9 +55,15 @@ pub struct Executed { pub cumulative_gas_used: U256, /// Vector of logs generated by transaction. pub logs: Vec, - /// Execution ended running out of gas. pub out_of_gas: bool, + /// Addresses of contracts created during execution of transaction. + /// Ordered from earliest creation. + /// + /// eg. sender creates contract A and A in constructor creates contract B + /// + /// B creation ends first, and it will be the first element of the vector. + pub contracts_created: Vec
} /// Transaction execution result. @@ -64,7 +74,7 @@ pub struct Executive<'a> { state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, - depth: usize, + depth: usize } impl<'a> Executive<'a> { @@ -85,14 +95,12 @@ impl<'a> Executive<'a> { state: state, info: info, engine: engine, - depth: depth, + depth: depth } } /// This funtion should be used to execute transaction. pub fn transact(&mut self, t: &Transaction) -> Result { - // TODO: validate transaction signature ?/ sender - let sender = try!(t.sender()); let nonce = self.state.nonce(&sender); @@ -112,18 +120,19 @@ impl<'a> Executive<'a> { // TODO: we might need bigints here, or at least check overflows. let balance = self.state.balance(&sender); - let gas_cost = t.gas * t.gas_price; - let total_cost = t.value + gas_cost; + let gas_cost = U512::from(t.gas) * U512::from(t.gas_price); + let total_cost = U512::from(t.value) + gas_cost; // avoid unaffordable transactions - if balance < total_cost { - return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: balance })); + if U512::from(balance) < total_cost { + return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: U512::from(balance) })); } // NOTE: there can be no invalid transactions from this point. self.state.inc_nonce(&sender); - let mut substate = Substate::new(); + self.state.sub_balance(&sender, &U256::from(gas_cost)); + let mut substate = Substate::new(); let backup = self.state.clone(); let res = match t.action() { @@ -216,6 +225,7 @@ impl<'a> Executive<'a> { cumulative_gas_used: self.info.gas_used + t.gas, logs: vec![], out_of_gas: true, + contracts_created: vec![] }) }, Ok(gas_left) => { @@ -232,9 +242,10 @@ impl<'a> Executive<'a> { self.state.add_balance(&t.sender().unwrap(), &refund_value); // fees earned by author - let fees = (t.gas - refund) * t.gas_price; + let fees = t.gas - refund; + let fees_value = fees * t.gas_price; let author = &self.info.author; - self.state.add_balance(author, &fees); + self.state.add_balance(author, &fees_value); // perform suicides for address in substate.suicides.iter() { @@ -249,6 +260,7 @@ impl<'a> Executive<'a> { cumulative_gas_used: self.info.gas_used + gas_used, logs: substate.logs, out_of_gas: false, + contracts_created: substate.contracts_created }) } } @@ -332,7 +344,7 @@ impl<'a> Ext for Externalities<'a> { fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option
), evm::Error> { // if balance is insufficient or we are to deep, return - if self.state.balance(&self.params.address) < *value && self.depth >= 1024 { + if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit { return Ok((gas, None)); } @@ -361,7 +373,7 @@ impl<'a> Ext for Externalities<'a> { let mut call_gas = call_gas; let is_call = receive_address == code_address; - if is_call && self.state.code(&code_address).is_none() { + if is_call && !self.state.exists(&code_address) { gas_cost = gas_cost + self.schedule.call_new_account_gas as u64; } @@ -377,9 +389,8 @@ impl<'a> Ext for Externalities<'a> { let gas = gas - gas_cost; - //println!("depth: {:?}", self.depth); // if balance is insufficient or we are to deep, return - if self.state.balance(&self.params.address) < *value && self.depth >= 1024 { + if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit { return Ok(gas + call_gas) } @@ -395,7 +406,9 @@ impl<'a> Ext for Externalities<'a> { }; let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); - ex.call(¶ms, self.substate, output).map(|gas_left| gas + gas_left.low_u64()) + ex.call(¶ms, self.substate, output).map(|gas_left| { + gas + gas_left.low_u64() + }) } fn extcode(&self, address: &Address) -> Vec { @@ -403,6 +416,7 @@ impl<'a> Ext for Externalities<'a> { } fn ret(&mut self, gas: u64, data: &[u8]) -> Result { + println!("ret"); match &mut self.output { &mut OutputPolicy::Return(ref mut slice) => unsafe { let len = cmp::min(slice.len(), data.len()); @@ -412,7 +426,10 @@ impl<'a> Ext for Externalities<'a> { &mut OutputPolicy::InitContract => { let return_cost = data.len() as u64 * self.schedule.create_data_gas as u64; if return_cost > gas { - return Err(evm::Error::OutOfGas); + return match self.schedule.exceptional_failed_code_deposit { + true => Err(evm::Error::OutOfGas), + false => Ok(gas) + } } let mut code = vec![]; code.reserve(data.len()); @@ -422,6 +439,7 @@ impl<'a> Ext for Externalities<'a> { } let address = &self.params.address; self.state.init_code(address, code); + self.substate.contracts_created.push(address.clone()); Ok(gas - return_cost) } } @@ -452,9 +470,35 @@ mod tests { use common::*; use state::*; use ethereum; - use null_engine::*; + use engine::*; + use spec::*; + use evm::Schedule; use super::Substate; + struct TestEngine { + spec: Spec, + stack_limit: usize + } + + impl TestEngine { + fn new(stack_limit: usize) -> TestEngine { + TestEngine { + spec: ethereum::new_frontier(), + stack_limit: stack_limit + } + } + } + + impl Engine for TestEngine { + fn name(&self) -> &str { "TestEngine" } + fn spec(&self) -> &Spec { &self.spec } + fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + let mut schedule = Schedule::new_frontier(); + schedule.stack_limit = self.stack_limit; + schedule + } + } + #[test] fn test_contract_address() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); @@ -464,35 +508,114 @@ mod tests { #[test] // TODO: replace params with transactions! - fn test_executive() { + fn test_sender_balance() { let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address = contract_address(&sender, &U256::zero()); let mut params = ActionParams::new(); params.address = address.clone(); params.sender = sender.clone(); - params.gas = U256::from(0x174876e800u64); + params.gas = U256::from(100_000); params.code = "3331600055".from_hex().unwrap(); params.value = U256::from(0x7); let mut state = State::new_temp(); state.add_balance(&sender, &U256::from(0x100u64)); let info = EnvInfo::new(); - let engine = NullEngine::new_boxed(ethereum::new_frontier()); + let engine = TestEngine::new(0); let mut substate = Substate::new(); - { - let mut ex = Executive::new(&mut state, &info, engine.deref()); - let _res = ex.create(¶ms, &mut substate); - } + let gas_left = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.create(¶ms, &mut substate).unwrap() + }; + assert_eq!(gas_left, U256::from(79_975)); assert_eq!(state.storage_at(&address, &H256::new()), H256::from(&U256::from(0xf9u64))); assert_eq!(state.balance(&sender), U256::from(0xf9)); assert_eq!(state.balance(&address), U256::from(0x7)); + // 0 cause contract hasn't returned + assert_eq!(substate.contracts_created.len(), 0); // TODO: just test state root. } #[test] fn test_create_contract() { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 17 - push 17 + // f0 - create + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + let address = contract_address(&sender, &U256::zero()); + // TODO: add tests for 'callcreate' + //let next_address = contract_address(&address, &U256::zero()); + let mut params = ActionParams::new(); + params.address = address.clone(); + params.sender = sender.clone(); + params.origin = sender.clone(); + params.gas = U256::from(100_000); + params.code = code.clone(); + params.value = U256::from(100); + let mut state = State::new_temp(); + state.add_balance(&sender, &U256::from(100)); + let info = EnvInfo::new(); + let engine = TestEngine::new(0); + let mut substate = Substate::new(); + + let gas_left = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.create(¶ms, &mut substate).unwrap() + }; + + assert_eq!(gas_left, U256::from(47_976)); + assert_eq!(substate.contracts_created.len(), 0); + } + + #[test] + fn test_create_contract_without_stack_limit() { + // code: + // + // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes? + // 60 00 - push 0 + // 52 + // 60 1d - push 29 + // 60 03 - push 3 + // 60 17 - push 17 + // f0 - create + // 60 00 - push 0 + // 55 sstore + // + // other code: + // + // 60 10 - push 16 + // 80 - duplicate first stack item + // 60 0c - push 12 + // 60 00 - push 0 + // 39 - copy current code to memory + // 60 00 - push 0 + // f3 - return + + let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(&sender, &U256::zero()); let next_address = contract_address(&address, &U256::zero()); @@ -500,23 +623,80 @@ mod tests { params.address = address.clone(); params.sender = sender.clone(); params.origin = sender.clone(); - params.gas = U256::from(0x174876e800u64); - params.code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036000f0600055".from_hex().unwrap(); + params.gas = U256::from(100_000); + params.code = code.clone(); + params.value = U256::from(100); let mut state = State::new_temp(); - state.add_balance(&sender, &U256::from(0x100u64)); + state.add_balance(&sender, &U256::from(100)); let info = EnvInfo::new(); - let engine = NullEngine::new_boxed(ethereum::new_frontier()); + let engine = TestEngine::new(1024); let mut substate = Substate::new(); { - let mut ex = Executive::new(&mut state, &info, engine.deref()); - let _res = ex.create(¶ms, &mut substate); - println!("res: {:?}", _res); + let mut ex = Executive::new(&mut state, &info, &engine); + ex.create(¶ms, &mut substate).unwrap(); } - assert_eq!(state.storage_at(&address, &H256::new()), H256::from(next_address.clone())); - assert_eq!(state.code(&next_address).unwrap(), "6000355415600957005b602035600035".from_hex().unwrap()); - //assert!(false); + assert_eq!(substate.contracts_created.len(), 1); + assert_eq!(substate.contracts_created[0], next_address); + } + + #[test] + fn test_aba_calls() { + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 18 - push 18 + // 73 945304eb96065b2a98b57a48a06ae28d285a71b5 - push this address + // 61 03e8 - push 1000 + // f1 - message call + // 58 - get PC + // 55 - sstore + + let code_a = "6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b56103e8f15855".from_hex().unwrap(); + + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 00 - push 0 + // 60 17 - push 17 + // 73 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 - push this address + // 61 0x01f4 - push 500 + // f1 - message call + // 60 01 - push 1 + // 01 - add + // 58 - get PC + // 55 - sstore + let code_b = "60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66101f4f16001015855".from_hex().unwrap(); + + let address_a = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5" ).unwrap(); + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + + let mut params = ActionParams::new(); + params.address = address_a.clone(); + params.sender = sender.clone(); + params.gas = U256::from(100_000); + params.code = code_a.clone(); + params.value = U256::from(100_000); + + let mut state = State::new_temp(); + state.init_code(&address_a, code_a.clone()); + state.init_code(&address_b, code_b.clone()); + state.add_balance(&sender, &U256::from(100_000)); + + let info = EnvInfo::new(); + let engine = TestEngine::new(0); + let mut substate = Substate::new(); + + let gas_left = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.call(¶ms, &mut substate, &mut []).unwrap() + }; + + assert_eq!(gas_left, U256::from(73_237)); + assert_eq!(state.storage_at(&address_a, &H256::from(&U256::from(0x23))), H256::from(&U256::from(1))); } #[test] @@ -538,28 +718,155 @@ mod tests { // 03 - sub // f1 - message call (self in this case) // 60 01 - push 1 - // 55 - store + // 55 - sstore let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap(); let address = contract_address(&sender, &U256::zero()); let mut params = ActionParams::new(); params.address = address.clone(); - params.sender = sender.clone(); - params.origin = sender.clone(); - params.gas = U256::from(0x590b3); - params.gas_price = U256::one(); + params.gas = U256::from(100_000); params.code = code.clone(); - println!("init gas: {:?}", params.gas.low_u64()); let mut state = State::new_temp(); state.init_code(&address, code.clone()); let info = EnvInfo::new(); - let engine = NullEngine::new_boxed(ethereum::new_frontier()); + let engine = TestEngine::new(0); let mut substate = Substate::new(); - { - let mut ex = Executive::new(&mut state, &info, engine.deref()); - let _res = ex.call(¶ms, &mut substate, &mut []); - println!("res: {:?}", _res); + let gas_left = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.call(¶ms, &mut substate, &mut []).unwrap() + }; + + assert_eq!(gas_left, U256::from(59_870)); + assert_eq!(state.storage_at(&address, &H256::from(&U256::zero())), H256::from(&U256::from(1))); + assert_eq!(state.storage_at(&address, &H256::from(&U256::one())), H256::from(&U256::from(1))); + } + + #[test] + fn test_transact_simple() { + let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero()); + let keypair = KeyPair::create().unwrap(); + t.sign(&keypair.secret()); + let sender = t.sender().unwrap(); + let contract = contract_address(&sender, &U256::zero()); + + let mut state = State::new_temp(); + state.add_balance(&sender, &U256::from(18)); + let mut info = EnvInfo::new(); + info.gas_limit = U256::from(100_000); + let engine = TestEngine::new(0); + + let executed = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.transact(&t).unwrap() + }; + + assert_eq!(executed.gas, U256::from(100_000)); + assert_eq!(executed.gas_used, U256::from(20_025)); + assert_eq!(executed.refunded, U256::from(79_975)); + assert_eq!(executed.cumulative_gas_used, U256::from(20_025)); + assert_eq!(executed.logs.len(), 0); + assert_eq!(executed.out_of_gas, false); + assert_eq!(executed.contracts_created.len(), 0); + assert_eq!(state.balance(&sender), U256::from(1)); + assert_eq!(state.balance(&contract), U256::from(17)); + assert_eq!(state.nonce(&sender), U256::from(1)); + assert_eq!(state.storage_at(&contract, &H256::new()), H256::from(&U256::from(1))); + } + + #[test] + fn test_transact_invalid_sender() { + let t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero()); + + let mut state = State::new_temp(); + let mut info = EnvInfo::new(); + info.gas_limit = U256::from(100_000); + let engine = TestEngine::new(0); + + let res = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.transact(&t) + }; + + match res { + Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => (), + _ => assert!(false, "Expected invalid signature error.") + } + } + + #[test] + fn test_transact_invalid_nonce() { + let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::one()); + let keypair = KeyPair::create().unwrap(); + t.sign(&keypair.secret()); + let sender = t.sender().unwrap(); + + let mut state = State::new_temp(); + state.add_balance(&sender, &U256::from(17)); + let mut info = EnvInfo::new(); + info.gas_limit = U256::from(100_000); + let engine = TestEngine::new(0); + + let res = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.transact(&t) + }; + + match res { + Err(Error::Execution(ExecutionError::InvalidNonce { expected, is })) + if expected == U256::zero() && is == U256::one() => (), + _ => assert!(false, "Expected invalid nonce error.") + } + } + + #[test] + fn test_transact_gas_limit_reached() { + let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(80_001), U256::zero(), U256::zero()); + let keypair = KeyPair::create().unwrap(); + t.sign(&keypair.secret()); + let sender = t.sender().unwrap(); + + let mut state = State::new_temp(); + state.add_balance(&sender, &U256::from(17)); + let mut info = EnvInfo::new(); + info.gas_used = U256::from(20_000); + info.gas_limit = U256::from(100_000); + let engine = TestEngine::new(0); + + let res = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.transact(&t) + }; + + match res { + Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) + if gas_limit == U256::from(100_000) && gas_used == U256::from(20_000) && gas == U256::from(80_001) => (), + _ => assert!(false, "Expected block gas limit error.") + } + } + + #[test] + fn test_not_enough_cash() { + let mut t = Transaction::new_create(U256::from(18), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::one(), U256::zero()); + let keypair = KeyPair::create().unwrap(); + t.sign(&keypair.secret()); + let sender = t.sender().unwrap(); + + let mut state = State::new_temp(); + state.add_balance(&sender, &U256::from(100_017)); + let mut info = EnvInfo::new(); + info.gas_limit = U256::from(100_000); + let engine = TestEngine::new(0); + + let res = { + let mut ex = Executive::new(&mut state, &info, &engine); + ex.transact(&t) + }; + + match res { + Err(Error::Execution(ExecutionError::NotEnoughCash { required , is })) + if required == U512::zero() && is == U512::one() => (), + _ => assert!(false, "Expected not enough cash error.") } } } diff --git a/src/header.rs b/src/header.rs index 7a01797a6..45190332d 100644 --- a/src/header.rs +++ b/src/header.rs @@ -10,7 +10,7 @@ pub type BlockNumber = u64; /// which is non-specific. /// /// Doesn't do all that much on its own. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Header { // TODO: make all private. pub parent_hash: H256, diff --git a/src/lib.rs b/src/lib.rs index 1e7da98dc..76ef7f09b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ #![feature(cell_extras)] #![feature(augmented_assignments)] +//#![feature(plugin)] +//#![plugin(interpolate_idents)] //! Ethcore's ethereum implementation //! //! ### Rust version @@ -107,6 +109,9 @@ pub mod blockchain; pub mod extras; pub mod evm; +#[cfg(test)] +mod tests; + pub mod client; pub mod sync; pub mod block; diff --git a/src/log_entry.rs b/src/log_entry.rs index 939d60276..8602eeb66 100644 --- a/src/log_entry.rs +++ b/src/log_entry.rs @@ -2,6 +2,7 @@ use util::*; use basic_types::LogBloom; /// A single log's entry. +#[derive(Debug)] pub struct LogEntry { pub address: Address, pub topics: Vec, @@ -59,4 +60,4 @@ mod tests { let log = LogEntry::new(address, vec![], vec![]); assert_eq!(log.bloom(), bloom); } -} \ No newline at end of file +} diff --git a/src/queue.rs b/src/queue.rs index 721960259..9cef88181 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,5 +1,5 @@ use util::*; -use blockchain::BlockChain; +use blockchain::*; use views::{BlockView}; use verification::*; use error::*; diff --git a/src/state.rs b/src/state.rs index 42813ca76..e02da4ecb 100644 --- a/src/state.rs +++ b/src/state.rs @@ -76,6 +76,11 @@ impl State { self.cache.borrow_mut().insert(account.clone(), None); } + /// Determine whether an account exists. + pub fn exists(&self, a: &Address) -> bool { + self.cache.borrow().get(&a).unwrap_or(&None).is_some() || SecTrieDB::new(&self.db, &self.root).contains(&a) + } + /// 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)) @@ -287,9 +292,12 @@ fn get_from_database() { fn remove() { let a = Address::zero(); let mut s = State::new_temp(); + assert_eq!(s.exists(&a), false); s.inc_nonce(&a); + assert_eq!(s.exists(&a), true); assert_eq!(s.nonce(&a), U256::from(1u64)); s.kill_account(&a); + assert_eq!(s.exists(&a), false); assert_eq!(s.nonce(&a), U256::from(0u64)); } @@ -300,20 +308,24 @@ fn remove_from_database() { let mut s = State::new_temp(); s.inc_nonce(&a); s.commit(); + assert_eq!(s.exists(&a), true); assert_eq!(s.nonce(&a), U256::from(1u64)); s.drop() }; let (r, db) = { let mut s = State::from_existing(db, r, U256::from(0u8)); + assert_eq!(s.exists(&a), true); assert_eq!(s.nonce(&a), U256::from(1u64)); s.kill_account(&a); s.commit(); + assert_eq!(s.exists(&a), false); assert_eq!(s.nonce(&a), U256::from(0u64)); s.drop() }; let s = State::from_existing(db, r, U256::from(0u8)); + assert_eq!(s.exists(&a), false); assert_eq!(s.nonce(&a), U256::from(0u64)); } diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 000000000..b048a1c16 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,4 @@ +#[macro_use] +mod test_common; + +mod transaction; \ No newline at end of file diff --git a/src/tests/test_common.rs b/src/tests/test_common.rs new file mode 100644 index 000000000..8c2f25d8a --- /dev/null +++ b/src/tests/test_common.rs @@ -0,0 +1,62 @@ +pub use common::*; + +pub fn clean(s: &str) -> &str { + if s.len() >= 2 && &s[0..2] == "0x" { + &s[2..] + } else { + s + } +} + +pub fn bytes_from_json(json: &Json) -> Bytes { + let s = json.as_string().unwrap(); + if s.len() % 2 == 1 { + FromHex::from_hex(&("0".to_string() + &(clean(s).to_string()))[..]).unwrap_or(vec![]) + } else { + FromHex::from_hex(clean(s)).unwrap_or(vec![]) + } +} + +pub fn address_from_json(json: &Json) -> Address { + let s = json.as_string().unwrap(); + if s.len() % 2 == 1 { + address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..]) + } else { + address_from_hex(clean(s)) + } +} + +pub fn u256_from_json(json: &Json) -> U256 { + let s = json.as_string().unwrap(); + if s.len() >= 2 && &s[0..2] == "0x" { + // hex + U256::from_str(&s[2..]).unwrap() + } + else { + // dec + U256::from_dec_str(s).unwrap() + } +} + +#[macro_export] +macro_rules! declare_test { + ($id: ident, $name: expr) => { + #[test] + #[allow(non_snake_case)] + fn $id() { + assert!(do_json_test(include_bytes!(concat!("../../res/ethereum/tests/", $name, ".json"))).len() == 0); + } + }; +} + +#[macro_export] +macro_rules! declare_test_ignore { + ($id: ident, $name: expr) => { + #[test] + #[ignore] + #[allow(non_snake_case)] + fn $id() { + assert!(do_json_test(include_bytes!(concat!("../../res/ethereum/tests/", $name, ".json"))).len() == 0); + } + }; +} diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs new file mode 100644 index 000000000..b6f592628 --- /dev/null +++ b/src/tests/transaction.rs @@ -0,0 +1,75 @@ +use super::test_common::*; +use evm; + +fn do_json_test(json_data: &[u8]) -> Vec { + let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid"); + let mut failed = Vec::new(); + let old_schedule = evm::Schedule::new_frontier(); + let new_schedule = evm::Schedule::new_homestead(); + for (name, test) in json.as_object().unwrap() { + let mut fail = false; + let mut fail_unless = |cond: bool| if !cond && fail { failed.push(name.to_string()); fail = true }; + let schedule = match test.find("blocknumber") + .and_then(|j| j.as_string()) + .and_then(|s| BlockNumber::from_str(s).ok()) + .unwrap_or(0) { x if x < 900000 => &old_schedule, _ => &new_schedule }; + let rlp = bytes_from_json(&test["rlp"]); + let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(schedule)); + fail_unless(test.find("transaction").is_none() == res.is_err()); + if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) { + let t = res.unwrap(); + fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender))); + fail_unless(t.data == bytes_from_json(&tx["data"])); + fail_unless(t.gas == u256_from_json(&tx["gasLimit"])); + fail_unless(t.gas_price == u256_from_json(&tx["gasPrice"])); + fail_unless(t.nonce == u256_from_json(&tx["nonce"])); + fail_unless(t.value == u256_from_json(&tx["value"])); + if let Action::Call(ref to) = t.action { + fail_unless(to == &address_from_json(&tx["to"])); + } else { + fail_unless(bytes_from_json(&tx["to"]).len() == 0); + } + } + } + for f in failed.iter() { + println!("FAILED: {:?}", f); + } + failed +} + +// Once we have interpolate idents. +/*macro_rules! declare_test { + ($test_set_name: ident / $name: ident) => { + #[test] + #[allow(non_snake_case)] + fn $name() { + assert!(do_json_test(include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($name), ".json"))).len() == 0); + } + }; + ($test_set_name: ident / $prename: ident / $name: ident) => { + #[test] + #[allow(non_snake_case)] + interpolate_idents! { fn [$prename _ $name]() + { + let json = include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($prename), "/", stringify!($name), ".json")); + assert!(do_json_test(json).len() == 0); + } + } + }; +} + +declare_test!{TransactionTests/ttTransactionTest} +declare_test!{TransactionTests/tt10mbDataField} +declare_test!{TransactionTests/ttWrongRLPTransaction} +declare_test!{TransactionTests/Homestead/ttTransactionTest} +declare_test!{TransactionTests/Homestead/tt10mbDataField} +declare_test!{TransactionTests/Homestead/ttWrongRLPTransaction} +declare_test!{TransactionTests/RandomTests/tr201506052141PYTHON}*/ + +declare_test!{TransactionTests_ttTransactionTest, "TransactionTests/ttTransactionTest"} +declare_test_ignore!{TransactionTests_tt10mbDataField, "TransactionTests/tt10mbDataField"} +declare_test!{TransactionTests_ttWrongRLPTransaction, "TransactionTests/ttWrongRLPTransaction"} +declare_test!{TransactionTests_Homestead_ttTransactionTest, "TransactionTests/Homestead/ttTransactionTest"} +declare_test_ignore!{TransactionTests_Homestead_tt10mbDataField, "TransactionTests/Homestead/tt10mbDataField"} +declare_test!{TransactionTests_Homestead_ttWrongRLPTransaction, "TransactionTests/Homestead/ttWrongRLPTransaction"} +declare_test!{TransactionTests_RandomTests_tr201506052141PYTHON, "TransactionTests/RandomTests/tr201506052141PYTHON"} diff --git a/src/transaction.rs b/src/transaction.rs index 64f5b5806..35bb0eed2 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -23,10 +23,58 @@ pub struct Transaction { pub r: U256, pub s: U256, - hash: RefCell>, //TODO: make this private + hash: RefCell>, + sender: RefCell>, } impl Transaction { + /// Create a new message-call transaction. + pub fn new_call(to: Address, value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction { + Transaction { + nonce: nonce, + gas_price: gas_price, + gas: gas, + action: Action::Call(to), + value: value, + data: data, + v: 0, + r: U256::zero(), + s: U256::zero(), + hash: RefCell::new(None), + sender: RefCell::new(None), + } + } + + /// Create a new contract-creation transaction. + pub fn new_create(value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction { + Transaction { + nonce: nonce, + gas_price: gas_price, + gas: gas, + action: Action::Create, + value: value, + data: data, + v: 0, + r: U256::zero(), + s: U256::zero(), + hash: RefCell::new(None), + sender: RefCell::new(None), + } + } + + /// Get the nonce of the transaction. + pub fn nonce(&self) -> &U256 { &self.nonce } + /// Get the gas price of the transaction. + pub fn gas_price(&self) -> &U256 { &self.gas_price } + /// Get the gas of the transaction. + pub fn gas(&self) -> &U256 { &self.gas } + /// Get the action of the transaction (Create or Call). + pub fn action(&self) -> &Action { &self.action } + /// Get the value of the transaction. + pub fn value(&self) -> &U256 { &self.value } + /// Get the data of the transaction. + pub fn data(&self) -> &Bytes { &self.data } + /// Append object into RLP stream, optionally with or without the signature. pub fn rlp_append_opt(&self, s: &mut RlpStream, with_seal: Seal) { s.append_list(6 + match with_seal { Seal::With => 3, _ => 0 }); @@ -75,9 +123,6 @@ impl Transaction { *self.hash.borrow_mut() = None; } - /// Returns transaction type. - pub fn action(&self) -> &Action { &self.action } - /// 0 is `v` is 27, 1 if 28, and 4 otherwise. pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } } @@ -88,27 +133,47 @@ impl Transaction { pub fn message_hash(&self) -> H256 { self.rlp_bytes_opt(Seal::Without).sha3() } /// Returns transaction sender. - pub fn sender(&self) -> Result { Ok(From::from(try!(ec::recover(&self.signature(), &self.message_hash())).sha3())) } + pub fn sender(&self) -> Result { + let mut sender = self.sender.borrow_mut(); + match &mut *sender { + &mut Some(ref h) => Ok(h.clone()), + sender @ &mut None => { + *sender = Some(From::from(try!(ec::recover(&self.signature(), &self.message_hash())).sha3())); + Ok(sender.as_ref().unwrap().clone()) + } + } + } + + /// Signs the transaction as coming from `sender`. + pub fn sign(&mut self, secret: &Secret) { + let sig = ec::sign(secret, &self.message_hash()); + let (r, s, v) = sig.unwrap().to_rsv(); + self.r = r; + self.s = s; + self.v = v + 27; + } + + /// Signs the transaction as coming from `sender`. + pub fn signed(self, secret: &Secret) -> Transaction { let mut r = self; r.sign(secret); r } /// Get the transaction cost in gas for the given params. - pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> U256 { - // CRITICAL TODO XXX FIX NEED BIGINT!!!!! + pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> u64 { data.iter().fold( - U256::from(if is_create {schedule.tx_create_gas} else {schedule.tx_gas}), - |g, b| g + U256::from(match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas}) + (if is_create {schedule.tx_create_gas} else {schedule.tx_gas}) as u64, + |g, b| g + (match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas }) as u64 ) } /// Get the transaction cost in gas for this transaction. - pub fn gas_required(&self, schedule: &Schedule) -> U256 { + pub fn gas_required(&self, schedule: &Schedule) -> u64 { Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule) } /// Do basic validation, checking for valid signature and minimum gas, pub fn validate(self, schedule: &Schedule) -> Result { try!(self.sender()); - if self.gas < self.gas_required(&schedule) { - Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(self.gas_required(&schedule)), max: None, found: self.gas}))) + if self.gas < U256::from(self.gas_required(&schedule)) { + Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}))) } else { Ok(self) } @@ -142,113 +207,29 @@ impl Decodable for Transaction { v: try!(u16::decode(&d[6])) as u8, r: try!(Decodable::decode(&d[7])), s: try!(Decodable::decode(&d[8])), - hash: RefCell::new(None) + hash: RefCell::new(None), + sender: RefCell::new(None), }) } } -pub fn clean(s: &str) -> &str { - if s.len() >= 2 && &s[0..2] == "0x" { - &s[2..] - } else { - s - } +#[test] +fn sender_test() { + let t: Transaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); + assert_eq!(t.data, b""); + assert_eq!(t.gas, U256::from(0x5208u64)); + assert_eq!(t.gas_price, U256::from(0x01u64)); + assert_eq!(t.nonce, U256::from(0x00u64)); + if let Action::Call(ref to) = t.action { + assert_eq!(*to, address_from_hex("095e7baea6a6c7c4c2dfeb977efac326af552d87")); + } else { panic!(); } + assert_eq!(t.value, U256::from(0x0au64)); + assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e")); } -pub fn bytes_from_json(json: &Json) -> Bytes { - let s = json.as_string().unwrap(); - if s.len() % 2 == 1 { - FromHex::from_hex(&("0".to_string() + &(clean(s).to_string()))[..]).unwrap_or(vec![]) - } else { - FromHex::from_hex(clean(s)).unwrap_or(vec![]) - } -} - -pub fn address_from_json(json: &Json) -> Address { - let s = json.as_string().unwrap(); - if s.len() % 2 == 1 { - address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..]) - } else { - address_from_hex(clean(s)) - } -} - -pub fn u256_from_json(json: &Json) -> U256 { - let s = json.as_string().unwrap(); - if s.len() >= 2 && &s[0..2] == "0x" { - // hex - U256::from_str(&s[2..]).unwrap() - } - else { - // dec - U256::from_dec_str(s).unwrap() - } -} - -#[cfg(test)] -mod tests { - use util::*; - use evm::Schedule; - use header::BlockNumber; - use super::*; - - #[test] - fn sender_test() { - let t: Transaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); - assert_eq!(t.data, b""); - assert_eq!(t.gas, U256::from(0x5208u64)); - assert_eq!(t.gas_price, U256::from(0x01u64)); - assert_eq!(t.nonce, U256::from(0x00u64)); - if let Action::Call(ref to) = t.action { - assert_eq!(*to, address_from_hex("095e7baea6a6c7c4c2dfeb977efac326af552d87")); - } else { panic!(); } - assert_eq!(t.value, U256::from(0x0au64)); - assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e")); - } - - fn do_json_test(json_data: &[u8]) -> Vec { - let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid"); - let mut failed = Vec::new(); - let schedule = Schedule::new_frontier(); - for (name, test) in json.as_object().unwrap() { - let mut fail = false; - let mut fail_unless = |cond: bool| if !cond && fail { failed.push(name.to_string()); fail = true }; - let _ = BlockNumber::from_str(test["blocknumber"].as_string().unwrap()).unwrap(); - let rlp = bytes_from_json(&test["rlp"]); - let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(&schedule)); - fail_unless(test.find("transaction").is_none() == res.is_err()); - if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) { - let t = res.unwrap(); - fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender))); - fail_unless(t.data == bytes_from_json(&tx["data"])); - fail_unless(t.gas == u256_from_json(&tx["gasLimit"])); - fail_unless(t.gas_price == u256_from_json(&tx["gasPrice"])); - fail_unless(t.nonce == u256_from_json(&tx["nonce"])); - fail_unless(t.value == u256_from_json(&tx["value"])); - if let Action::Call(ref to) = t.action { - fail_unless(to == &address_from_json(&tx["to"])); - } else { - fail_unless(bytes_from_json(&tx["to"]).len() == 0); - } - } - } - for f in failed.iter() { - println!("FAILED: {:?}", f); - } - failed - } - - macro_rules! declare_test { - ($test_set_name: ident/$name: ident) => { - #[test] - #[allow(non_snake_case)] - fn $name() { - assert!(do_json_test(include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($name), ".json"))).len() == 0); - } - } - } - - declare_test!{TransactionTests/ttTransactionTest} - declare_test!{TransactionTests/tt10mbDataField} - declare_test!{TransactionTests/ttWrongRLPTransaction} +#[test] +fn signing() { + let key = KeyPair::create().unwrap(); + let t = Transaction::new_create(U256::from(42u64), b"Hello!".to_vec(), U256::from(3000u64), U256::from(50_000u64), U256::from(1u64)).signed(&key.secret()); + assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap()); } \ No newline at end of file diff --git a/src/verification.rs b/src/verification.rs index 1f7125e6f..5dabcf84f 100644 --- a/src/verification.rs +++ b/src/verification.rs @@ -7,17 +7,17 @@ use common::*; use engine::Engine; -use blockchain::BlockChain; +use blockchain::*; /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block pub fn verify_block_basic(bytes: &[u8], engine: &Engine) -> Result<(), Error> { let block = BlockView::new(bytes); let header = block.header(); - try!(verify_header(&header)); + try!(verify_header(&header, engine)); try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash)); try!(engine.verify_block_basic(&header, Some(bytes))); for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { - try!(verify_header(&u)); + try!(verify_header(&u, engine)); try!(engine.verify_block_basic(&u, None)); } Ok(()) @@ -37,7 +37,7 @@ pub fn verify_block_unordered(bytes: &[u8], engine: &Engine) -> Result<(), Error } /// Phase 3 verification. Check block information against parent and uncles. -pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Result<(), Error> { +pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { let block = BlockView::new(bytes); let header = block.header(); let parent = try!(bc.block_header(&header.parent_hash).ok_or::(From::from(BlockError::UnknownParent(header.parent_hash.clone())))); @@ -67,9 +67,8 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res } for uncle in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { - let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or::(From::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone())))); - if excluded.contains(&uncle_parent.hash()) { - return Err(From::from(BlockError::UncleInChain(uncle_parent.hash()))) + if excluded.contains(&uncle.hash()) { + return Err(From::from(BlockError::UncleInChain(uncle.hash()))) } // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() @@ -99,13 +98,20 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res // cB.p^7 -------------/ // cB.p^8 let mut expected_uncle_parent = header.parent_hash.clone(); + let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or::(From::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone())))); for _ in 0..depth { - expected_uncle_parent = bc.block_details(&expected_uncle_parent).unwrap().parent; + match bc.block_details(&expected_uncle_parent) { + Some(details) => { + expected_uncle_parent = details.parent; + }, + None => break + } } if expected_uncle_parent != uncle_parent.hash() { return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash()))); } + try!(verify_parent(&uncle, &uncle_parent)); try!(engine.verify_block_final(&uncle, &uncle_parent, Some(bytes))); } } @@ -113,13 +119,21 @@ pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Res } /// Check basic header parameters. -fn verify_header(header: &Header) -> Result<(), Error> { - if header.number > From::from(BlockNumber::max_value()) { +fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> { + if header.number >= From::from(BlockNumber::max_value()) { return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number }))) } if header.gas_used > header.gas_limit { return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }))); } + let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap()); + if header.gas_limit < min_gas_limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }))); + } + let maximum_extra_data_size = engine.maximum_extra_data_size(); + if header.number != 0 && header.extra_data.len() > maximum_extra_data_size { + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data.len() }))); + } Ok(()) } @@ -152,3 +166,235 @@ fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: & Ok(()) } +#[cfg(test)] +mod tests { + use util::*; + use header::*; + use verification::*; + use extras::*; + use error::*; + use error::BlockError::*; + use views::*; + use blockchain::*; + use ethereum; + + fn create_test_block(header: &Header) -> Bytes { + let mut rlp = RlpStream::new_list(3); + rlp.append(header); + rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); + rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); + rlp.out() + } + + fn create_test_block_with_data(header: &Header, transactions: &[Bytes], uncles: &[Header]) -> Bytes { + let mut rlp = RlpStream::new_list(3); + rlp.append(header); + rlp.append_list(transactions.len()); + for t in transactions { + rlp.append_raw(t, 1); + } + rlp.append_list(uncles.len()); + for h in uncles { + rlp.append(h); + } + rlp.out() + } + + fn check_ok(result: Result<(), Error>) { + result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); + } + + fn check_fail(result: Result<(), Error>, e: BlockError) { + match result { + Err(Error::Block(ref error)) if *error == e => (), + Err(other) => panic!("Block verification failed.\nExpected: {:?}\nGot: {:?}", e, other), + Ok(_) => panic!("Block verification failed.\nExpected: {:?}\nGot: Ok", e), + } + } + + struct TestBlockChain { + blocks: HashMap, + numbers: HashMap, + } + + impl TestBlockChain { + pub fn new() -> TestBlockChain { + TestBlockChain { + blocks: HashMap::new(), + numbers: HashMap::new(), + } + } + + pub fn insert(&mut self, bytes: Bytes) { + let number = BlockView::new(&bytes).header_view().number(); + let hash = BlockView::new(&bytes).header_view().sha3(); + self.blocks.insert(hash.clone(), bytes); + self.numbers.insert(number, hash.clone()); + } + } + + impl BlockProvider for TestBlockChain { + fn is_known(&self, hash: &H256) -> bool { + self.blocks.contains_key(hash) + } + + /// Get raw block data + fn block(&self, hash: &H256) -> Option { + self.blocks.get(hash).map(|b| b.clone()) + } + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option { + self.blocks.get(hash).map(|bytes| { + let header = BlockView::new(bytes).header(); + BlockDetails { + number: header.number, + total_difficulty: header.difficulty, + parent: header.parent_hash, + children: Vec::new(), + } + }) + } + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option { + self.numbers.get(&index).map(|h| h.clone()) + } + } + + #[test] + fn test_verify_block() { + // Test against morden + let mut good = Header::new(); + let spec = ethereum::new_morden(); + let engine = spec.to_engine().unwrap(); + + let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap()); + let min_difficulty = decode(engine.spec().engine_params.get("minimumDifficulty").unwrap()); + good.gas_limit = min_gas_limit; + good.difficulty = min_difficulty; + good.timestamp = 40; + good.number = 10; + + let good_transactions = vec![ vec![ 1u8 ], vec![ 2u8 ] ]; // TODO: proper transactions + + let diff_inc = U256::from(0x40); + + let mut parent6 = good.clone(); + parent6.number = 6; + let mut parent7 = good.clone(); + parent7.number = 7; + parent7.parent_hash = parent6.hash(); + parent7.difficulty = parent6.difficulty + diff_inc; + parent7.timestamp = parent6.timestamp + 10; + let mut parent8 = good.clone(); + parent8.number = 8; + parent8.parent_hash = parent7.hash(); + parent8.difficulty = parent7.difficulty + diff_inc; + parent8.timestamp = parent7.timestamp + 10; + + let mut good_uncle1 = good.clone(); + good_uncle1.number = 9; + good_uncle1.parent_hash = parent8.hash(); + good_uncle1.difficulty = parent8.difficulty + diff_inc; + good_uncle1.timestamp = parent8.timestamp + 10; + good_uncle1.extra_data.push(1u8); + + let mut good_uncle2 = good.clone(); + good_uncle2.number = 8; + good_uncle2.parent_hash = parent7.hash(); + good_uncle2.difficulty = parent7.difficulty + diff_inc; + good_uncle2.timestamp = parent7.timestamp + 10; + good_uncle2.extra_data.push(2u8); + + let good_uncles = vec![ good_uncle1.clone(), good_uncle2.clone() ]; + let mut uncles_rlp = RlpStream::new(); + uncles_rlp.append(&good_uncles); + let good_uncles_hash = uncles_rlp.as_raw().sha3(); + let good_transactions_root = ordered_trie_root(good_transactions.clone()); + + let mut parent = good.clone(); + parent.number = 9; + parent.timestamp = parent8.timestamp + 10; + parent.parent_hash = parent8.hash(); + parent.difficulty = parent8.difficulty + diff_inc; + + good.parent_hash = parent.hash(); + good.difficulty = parent.difficulty + diff_inc; + good.timestamp = parent.timestamp + 10; + + let mut bc = TestBlockChain::new(); + bc.insert(create_test_block(&good)); + bc.insert(create_test_block(&parent)); + bc.insert(create_test_block(&parent6)); + bc.insert(create_test_block(&parent7)); + bc.insert(create_test_block(&parent8)); + + check_ok(verify_block_basic(&create_test_block(&good), engine.deref())); + + let mut header = good.clone(); + header.transactions_root = good_transactions_root.clone(); + header.uncles_hash = good_uncles_hash.clone(); + check_ok(verify_block_basic(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref())); + + header.gas_limit = min_gas_limit - From::from(1); + check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), + InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit })); + + header = good.clone(); + header.number = BlockNumber::max_value(); + check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), + InvalidNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number })); + + header = good.clone(); + header.gas_used = header.gas_limit + From::from(1); + check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), + TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })); + + header = good.clone(); + header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8); + check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), + ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() })); + + header = good.clone(); + header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8); + check_fail(verify_block_basic(&create_test_block(&header), engine.deref()), + ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() })); + + header = good.clone(); + header.uncles_hash = good_uncles_hash.clone(); + check_fail(verify_block_basic(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()), + InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root })); + + header = good.clone(); + header.transactions_root = good_transactions_root.clone(); + check_fail(verify_block_basic(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()), + InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash })); + + check_ok(verify_block_final(&create_test_block(&good), engine.deref(), &bc)); + check_ok(verify_block_final(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine.deref(), &bc)); + + header = good.clone(); + header.parent_hash = H256::random(); + check_fail(verify_block_final(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc), + UnknownParent(header.parent_hash)); + + header = good.clone(); + header.timestamp = 10; + check_fail(verify_block_final(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc), + InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })); + + header = good.clone(); + header.number = 9; + check_fail(verify_block_final(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc), + InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number })); + + header = good.clone(); + let mut bad_uncles = good_uncles.clone(); + bad_uncles.push(good_uncle1.clone()); + check_fail(verify_block_final(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine.deref(), &bc), + TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() })); + + // TODO: some additional uncle checks + } +}