diff --git a/res/ethereum/frontier.json b/res/ethereum/frontier.json index 6394a9010..eaf0ef4c1 100644 --- a/res/ethereum/frontier.json +++ b/res/ethereum/frontier.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xfffa2990", + "frontierCompatibilityModeLimit": "0xDBBA0", "maximumExtraDataSize": "0x20", "tieBreakingGas": false, "minGasLimit": "0x1388", diff --git a/res/ethereum/homestead_test.json b/res/ethereum/homestead_test.json index ee73d0ed3..b11ef9740 100644 --- a/res/ethereum/homestead_test.json +++ b/res/ethereum/homestead_test.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xffffffff", + "frontierCompatibilityModeLimit": "0", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "tieBreakingGas": false, diff --git a/res/ethereum/morden.json b/res/ethereum/morden.json index 79f9f3d99..32fed0cab 100644 --- a/res/ethereum/morden.json +++ b/res/ethereum/morden.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x0100000", - "frontierCompatibilityModeLimit": "0xfffa2990", + "frontierCompatibilityModeLimit": "0xDBBA0", "maximumExtraDataSize": "0x20", "tieBreakingGas": false, "minGasLimit": "0x1388", diff --git a/res/ethereum/olympic.json b/res/ethereum/olympic.json index 4318d9230..b3dfc1ed8 100644 --- a/res/ethereum/olympic.json +++ b/res/ethereum/olympic.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xffffffff", + "frontierCompatibilityModeLimit": "0xffffffffffffffff", "maximumExtraDataSize": "0x0400", "tieBreakingGas": false, "minGasLimit": "125000", diff --git a/src/blockchain.rs b/src/blockchain.rs index 98b3bfbd1..f08d15057 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -252,8 +252,7 @@ impl BlockChain { /// If it doesn't, then rewind down until we find one that does and delete data to ensure that /// later blocks will be reimported. pub fn ensure_good(&mut self, _state: &OverlayDB) { - info!("Rescuing database."); - // TODO. + unimplemented!(); } /// Returns a tree route between `from` and `to`, which is a tuple of: diff --git a/src/client.rs b/src/client.rs index 19a858564..3ee84ccd7 100644 --- a/src/client.rs +++ b/src/client.rs @@ -141,7 +141,7 @@ impl Client { engine.spec().ensure_db_good(&mut state_db); state_db.commit().expect("Error commiting genesis state to state DB"); - chain.write().unwrap().ensure_good(&state_db); +// chain.write().unwrap().ensure_good(&state_db); Ok(Client { chain: chain, diff --git a/src/engine.rs b/src/engine.rs index e76b3b28f..3deec2ab4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -50,6 +50,7 @@ pub trait Engine : Sync + Send { /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. // TODO: consider including State in the params. + fn verify_transaction_basic(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) } fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) } /// Don't forget to call Super::populateFromParent when subclassing & overriding. diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 989ab8cd9..01508353c 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -8,14 +8,28 @@ use evm::Schedule; /// mainnet chains in the Olympic, Frontier and Homestead eras. pub struct Ethash { spec: Spec, + u64_params: RwLock>, + u256_params: RwLock>, } impl Ethash { pub fn new_boxed(spec: Spec) -> Box { - Box::new(Ethash{spec: spec}) + Box::new(Ethash{ + spec: spec, + u64_params: RwLock::new(HashMap::new()), + u256_params: RwLock::new(HashMap::new()), + }) } - fn u256_param(&self, name: &str) -> U256 { self.spec().engine_params.get(name).map(|a| decode(&a)).unwrap_or(U256::from(0u64)) } + fn u64_param(&self, name: &str) -> u64 { + *self.u64_params.write().unwrap().entry(name.to_string()).or_insert_with(|| + self.spec().engine_params.get(name).map(|a| decode(&a)).unwrap_or(0u64)) + } + + fn u256_param(&self, name: &str) -> U256 { + *self.u256_params.write().unwrap().entry(name.to_string()).or_insert_with(|| + self.spec().engine_params.get(name).map(|a| decode(&a)).unwrap_or(x!(0))) + } } impl Engine for Ethash { @@ -29,7 +43,12 @@ impl Engine for Ethash { /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, _header: &Header) -> HashMap { HashMap::new() } fn spec(&self) -> &Spec { &self.spec } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() } + fn schedule(&self, env_info: &EnvInfo) -> Schedule { + match env_info.number < self.u64_param("frontierCompatibilityModeLimit") { + true => Schedule::new_frontier(), + _ => Schedule::new_homestead(), + } + } fn populate_from_parent(&self, header: &mut Header, parent: &Header) { header.difficulty = self.calculate_difficuty(header, parent); @@ -64,12 +83,13 @@ impl Engine for Ethash { fields.state.commit(); } - fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { + fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap()); if header.difficulty < min_difficulty { return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty }))) } // TODO: Verify seal (quick) + Ok(()) } @@ -93,7 +113,12 @@ impl Engine for Ethash { Ok(()) } - fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> result::Result<(), Error> { Ok(()) } + fn verify_transaction_basic(&self, t: &Transaction, header: &Header) -> result::Result<(), Error> { + if header.number() >= self.u64_param("frontierCompatibilityModeLimit") { + try!(t.check_low_s()); + } + Ok(()) + } } impl Ethash { @@ -103,10 +128,10 @@ impl Ethash { panic!("Can't calculate genesis block difficulty"); } - let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap()); - let difficulty_bound_divisor = decode(self.spec().engine_params.get("difficultyBoundDivisor").unwrap()); - let duration_limit: u64 = decode(self.spec().engine_params.get("durationLimit").unwrap()); - let frontier_limit = decode(self.spec().engine_params.get("frontierCompatibilityModeLimit").unwrap()); + let min_difficulty = self.u256_param("minimumDifficulty"); + let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor"); + let duration_limit = self.u64_param("durationLimit"); + let frontier_limit = self.u64_param("frontierCompatibilityModeLimit"); let mut target = if header.number < frontier_limit { if header.timestamp >= parent.timestamp + duration_limit { parent.difficulty - (parent.difficulty / difficulty_bound_divisor) diff --git a/src/service.rs b/src/service.rs index 363cd455e..3bc137c9c 100644 --- a/src/service.rs +++ b/src/service.rs @@ -48,7 +48,8 @@ struct ClientIoHandler { } impl IoHandler for ClientIoHandler { - fn initialize<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>) { } + fn initialize<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>) { + } fn message<'s>(&'s mut self, _io: &mut IoContext<'s, NetSyncMessage>, net_message: &'s mut NetSyncMessage) { match net_message { diff --git a/src/transaction.rs b/src/transaction.rs index 56acf5ab7..4f547a243 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -215,6 +215,15 @@ impl Transaction { Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule) } + /// Checks whether the signature has a low 's' value. + pub fn check_low_s(&self) -> Result<(), Error> { + if !ec::is_low_s(&self.s) { + Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) + } else { + Ok(()) + } + } + /// Do basic validation, checking for valid signature and minimum gas, pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result { if require_low && !ec::is_low_s(&self.s) { diff --git a/src/verification.rs b/src/verification.rs index bb7b912aa..aecee2734 100644 --- a/src/verification.rs +++ b/src/verification.rs @@ -18,6 +18,12 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res try!(verify_header(&u, engine)); try!(engine.verify_block_basic(&u, None)); } + // Verify transactions. + // TODO: either use transaction views or cache the decoded transactions. + let v = BlockView::new(bytes); + for t in v.transactions() { + try!(engine.verify_transaction_basic(&t, &header)); + } Ok(()) } @@ -29,6 +35,12 @@ pub fn verify_block_unordered(header: &Header, bytes: &[u8], engine: &Engine) -> for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::
()) { try!(engine.verify_block_unordered(&u, None)); } + // Verify transactions. + // TODO: pass in pre-recovered transactions - maybe verify_transaction wants to call `sender()`. + let v = BlockView::new(bytes); + for t in v.transactions() { + try!(engine.verify_transaction(&t, &header)); + } Ok(()) } diff --git a/src/views.rs b/src/views.rs index 7a1a20e9f..6c616774d 100644 --- a/src/views.rs +++ b/src/views.rs @@ -3,6 +3,64 @@ use util::*; use header::*; use transaction::*; +/// View onto transaction rlp. +pub struct TransactionView<'a> { + rlp: Rlp<'a> +} + +impl<'a> TransactionView<'a> { + /// Creates new view onto block from raw bytes. + pub fn new(bytes: &'a [u8]) -> TransactionView<'a> { + TransactionView { + rlp: Rlp::new(bytes) + } + } + + /// Creates new view onto block from rlp. + pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> { + TransactionView { + rlp: rlp + } + } + + /// Return reference to underlaying rlp. + pub fn rlp(&self) -> &Rlp<'a> { + &self.rlp + } + + /// Get the nonce field of the transaction. + pub fn nonce(&self) -> U256 { self.rlp.val_at(0) } + + /// Get the gas_price field of the transaction. + pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) } + + /// Get the gas field of the transaction. + pub fn gas(&self) -> U256 { self.rlp.val_at(2) } + + /// Get the value field of the transaction. + pub fn value(&self) -> U256 { self.rlp.val_at(4) } + + /// Get the data field of the transaction. + pub fn data(&self) -> Bytes { self.rlp.val_at(5) } + + /// Get the v field of the transaction. + pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 } + + /// Get the r field of the transaction. + pub fn r(&self) -> U256 { self.rlp.val_at(7) } + + /// Get the s field of the transaction. + pub fn s(&self) -> U256 { self.rlp.val_at(8) } + + // TODO: something like pub fn action(&self) -> Action { self.rlp.val_at(3) } +} + +impl<'a> Hashable for TransactionView<'a> { + fn sha3(&self) -> H256 { + self.rlp.as_raw().sha3() + } +} + /// View onto block rlp. pub struct BlockView<'a> { rlp: Rlp<'a> @@ -38,6 +96,11 @@ impl<'a> BlockView<'a> { HeaderView::new_from_rlp(self.rlp.at(0)) } + /// Return List of transactions in given block. + pub fn transaction_views(&self) -> Vec { + self.rlp.at(1).iter().map(|rlp| TransactionView::new_from_rlp(rlp)).collect() + } + /// Return List of transactions in given block. pub fn transactions(&self) -> Vec { self.rlp.val_at(1)