diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json
index a8a26b1c3..a57ad8ed8 100644
--- a/ethcore/res/ethereum/frontier.json
+++ b/ethcore/res/ethereum/frontier.json
@@ -3,19 +3,18 @@
"engine": {
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
+ "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
+ "frontierCompatibilityModeLimit": "0x118c30"
}
}
},
"params": {
"accountStartNonce": "0x00",
- "frontierCompatibilityModeLimit": "0x118c30",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json
index fec7c4371..ca31927e7 100644
--- a/ethcore/res/ethereum/frontier_like_test.json
+++ b/ethcore/res/ethereum/frontier_like_test.json
@@ -3,19 +3,18 @@
"engine": {
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
+ "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
+ "frontierCompatibilityModeLimit": "0x118c30"
}
}
},
"params": {
"accountStartNonce": "0x00",
- "frontierCompatibilityModeLimit": "0x118c30",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json
index 4f0284d14..3964d33ad 100644
--- a/ethcore/res/ethereum/frontier_test.json
+++ b/ethcore/res/ethereum/frontier_test.json
@@ -3,19 +3,18 @@
"engine": {
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
+ "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
+ "frontierCompatibilityModeLimit": "0xffffffffffffffff"
}
}
},
"params": {
"accountStartNonce": "0x00",
- "frontierCompatibilityModeLimit": "0xffffffffffffffff",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json
index 962592404..a0e3df275 100644
--- a/ethcore/res/ethereum/homestead_test.json
+++ b/ethcore/res/ethereum/homestead_test.json
@@ -3,19 +3,18 @@
"engine": {
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
+ "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
+ "frontierCompatibilityModeLimit": 0
}
}
},
"params": {
"accountStartNonce": "0x00",
- "frontierCompatibilityModeLimit": 0,
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json
index 0723e2e9d..1d1dec0b7 100644
--- a/ethcore/res/ethereum/morden.json
+++ b/ethcore/res/ethereum/morden.json
@@ -3,19 +3,18 @@
"engine": {
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar": ""
+ "registrar": "",
+ "frontierCompatibilityModeLimit": "0x789b0"
}
}
},
"params": {
"accountStartNonce": "0x0100000",
- "frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2"
diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json
index 8ee9f1d66..1e0c210b5 100644
--- a/ethcore/res/ethereum/olympic.json
+++ b/ethcore/res/ethereum/olympic.json
@@ -3,19 +3,18 @@
"engine": {
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x08",
"blockReward": "0x14D1120D7B160000",
- "registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
+ "registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
+ "frontierCompatibilityModeLimit": "0xffffffffffffffff"
}
}
},
"params": {
"accountStartNonce": "0x00",
- "frontierCompatibilityModeLimit": "0xffffffffffffffff",
"maximumExtraDataSize": "0x0400",
"minGasLimit": "125000",
"networkID" : "0x0"
diff --git a/ethcore/res/null_morden.json b/ethcore/res/null_morden.json
index 48631a7ce..c820579d0 100644
--- a/ethcore/res/null_morden.json
+++ b/ethcore/res/null_morden.json
@@ -5,7 +5,6 @@
},
"params": {
"accountStartNonce": "0x0100000",
- "frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2"
diff --git a/ethcore/res/null_homestead_morden.json b/ethcore/res/test_authority.json
similarity index 74%
rename from ethcore/res/null_homestead_morden.json
rename to ethcore/res/test_authority.json
index 145540853..1ab482863 100644
--- a/ethcore/res/null_homestead_morden.json
+++ b/ethcore/res/test_authority.json
@@ -1,20 +1,25 @@
{
- "name": "Morden",
+ "name": "TestAuthority",
"engine": {
- "Null": null
+ "BasicAuthority": {
+ "params": {
+ "gasLimitBoundDivisor": "0x0400",
+ "durationLimit": "0x0d",
+ "authorities" : ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"]
+ }
+ }
},
"params": {
"accountStartNonce": "0x0100000",
- "frontierCompatibilityModeLimit": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
- "networkID" : "0x2"
+ "networkID" : "0x69"
},
"genesis": {
"seal": {
- "ethereum": {
- "nonce": "0x00006d6f7264656e",
- "mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
+ "generic": {
+ "fields": 1,
+ "rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
}
},
"difficulty": "0x20000",
@@ -29,6 +34,6 @@
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
- "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
+ "9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
}
}
diff --git a/ethcore/src/basic_authority.rs b/ethcore/src/basic_authority.rs
new file mode 100644
index 000000000..af0bcb207
--- /dev/null
+++ b/ethcore/src/basic_authority.rs
@@ -0,0 +1,297 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
+
+use common::*;
+use util::keys::store::AccountProvider;
+use block::*;
+use spec::{CommonParams, Spec};
+use engine::*;
+use evm::{Schedule, Factory};
+use ethjson;
+
+/// BasicAuthority params.
+#[derive(Debug, PartialEq)]
+pub struct BasicAuthorityParams {
+ /// Gas limit divisor.
+ pub gas_limit_bound_divisor: U256,
+ /// Block duration.
+ pub duration_limit: u64,
+ /// Valid signatories.
+ pub authorities: HashSet
,
+}
+
+impl From for BasicAuthorityParams {
+ fn from(p: ethjson::spec::BasicAuthorityParams) -> Self {
+ BasicAuthorityParams {
+ gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
+ duration_limit: p.duration_limit.into(),
+ authorities: p.authorities.into_iter().map(Into::into).collect::>(),
+ }
+ }
+}
+
+/// Engine using BasicAuthority proof-of-work consensus algorithm, suitable for Ethereum
+/// mainnet chains in the Olympic, Frontier and Homestead eras.
+pub struct BasicAuthority {
+ params: CommonParams,
+ our_params: BasicAuthorityParams,
+ builtins: BTreeMap,
+ factory: Factory,
+}
+
+impl BasicAuthority {
+ /// Create a new instance of BasicAuthority engine
+ pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap) -> Self {
+ BasicAuthority {
+ params: params,
+ our_params: our_params,
+ builtins: builtins,
+ factory: Factory::default(),
+ }
+ }
+}
+
+impl Engine for BasicAuthority {
+ fn name(&self) -> &str { "BasicAuthority" }
+ fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
+ // One field - the signature
+ fn seal_fields(&self) -> usize { 1 }
+
+ fn params(&self) -> &CommonParams { &self.params }
+ fn builtins(&self) -> &BTreeMap { &self.builtins }
+
+ /// Additional engine-specific information for the user/developer concerning `header`.
+ fn extra_info(&self, _header: &Header) -> HashMap { hash_map!["signature".to_owned() => "TODO".to_owned()] }
+
+ fn vm_factory(&self) -> &Factory { &self.factory }
+
+ fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
+ Schedule::new_homestead()
+ }
+
+ fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256) {
+ header.difficulty = parent.difficulty;
+ header.gas_limit = {
+ let gas_limit = parent.gas_limit;
+ let bound_divisor = self.our_params.gas_limit_bound_divisor;
+ if gas_limit < gas_floor_target {
+ min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
+ } else {
+ max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1))
+ }
+ };
+ header.note_dirty();
+// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
+ }
+
+ /// Apply the block reward on finalisation of the block.
+ /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
+ fn on_close_block(&self, _block: &mut ExecutedBlock) {}
+
+ /// Attempt to seal the block internally.
+ ///
+ /// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
+ /// be returned.
+ fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option> {
+ if let Some(ap) = accounts {
+ // check to see if author is contained in self.our_params.authorities
+ if self.our_params.authorities.contains(block.header().author()) {
+ if let Ok(secret) = ap.account_secret(block.header().author()) {
+ return Some(block.header().author_seal(&secret));
+ } else {
+ trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
+ }
+ } else {
+ trace!(target: "basicauthority", "generate_seal: FAIL: block author {} isn't one of the authorized accounts {:?}", block.header().author(), self.our_params.authorities);
+ }
+ } else {
+ trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided");
+ }
+ None
+ }
+
+ fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
+ // check the seal fields.
+ // TODO: pull this out into common code.
+ if header.seal.len() != self.seal_fields() {
+ return Err(From::from(BlockError::InvalidSealArity(
+ Mismatch { expected: self.seal_fields(), found: header.seal.len() }
+ )));
+ }
+ Ok(())
+ }
+
+ fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
+ // check the signature is legit.
+ let sig = try!(UntrustedRlp::new(&header.seal[0]).as_val::());
+ let signer = Address::from(try!(ec::recover(&sig, &header.bare_hash())).sha3());
+ if !self.our_params.authorities.contains(&signer) {
+ return try!(Err(BlockError::InvalidSeal));
+ }
+ Ok(())
+ }
+
+ fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
+ // we should not calculate difficulty for genesis blocks
+ if header.number() == 0 {
+ return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
+ }
+
+ // Check difficulty is correct given the two timestamps.
+ if header.difficulty() != parent.difficulty() {
+ return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
+ }
+ let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
+ let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
+ let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor;
+ if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
+ return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
+ }
+ Ok(())
+ }
+
+ fn verify_transaction_basic(&self, t: &SignedTransaction, _header: &Header) -> result::Result<(), Error> {
+ try!(t.check_low_s());
+ Ok(())
+ }
+
+ fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
+ t.sender().map(|_|()) // Perform EC recovery and cache sender
+ }
+}
+
+impl Header {
+ /// Get the none field of the header.
+ pub fn signature(&self) -> H520 {
+ decode(&self.seal()[0])
+ }
+
+ /// Generate a seal for the block with the given `secret`.
+ pub fn author_seal(&self, secret: &Secret) -> Vec {
+ vec![encode(&ec::sign(secret, &self.bare_hash()).unwrap_or(Signature::new())).to_vec()]
+ }
+
+ /// Set the nonce and mix hash fields of the header.
+ pub fn sign(&mut self, secret: &Secret) {
+ self.seal = self.author_seal(secret);
+ }
+}
+
+/// Create a new test chain spec with BasicAuthority consensus engine.
+pub fn new_test_authority() -> Spec { Spec::load(include_bytes!("../res/test_authority.json")) }
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use common::*;
+ use block::*;
+ use engine::*;
+ use tests::helpers::*;
+ use util::keys::{TestAccountProvider, TestAccount};
+
+ #[test]
+ fn has_valid_metadata() {
+ let engine = new_test_authority().engine;
+ assert!(!engine.name().is_empty());
+ assert!(engine.version().major >= 1);
+ }
+
+ #[test]
+ fn can_return_factory() {
+ let engine = new_test_authority().engine;
+ engine.vm_factory();
+ }
+
+ #[test]
+ fn can_return_schedule() {
+ let engine = new_test_authority().engine;
+ let schedule = engine.schedule(&EnvInfo {
+ number: 10000000,
+ author: x!(0),
+ timestamp: 0,
+ difficulty: x!(0),
+ last_hashes: vec![],
+ gas_used: x!(0),
+ gas_limit: x!(0)
+ });
+
+ assert!(schedule.stack_limit > 0);
+ }
+
+ #[test]
+ fn can_do_seal_verification_fail() {
+ let engine = new_test_authority().engine;
+ let header: Header = Header::default();
+
+ let verify_result = engine.verify_block_basic(&header, None);
+
+ match verify_result {
+ Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
+ Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
+ _ => { panic!("Should be error, got Ok"); },
+ }
+ }
+
+ #[test]
+ fn can_do_signature_verification_fail() {
+ let engine = new_test_authority().engine;
+ let mut header: Header = Header::default();
+ header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]);
+
+ let verify_result = engine.verify_block_unordered(&header, None);
+
+ match verify_result {
+ Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => {},
+ Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); },
+ _ => { panic!("Should be error, got Ok"); },
+ }
+ }
+
+ #[test]
+ fn can_do_signature_verification() {
+ let secret = "".sha3();
+ let addr = KeyPair::from_secret("".sha3()).unwrap().address();
+
+ let engine = new_test_authority().engine;
+ let mut header: Header = Header::default();
+ header.set_author(addr);
+ header.sign(&secret);
+
+ assert!(engine.verify_block_unordered(&header, None).is_ok());
+ }
+
+ #[test]
+ fn can_generate_seal() {
+ let addr = KeyPair::from_secret("".sha3()).unwrap().address();
+ let accounts = hash_map![addr => TestAccount{unlocked: true, password: Default::default(), secret: "".sha3()}];
+ let tap = TestAccountProvider::new(accounts);
+
+ let spec = new_test_authority();
+ let engine = &spec.engine;
+ let genesis_header = spec.genesis_header();
+ let mut db_result = get_temp_journal_db();
+ let mut db = db_result.take();
+ spec.ensure_db_good(db.as_hashdb_mut());
+ let last_hashes = vec![genesis_header.hash()];
+ let b = OpenBlock::new(engine.deref(), false, db, &genesis_header, last_hashes, addr.clone(), x!(3141562), vec![]);
+ let b = b.close_and_lock();
+ let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
+
+ assert!(b.try_seal(engine.deref(), seal).is_ok());
+ }
+}
diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs
index 782a20f52..17b0e7071 100644
--- a/ethcore/src/block.rs
+++ b/ethcore/src/block.rs
@@ -89,7 +89,7 @@ pub struct ExecutedBlock {
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
pub struct BlockRefMut<'a> {
/// Block header.
- pub header: &'a Header,
+ pub header: &'a mut Header,
/// Block transactions.
pub transactions: &'a Vec,
/// Block uncles.
@@ -133,7 +133,7 @@ impl ExecutedBlock {
/// Get a structure containing individual references to all public fields.
pub fn fields_mut(&mut self) -> BlockRefMut {
BlockRefMut {
- header: &self.base.header,
+ header: &mut self.base.header,
transactions: &self.base.transactions,
uncles: &self.base.uncles,
state: &mut self.state,
diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs
index 19802a93c..ed5460ffd 100644
--- a/ethcore/src/client/client.rs
+++ b/ethcore/src/client/client.rs
@@ -42,8 +42,7 @@ use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt;
pub use blockchain::CacheSize as BlockChainCacheSize;
-use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase, Filter as
- TracedbFilter};
+use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase, Filter as TracedbFilter};
use trace;
/// General block status
diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs
index ebfb2a791..90dd78015 100644
--- a/ethcore/src/client/mod.rs
+++ b/ethcore/src/client/mod.rs
@@ -34,9 +34,10 @@ use std::collections::HashSet;
use util::bytes::Bytes;
use util::hash::{Address, H256, H2048};
use util::numbers::U256;
+use util::keys::store::AccountProvider;
use blockchain::TreeRoute;
use block_queue::BlockQueueInfo;
-use block::{ClosedBlock, LockedBlock, SealedBlock};
+use block::{ExecutedBlock, ClosedBlock, LockedBlock, SealedBlock};
use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry;
@@ -134,6 +135,9 @@ pub trait BlockChainClient : Sync + Send {
/// Makes a non-persistent transaction call.
fn call(&self, t: &SignedTransaction) -> Result;
+ /// Attempt to seal the block internally. See `Engine`.
+ fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option> { self.engine().generate_seal(block, accounts) }
+
/// Executes a function providing it with a reference to an engine.
fn engine(&self) -> &Engine;
diff --git a/ethcore/src/engine.rs b/ethcore/src/engine.rs
index 74b9dff26..344144c6e 100644
--- a/ethcore/src/engine.rs
+++ b/ethcore/src/engine.rs
@@ -15,10 +15,10 @@
// along with Parity. If not, see .
use common::*;
+use util::keys::store::AccountProvider;
use block::ExecutedBlock;
use spec::CommonParams;
-use evm::Schedule;
-use evm::Factory;
+use evm::{Schedule, Factory};
/// 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.
@@ -61,6 +61,14 @@ pub trait Engine : Sync + Send {
/// Block transformation functions, after the transactions.
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
+ /// Attempt to seal the block internally.
+ ///
+ /// If `Some` is returned, then you get a valid seal.
+ ///
+ /// This operation is synchronous and may (quite reasonably) not be available, in which None will
+ /// be returned.
+ fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option> { None }
+
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs
index d74e225f2..922d72700 100644
--- a/ethcore/src/error.rs
+++ b/ethcore/src/error.rs
@@ -137,6 +137,8 @@ pub enum BlockError {
MismatchedH256SealElement(Mismatch),
/// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid.
InvalidProofOfWork(OutOfBounds),
+ /// Some low-level aspect of the seal in incorrect.
+ InvalidSeal,
/// Gas limit header field is invalid.
InvalidGasLimit(OutOfBounds),
/// Receipts trie root header field is invalid.
diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs
index 9d4a87eed..11b13c0c1 100644
--- a/ethcore/src/ethereum/ethash.rs
+++ b/ethcore/src/ethereum/ethash.rs
@@ -27,8 +27,6 @@ use ethjson;
/// Ethash params.
#[derive(Debug, PartialEq)]
pub struct EthashParams {
- /// Tie breaking gas.
- pub tie_breaking_gas: bool,
/// Gas limit divisor.
pub gas_limit_bound_divisor: U256,
/// Minimum difficulty.
@@ -41,18 +39,20 @@ pub struct EthashParams {
pub block_reward: U256,
/// Namereg contract address.
pub registrar: Address,
+ /// Homestead transition block number.
+ pub frontier_compatibility_mode_limit: u64,
}
impl From for EthashParams {
fn from(p: ethjson::spec::EthashParams) -> Self {
EthashParams {
- tie_breaking_gas: p.tie_breaking_gas,
gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
minimum_difficulty: p.minimum_difficulty.into(),
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
duration_limit: p.duration_limit.into(),
block_reward: p.block_reward.into(),
registrar: p.registrar.into(),
+ frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
}
}
}
@@ -100,9 +100,9 @@ impl Engine for Ethash {
}
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
- trace!(target: "client", "Creating schedule. fCML={}", self.params.frontier_compatibility_mode_limit);
+ trace!(target: "client", "Creating schedule. fCML={}", self.ethash_params.frontier_compatibility_mode_limit);
- if env_info.number < self.params.frontier_compatibility_mode_limit {
+ if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
Schedule::new_frontier()
} else {
Schedule::new_homestead()
@@ -207,7 +207,7 @@ impl Engine for Ethash {
}
fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
- if header.number() >= self.params.frontier_compatibility_mode_limit {
+ if header.number() >= self.ethash_params.frontier_compatibility_mode_limit {
try!(t.check_low_s());
}
Ok(())
@@ -229,7 +229,7 @@ impl Ethash {
let min_difficulty = self.ethash_params.minimum_difficulty;
let difficulty_bound_divisor = self.ethash_params.difficulty_bound_divisor;
let duration_limit = self.ethash_params.duration_limit;
- let frontier_limit = self.params.frontier_compatibility_mode_limit;
+ let frontier_limit = self.ethash_params.frontier_compatibility_mode_limit;
let mut target = if header.number < frontier_limit {
if header.timestamp >= parent.timestamp + duration_limit {
diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs
index a7b9125fc..43b4c5099 100644
--- a/ethcore/src/lib.rs
+++ b/ethcore/src/lib.rs
@@ -89,6 +89,7 @@ extern crate bloomchain;
#[cfg(test)] extern crate ethcore_devtools as devtools;
#[cfg(feature = "jit" )] extern crate evmjit;
+pub mod basic_authority;
pub mod block;
pub mod block_queue;
pub mod client;
diff --git a/ethcore/src/null_engine.rs b/ethcore/src/null_engine.rs
index 505ff5bf6..58ae5baa9 100644
--- a/ethcore/src/null_engine.rs
+++ b/ethcore/src/null_engine.rs
@@ -57,11 +57,7 @@ impl Engine for NullEngine {
&self.builtins
}
- fn schedule(&self, env_info: &EnvInfo) -> Schedule {
- if env_info.number < self.params.frontier_compatibility_mode_limit {
- Schedule::new_frontier()
- } else {
- Schedule::new_homestead()
- }
+ fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
+ Schedule::new_homestead()
}
}
diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs
index 353232ef6..743f1bd9b 100644
--- a/ethcore/src/spec/spec.rs
+++ b/ethcore/src/spec/spec.rs
@@ -24,6 +24,7 @@ use account_db::*;
use super::genesis::Genesis;
use super::seal::Generic as GenericSeal;
use ethereum;
+use basic_authority::BasicAuthority;
use ethjson;
/// Parameters common to all engines.
@@ -31,8 +32,6 @@ use ethjson;
pub struct CommonParams {
/// Account start nonce.
pub account_start_nonce: U256,
- /// Frontier compatibility mode limit.
- pub frontier_compatibility_mode_limit: u64,
/// Maximum size of extra data.
pub maximum_extra_data_size: usize,
/// Network id.
@@ -45,7 +44,6 @@ impl From for CommonParams {
fn from(p: ethjson::spec::Params) -> Self {
CommonParams {
account_start_nonce: p.account_start_nonce.into(),
- frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
maximum_extra_data_size: p.maximum_extra_data_size.into(),
network_id: p.network_id.into(),
min_gas_limit: p.min_gas_limit.into(),
@@ -131,7 +129,8 @@ impl Spec {
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap) -> Box {
match engine_spec {
ethjson::spec::Engine::Null => Box::new(NullEngine::new(params, builtins)),
- ethjson::spec::Engine::Ethash(ethash) => Box::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins))
+ ethjson::spec::Engine::Ethash(ethash) => Box::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins)),
+ ethjson::spec::Engine::BasicAuthority(basic_authority) => Box::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)),
}
}
@@ -241,15 +240,10 @@ impl Spec {
From::from(ethjson::spec::Spec::load(reader).expect("invalid json file"))
}
- /// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
+ /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus.
pub fn new_test() -> Spec {
Spec::load(include_bytes!("../../res/null_morden.json"))
}
-
- /// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
- pub fn new_homestead_test() -> Spec {
- Spec::load(include_bytes!("../../res/null_homestead_morden.json"))
- }
}
#[cfg(test)]
diff --git a/json/src/spec/basic_authority.rs b/json/src/spec/basic_authority.rs
new file mode 100644
index 000000000..967fb024a
--- /dev/null
+++ b/json/src/spec/basic_authority.rs
@@ -0,0 +1,59 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+//! Ethash params deserialization.
+
+use uint::Uint;
+use hash::Address;
+
+/// Ethash params deserialization.
+#[derive(Debug, PartialEq, Deserialize)]
+pub struct BasicAuthorityParams {
+ /// Gas limit divisor.
+ #[serde(rename="gasLimitBoundDivisor")]
+ pub gas_limit_bound_divisor: Uint,
+ /// Block duration.
+ #[serde(rename="durationLimit")]
+ pub duration_limit: Uint,
+ /// Valid authorities
+ pub authorities: Vec,
+}
+
+/// Ethash engine deserialization.
+#[derive(Debug, PartialEq, Deserialize)]
+pub struct BasicAuthority {
+ /// Ethash params.
+ pub params: BasicAuthorityParams,
+}
+
+#[cfg(test)]
+mod tests {
+ use serde_json;
+ use spec::basic_authority::BasicAuthority;
+
+ #[test]
+ fn basic_authority_deserialization() {
+ let s = r#"{
+ "params": {
+ "gasLimitBoundDivisor": "0x0400",
+ "durationLimit": "0x0d",
+ "authorities" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"]
+ }
+ }"#;
+
+ let _deserialized: BasicAuthority = serde_json::from_str(s).unwrap();
+ }
+}
diff --git a/json/src/spec/engine.rs b/json/src/spec/engine.rs
index e5f5dc718..7d81aaa5a 100644
--- a/json/src/spec/engine.rs
+++ b/json/src/spec/engine.rs
@@ -19,6 +19,7 @@
use serde::Deserializer;
use serde::de::Visitor;
use spec::Ethash;
+use spec::BasicAuthority;
/// Engine deserialization.
#[derive(Debug, PartialEq, Deserialize)]
@@ -27,6 +28,8 @@ pub enum Engine {
Null,
/// Ethash engine.
Ethash(Ethash),
+ /// BasicAuthority engine.
+ BasicAuthority(BasicAuthority),
}
#[cfg(test)]
@@ -46,13 +49,13 @@ mod tests {
let s = r#"{
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
+ "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
+ "frontierCompatibilityModeLimit" : "0x"
}
}
}"#;
diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs
index 85f855dde..254ffff9b 100644
--- a/json/src/spec/ethash.rs
+++ b/json/src/spec/ethash.rs
@@ -22,9 +22,6 @@ use hash::Address;
/// Ethash params deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub struct EthashParams {
- /// Tie breaking gas.
- #[serde(rename="tieBreakingGas")]
- pub tie_breaking_gas: bool,
/// Gas limit divisor.
#[serde(rename="gasLimitBoundDivisor")]
pub gas_limit_bound_divisor: Uint,
@@ -42,6 +39,9 @@ pub struct EthashParams {
pub block_reward: Uint,
/// Namereg contract address.
pub registrar: Address,
+ /// Homestead transition block number.
+ #[serde(rename="frontierCompatibilityModeLimit")]
+ pub frontier_compatibility_mode_limit: Uint,
}
/// Ethash engine deserialization.
@@ -60,13 +60,13 @@ mod tests {
fn ethash_deserialization() {
let s = r#"{
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
+ "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
+ "frontierCompatibilityModeLimit" : "0x42"
}
}"#;
diff --git a/json/src/spec/mod.rs b/json/src/spec/mod.rs
index 72ea2a42e..f6c856b13 100644
--- a/json/src/spec/mod.rs
+++ b/json/src/spec/mod.rs
@@ -25,6 +25,7 @@ pub mod seal;
pub mod engine;
pub mod state;
pub mod ethash;
+pub mod basic_authority;
pub use self::account::Account;
pub use self::builtin::{Builtin, Pricing, Linear};
@@ -35,3 +36,4 @@ pub use self::seal::{Seal, Ethereum, Generic};
pub use self::engine::Engine;
pub use self::state::State;
pub use self::ethash::{Ethash, EthashParams};
+pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams};
diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs
index 2370368ba..8a953bb89 100644
--- a/json/src/spec/params.rs
+++ b/json/src/spec/params.rs
@@ -24,9 +24,6 @@ pub struct Params {
/// Account start nonce.
#[serde(rename="accountStartNonce")]
pub account_start_nonce: Uint,
- /// Homestead transition block number.
- #[serde(rename="frontierCompatibilityModeLimit")]
- pub frontier_compatibility_mode_limit: Uint,
/// Maximum size of extra data.
#[serde(rename="maximumExtraDataSize")]
pub maximum_extra_data_size: Uint,
diff --git a/json/src/spec/spec.rs b/json/src/spec/spec.rs
index ad73ffbf3..fbb206c40 100644
--- a/json/src/spec/spec.rs
+++ b/json/src/spec/spec.rs
@@ -57,13 +57,13 @@ mod tests {
"engine": {
"Ethash": {
"params": {
- "tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
- "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
+ "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
+ "frontierCompatibilityModeLimit" : "0x"
}
}
},
diff --git a/miner/src/miner.rs b/miner/src/miner.rs
index 701016024..66545c8e1 100644
--- a/miner/src/miner.rs
+++ b/miner/src/miner.rs
@@ -18,6 +18,7 @@ use rayon::prelude::*;
use std::sync::atomic::AtomicBool;
use util::*;
+use util::keys::store::{AccountService, AccountProvider};
use ethcore::views::{BlockView, HeaderView};
use ethcore::client::{BlockChainClient, BlockId};
use ethcore::block::{ClosedBlock, IsBlock};
@@ -38,6 +39,8 @@ pub struct Miner {
gas_floor_target: RwLock,
author: RwLock,
extra_data: RwLock,
+
+ accounts: RwLock