From dd5fcb398f2d3ec7f28e17c22f2ff5b9612443c3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 16 Jan 2016 18:30:27 +0100 Subject: [PATCH 1/7] Support Homestead, --- src/blockchain.rs | 3 +-- src/client.rs | 2 +- src/engine.rs | 1 + src/ethereum/ethash.rs | 43 ++++++++++++++++++++++++------- src/service.rs | 3 ++- src/transaction.rs | 9 +++++++ src/verification.rs | 12 +++++++++ src/views.rs | 57 +++++++++++++++++++++++++++++++++++++++++- 8 files changed, 116 insertions(+), 14 deletions(-) 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 3eda8d1af..6e74f21a0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -138,7 +138,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..95cc1ed8b 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 da1f65f88..d6df3cf0a 100644 --- a/src/service.rs +++ b/src/service.rs @@ -38,7 +38,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..efccb33c4 100644 --- a/src/views.rs +++ b/src/views.rs @@ -2,7 +2,57 @@ use util::*; use header::*; use transaction::*; +/* +/// View onto block 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> { + BlockView { + 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 + } + + pub fn nonce(&self) -> U256 { self.rlp.val_at(0) } + + pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) } + + pub fn gas(&self) -> U256 { self.rlp.val_at(2) } + + pub fn action(&self) -> Action { self.rlp.val_at(3) } + + pub fn value(&self) -> U256 { self.rlp.val_at(4) } + + pub fn data(&self) -> Bytes { self.rlp.val_at(5) } + + pub fn v(&self) -> u8 { self.rlp.val_at(6) } + + pub fn r(&self) -> U256 { self.rlp.val_at(7) } + + pub fn s(&self) -> U256 { self.rlp.val_at(8) } +} + +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> @@ -37,7 +87,12 @@ impl<'a> BlockView<'a> { pub fn header_view(&self) -> HeaderView<'a> { HeaderView::new_from_rlp(self.rlp.at(0)) } - +/* + /// Return List of transactions in given block. + pub fn transactions(&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) From d8efbb2173bd9940ffb66460627d587161015247 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 16 Jan 2016 18:35:07 +0100 Subject: [PATCH 2/7] Change netspecs to real values. --- res/ethereum/frontier.json | 2 +- res/ethereum/homestead_test.json | 2 +- res/ethereum/morden.json | 2 +- res/ethereum/olympic.json | 2 +- src/ethereum/ethash.rs | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/res/ethereum/frontier.json b/res/ethereum/frontier.json index 6394a9010..75a0e0bc3 100644 --- a/res/ethereum/frontier.json +++ b/res/ethereum/frontier.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xfffa2990", + "frontierCompatibilityModeLimit": "0xf4240", "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..6897b7fcb 100644 --- a/res/ethereum/morden.json +++ b/res/ethereum/morden.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x0100000", - "frontierCompatibilityModeLimit": "0xfffa2990", + "frontierCompatibilityModeLimit": "0xf4240", "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/ethereum/ethash.rs b/src/ethereum/ethash.rs index 95cc1ed8b..750b8297c 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -132,6 +132,7 @@ impl Ethash { let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor"); let duration_limit = self.u64_param("durationLimit"); let frontier_limit = self.u64_param("frontierCompatibilityModeLimit"); + info!("Frontier limit: {}", frontier_limit); let mut target = if header.number < frontier_limit { if header.timestamp >= parent.timestamp + duration_limit { parent.difficulty - (parent.difficulty / difficulty_bound_divisor) From 60f0ff8e9e6302296d6c1b331a750c85917b79c0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 16 Jan 2016 18:46:23 +0100 Subject: [PATCH 3/7] Fix bug. --- src/ethereum/ethash.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 750b8297c..01508353c 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -22,12 +22,12 @@ impl Ethash { } fn u64_param(&self, name: &str) -> u64 { - *self.u64_params.write().unwrap().entry("name".to_string()).or_insert_with(|| + *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.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))) } } @@ -132,7 +132,6 @@ impl Ethash { let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor"); let duration_limit = self.u64_param("durationLimit"); let frontier_limit = self.u64_param("frontierCompatibilityModeLimit"); - info!("Frontier limit: {}", frontier_limit); let mut target = if header.number < frontier_limit { if header.timestamp >= parent.timestamp + duration_limit { parent.difficulty - (parent.difficulty / difficulty_bound_divisor) From 86a6fa95a66b9b29178bead4f1fc977d38ce5e2e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 16 Jan 2016 19:09:36 +0100 Subject: [PATCH 4/7] Minor fixes to JSON files. --- res/ethereum/frontier.json | 2 +- res/ethereum/morden.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/ethereum/frontier.json b/res/ethereum/frontier.json index 75a0e0bc3..eaf0ef4c1 100644 --- a/res/ethereum/frontier.json +++ b/res/ethereum/frontier.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x00", - "frontierCompatibilityModeLimit": "0xf4240", + "frontierCompatibilityModeLimit": "0xDBBA0", "maximumExtraDataSize": "0x20", "tieBreakingGas": false, "minGasLimit": "0x1388", diff --git a/res/ethereum/morden.json b/res/ethereum/morden.json index 6897b7fcb..32fed0cab 100644 --- a/res/ethereum/morden.json +++ b/res/ethereum/morden.json @@ -3,7 +3,7 @@ "engineName": "Ethash", "params": { "accountStartNonce": "0x0100000", - "frontierCompatibilityModeLimit": "0xf4240", + "frontierCompatibilityModeLimit": "0xDBBA0", "maximumExtraDataSize": "0x20", "tieBreakingGas": false, "minGasLimit": "0x1388", From cf1e9f24b6a975f92bbe6355af5d466e40ed49ea Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 16 Jan 2016 21:01:02 +0100 Subject: [PATCH 5/7] Enable TransactionView. --- src/views.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/views.rs b/src/views.rs index efccb33c4..55e304ef4 100644 --- a/src/views.rs +++ b/src/views.rs @@ -2,7 +2,7 @@ use util::*; use header::*; use transaction::*; -/* + /// View onto block rlp. pub struct TransactionView<'a> { rlp: Rlp<'a> @@ -11,7 +11,7 @@ pub struct TransactionView<'a> { impl<'a> TransactionView<'a> { /// Creates new view onto block from raw bytes. pub fn new(bytes: &'a [u8]) -> TransactionView<'a> { - BlockView { + TransactionView { rlp: Rlp::new(bytes) } } @@ -34,13 +34,13 @@ impl<'a> TransactionView<'a> { pub fn gas(&self) -> U256 { self.rlp.val_at(2) } - pub fn action(&self) -> Action { self.rlp.val_at(3) } + // TODO: something like pub fn action(&self) -> Action { self.rlp.val_at(3) } pub fn value(&self) -> U256 { self.rlp.val_at(4) } pub fn data(&self) -> Bytes { self.rlp.val_at(5) } - pub fn v(&self) -> u8 { self.rlp.val_at(6) } + pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 } pub fn r(&self) -> U256 { self.rlp.val_at(7) } @@ -52,7 +52,7 @@ impl<'a> Hashable for TransactionView<'a> { self.rlp.as_raw().sha3() } } -*/ + /// View onto block rlp. pub struct BlockView<'a> { rlp: Rlp<'a> From bffbba32d29acd2b32ad81d4730f6957b1ee1981 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 16 Jan 2016 21:02:31 +0100 Subject: [PATCH 6/7] Docs. --- src/views.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/views.rs b/src/views.rs index 55e304ef4..999187445 100644 --- a/src/views.rs +++ b/src/views.rs @@ -3,7 +3,7 @@ use util::*; use header::*; use transaction::*; -/// View onto block rlp. +/// View onto transaction rlp. pub struct TransactionView<'a> { rlp: Rlp<'a> } @@ -28,23 +28,31 @@ impl<'a> TransactionView<'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) } - // TODO: something like pub fn action(&self) -> Action { self.rlp.val_at(3) } - + /// 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> { From cbdc2ec80db86fe9648db82a8fc42e5d06e5eecd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 16 Jan 2016 21:04:14 +0100 Subject: [PATCH 7/7] Enable TransactionViews from the BlockView. --- src/views.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views.rs b/src/views.rs index 999187445..6c616774d 100644 --- a/src/views.rs +++ b/src/views.rs @@ -95,12 +95,12 @@ impl<'a> BlockView<'a> { pub fn header_view(&self) -> HeaderView<'a> { HeaderView::new_from_rlp(self.rlp.at(0)) } -/* + /// Return List of transactions in given block. - pub fn transactions(&self) -> Vec { + 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)