Merge branch 'master' into pip-msg

This commit is contained in:
Robert Habermeier
2017-03-08 18:54:48 +01:00
301 changed files with 9175 additions and 3037 deletions

View File

@@ -3,7 +3,7 @@ description = "Ethcore library"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "ethcore"
version = "1.6.0"
version = "1.7.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"

View File

@@ -3,7 +3,7 @@ description = "Parity Light Client Implementation"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "ethcore-light"
version = "1.6.0"
version = "1.7.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"

View File

@@ -33,7 +33,7 @@
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x2fefd8"
"gasLimit": "0x222222"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@@ -48,18 +48,12 @@
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
},
"nodes": [
"enode://08c7ee6a4f861ff0664a49532bcc86de1363acd608999d1b76609bb9bc278649906f069057630fd9493924a368b5d1dc9b8f8bf13ac26df72512f6d1fabd8c95@45.32.7.81:30303",
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
"enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303",
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
"enode://ca5ae4eca09ba6787e29cf6d86f7634d07aae6b9e6317a59aff675851c0bf445068173208cf8ef7f5cd783d8e29b85b2fa3fa358124cf0546823149724f9bde1@138.68.1.16:30303",
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303",
"enode://fa20444ef991596ce99b81652ac4e61de1eddc4ff21d3cd42762abd7ed47e7cf044d3c9ccddaf6035d39725e4eb372806787829ccb9a08ec7cb71883cb8c3abd@50.149.116.182:30303",
"enode://4bd6a4df3612c718333eb5ea7f817923a8cdf1bed89cee70d1710b45a0b6b77b2819846440555e451a9b602ad2efa2d2facd4620650249d8468008946887820a@71.178.232.20:30304",
"enode://921cf8e4c345fe8db913c53964f9cadc667644e7f20195a0b7d877bd689a5934e146ff2c2259f1bae6817b6585153a007ceb67d260b720fa3e6fc4350df25c7f@51.255.49.170:30303",
"enode://ffea3b01c000cdd89e1e9229fea3e80e95b646f9b2aa55071fc865e2f19543c9b06045cc2e69453e6b78100a119e66be1b5ad50b36f2ffd27293caa28efdd1b2@128.199.93.177:3030",
"enode://ee3da491ce6a155eb132708eb0e8d04b0637926ec0ae1b79e63fc97cb9fc3818f49250a0ae0d7f79ed62b66ec677f408c4e01741504dc7a051e274f1e803d454@91.121.65.179:40404",
"enode://48e063a6cf5f335b1ef2ed98126bf522cf254396f850c7d442fe2edbbc23398787e14cd4de7968a00175a82762de9cbe9e1407d8ccbcaeca5004d65f8398d759@159.203.255.59:30303"
"enode://5fbfb426fbb46f8b8c1bd3dd140f5b511da558cd37d60844b525909ab82e13a25ee722293c829e52cb65c2305b1637fa9a2ea4d6634a224d5f400bfe244ac0de@162.243.55.45:30303",
"enode://42d8f29d1db5f4b2947cd5c3d76c6d0d3697e6b9b3430c3d41e46b4bb77655433aeedc25d4b4ea9d8214b6a43008ba67199374a9b53633301bca0cd20c6928ab@104.155.176.151:30303",
"enode://814920f1ec9510aa9ea1c8f79d8b6e6a462045f09caa2ae4055b0f34f7416fca6facd3dd45f1cf1673c0209e0503f02776b8ff94020e98b6679a0dc561b4eba0@104.154.136.117:30303",
"enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303"
],
"accounts": {
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@@ -1,5 +1,5 @@
{
"name": "Frontier/Homestead",
"name": "Foundation",
"dataDir": "ethereum",
"engine": {
"Ethash": {
@@ -9,7 +9,7 @@
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0x3bb2bb5c6c9c9b7f4ef430b47dc7e026310042ea",
"registrar" : "0xe3389675d0338462dC76C6f9A3e432550c36A142",
"homesteadTransition": "0x118c30",
"daoHardforkTransition": "0x1d4c00",
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",

View File

@@ -0,0 +1,59 @@
{
"name": "Kovan",
"dataDir": "kovan",
"engine": {
"authorityRound": {
"params": {
"gasLimitBoundDivisor": "0x400",
"registrar" : "0xfAb104398BBefbd47752E7702D9fE23047E1Bca3",
"stepDuration": "4",
"blockReward": "0x4563918244F40000",
"validators" : {
"list": [
"0x00D6Cc1BA9cf89BD2e58009741f4F7325BAdc0ED",
"0x00427feae2419c15b89d1c21af10d1b6650a4d3d",
"0x4Ed9B08e6354C70fE6F8CB0411b0d3246b424d6c",
"0x0020ee4Be0e2027d76603cB751eE069519bA81A1",
"0x0010f94b296a852aaac52ea6c5ac72e03afd032d",
"0x007733a1FE69CF3f2CF989F81C7b4cAc1693387A",
"0x00E6d2b931F55a3f1701c7389d592a7778897879",
"0x00e4a10650e5a6D6001C38ff8E64F97016a1645c",
"0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de"
]
}
}
}
},
"params": {
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2A"
},
"genesis": {
"seal": {
"authorityRound": {
"step": "0x0",
"signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x20000",
"gasLimit": "0x5B8D80"
},
"accounts": {
"0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0x0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
},
"nodes": [
"enode://c005dd308256c60fab247813d8bf6d6e81f9cd354287837eb1c2fcf294adaa913a3208e88900ef5c55a8cba7042c301d80503edec2ad3f92a72e241ee6743854@192.241.230.87:30303",
"enode://48caeceb2724f2f71406990aa81efe87f8c53f26441d891473da2ae50cc138f238addc0e46b5aee240db55de8c711daac53d7b32a3f13e30edb86a3ca7c2700b@138.68.143.220:30303",
"enode://85705212fd28ebdd56669fb55e958feb9d81f74fe76c82f867564b6c2995e69f596df0f588eba16f1a43b69ce06485d68231a0c83fed8469b41eba0e390c126f@139.59.146.42:30303",
"enode://2aa81bd0a761cd4f02c934dcf3f81c5b65953e51ab5ba03ceb1f125eb06418a1cdffb1c9d01871aa7bd456f3fce35e745608189ad1164f72b2161634b0c3f6ea@188.166.240.190:30303",
"enode://c5900cdd6d20795d58372f42dfbab9d664c27bb97e9c27972741942736e919122f9bac28e74cbc58e4ff195475ea90d9880b71a37af5b5a8cb41d843f765cff8@174.138.79.48:30303"
]
}

View File

@@ -48,7 +48,10 @@
},
"nodes": [
"enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303",
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303"
"enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303",
"enode://fb28713820e718066a2f5df6250ae9d07cff22f672dbf26be6c75d088f821a9ad230138ba492c533a80407d054b1436ef18e951bb65e6901553516c8dffe8ff0@104.155.176.151:30304",
"enode://afdc6076b9bf3e7d3d01442d6841071e84c76c73a7016cb4f35c0437df219db38565766234448f1592a07ba5295a867f0ce87b359bf50311ed0b830a2361392d@104.154.136.117:30403",
"enode://21101a9597b79e933e17bc94ef3506fe99a137808907aa8fefa67eea4b789792ad11fb391f38b00087f8800a2d3dff011572b62a31232133dd1591ac2d1502c8@104.198.71.200:30403"
],
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

File diff suppressed because one or more lines are too long

View File

@@ -38,7 +38,7 @@
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x2fefd8"
"gasLimit": "0x222222"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@@ -27,7 +27,7 @@
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x2fefd8"
"gasLimit": "0x222222"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },

View File

@@ -253,7 +253,7 @@ impl Client {
if let Some(reg_addr) = client.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()) {
trace!(target: "client", "Found registrar at {}", reg_addr);
let weak = Arc::downgrade(&client);
let registrar = Registry::new(reg_addr, move |a, d| weak.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d)));
let registrar = Registry::new(reg_addr, move |a, d| weak.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d)));
*client.registrar.lock() = Some(registrar);
}
Ok(client)
@@ -1428,7 +1428,7 @@ impl BlockChainClient for Client {
}
}
fn call_contract(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
let from = Address::default();
let transaction = Transaction {
nonce: self.latest_nonce(&from),
@@ -1439,7 +1439,7 @@ impl BlockChainClient for Client {
data: data,
}.fake_sign(from);
self.call(&transaction, BlockId::Latest, Default::default())
self.call(&transaction, block_id, Default::default())
.map_err(|e| format!("{:?}", e))
.map(|executed| {
executed.output
@@ -1612,7 +1612,6 @@ impl ::client::ProvingBlockChainClient for Client {
_ => return Some(state.drop().1.extract_proof()),
}
}
}
impl Drop for Client {

File diff suppressed because one or more lines are too long

View File

@@ -732,7 +732,7 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn call_contract(&self, _address: Address, _data: Bytes) -> Result<Bytes, String> { Ok(vec![]) }
fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result<Bytes, String> { Ok(vec![]) }
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError> {
let transaction = Transaction {

View File

@@ -256,7 +256,7 @@ pub trait BlockChainClient : Sync + Send {
fn pruning_info(&self) -> PruningInfo;
/// Like `call`, but with various defaults. Designed to be used for calling contracts.
fn call_contract(&self, address: Address, data: Bytes) -> Result<Bytes, String>;
fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String>;
/// Import a transaction: used for misbehaviour reporting.
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError>;

View File

@@ -47,6 +47,8 @@ pub struct AuthorityRoundParams {
pub step_duration: Duration,
/// Block reward.
pub block_reward: U256,
/// Namereg contract address.
pub registrar: Address,
/// Starting step,
pub start_step: Option<u64>,
/// Valid validators.
@@ -60,6 +62,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
step_duration: Duration::from_secs(p.step_duration.into()),
validators: p.validators,
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
registrar: p.registrar.map_or_else(Address::new, Into::into),
start_step: p.start_step.map(Into::into),
}
}
@@ -71,6 +74,7 @@ pub struct AuthorityRound {
params: CommonParams,
gas_limit_bound_divisor: U256,
block_reward: U256,
registrar: Address,
step_duration: Duration,
builtins: BTreeMap<Address, Builtin>,
transition_service: IoService<()>,
@@ -79,6 +83,8 @@ pub struct AuthorityRound {
client: RwLock<Option<Weak<EngineClient>>>,
signer: EngineSigner,
validators: Box<ValidatorSet + Send + Sync>,
/// Is this Engine just for testing (prevents step calibration).
calibrate_step: bool,
}
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
@@ -109,6 +115,7 @@ impl AuthorityRound {
params: params,
gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
block_reward: our_params.block_reward,
registrar: our_params.registrar,
step_duration: our_params.step_duration,
builtins: builtins,
transition_service: IoService::<()>::start()?,
@@ -117,6 +124,7 @@ impl AuthorityRound {
client: RwLock::new(None),
signer: Default::default(),
validators: new_validator_set(our_params.validators),
calibrate_step: our_params.start_step.is_none(),
});
// Do not initialize timeouts for tests.
if should_timeout {
@@ -126,6 +134,12 @@ impl AuthorityRound {
Ok(engine)
}
fn calibrate_step(&self) {
if self.calibrate_step {
self.step.store((unix_now().as_secs() / self.step_duration.as_secs()) as usize, AtomicOrdering::SeqCst);
}
}
fn remaining_step_duration(&self) -> Duration {
let now = unix_now();
let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
@@ -136,12 +150,22 @@ impl AuthorityRound {
}
}
fn step_proposer(&self, step: usize) -> Address {
self.validators.get(step)
fn step_proposer(&self, bh: &H256, step: usize) -> Address {
self.validators.get(bh, step)
}
fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
self.step_proposer(step) == *address
fn is_step_proposer(&self, bh: &H256, step: usize, address: &Address) -> bool {
self.step_proposer(bh, step) == *address
}
fn is_future_step(&self, step: usize) -> bool {
if step > self.step.load(AtomicOrdering::SeqCst) + 1 {
// Make absolutely sure that the step is correct.
self.calibrate_step();
step > self.step.load(AtomicOrdering::SeqCst) + 1
} else {
false
}
}
}
@@ -176,11 +200,16 @@ impl IoHandler<()> for TransitionHandler {
impl Engine for AuthorityRound {
fn name(&self) -> &str { "AuthorityRound" }
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
/// Two fields - consensus step and the corresponding proposer signature.
fn seal_fields(&self) -> usize { 2 }
fn params(&self) -> &CommonParams { &self.params }
fn additional_params(&self) -> HashMap<String, String> { hash_map!["registrar".to_owned() => self.registrar.hex()] }
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
fn step(&self) {
@@ -221,7 +250,7 @@ impl Engine for AuthorityRound {
}
fn seals_internally(&self) -> Option<bool> {
Some(self.validators.contains(&self.signer.address()))
Some(self.signer.address() != Address::default())
}
/// Attempt to seal the block internally.
@@ -232,7 +261,7 @@ impl Engine for AuthorityRound {
if self.proposed.load(AtomicOrdering::SeqCst) { return Seal::None; }
let header = block.header();
let step = self.step.load(AtomicOrdering::SeqCst);
if self.is_step_proposer(step, header.author()) {
if self.is_step_proposer(header.parent_hash(), step, header.author()) {
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step);
self.proposed.store(true, AtomicOrdering::SeqCst);
@@ -271,32 +300,32 @@ impl Engine for AuthorityRound {
}
}
/// Check if the signature belongs to the correct proposer.
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
let header_step = header_step(header)?;
// Give one step slack if step is lagging, double vote is still not possible.
if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 {
let proposer_signature = header_signature(header)?;
let correct_proposer = self.step_proposer(header_step);
if verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? {
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
Ok(())
} else {
trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", header_step);
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
}
} else {
}
/// Do the validator and gas limit validation.
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
let step = header_step(header)?;
// Give one step slack if step is lagging, double vote is still not possible.
if self.is_future_step(step) {
trace!(target: "engine", "verify_block_unordered: block from the future");
self.validators.report_benign(header.author());
Err(BlockError::InvalidSeal)?
} else {
let proposer_signature = header_signature(header)?;
let correct_proposer = self.step_proposer(header.parent_hash(), step);
if !verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? {
trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", step);
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
}
}
}
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
// Do 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() })));
}
let step = header_step(header)?;
// Check if parent is from a previous step.
if step == header_step(parent)? {
trace!(target: "engine", "Multiple blocks proposed for step {}.", step);
@@ -384,7 +413,7 @@ mod tests {
let mut header: Header = Header::default();
header.set_seal(vec![encode(&H520::default()).to_vec()]);
let verify_result = engine.verify_block_unordered(&header, None);
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
assert!(verify_result.is_err());
}
@@ -422,10 +451,14 @@ mod tests {
#[test]
fn proposer_switching() {
let mut header: Header = Header::default();
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
let mut parent_header: Header = Header::default();
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
let mut header: Header = Header::default();
header.set_number(1);
header.set_gas_limit(U256::from_str("222222").unwrap());
header.set_author(addr);
let engine = Spec::new_test_round().engine;
@@ -434,17 +467,22 @@ mod tests {
// Two validators.
// Spec starts with step 2.
header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
assert!(engine.verify_block_seal(&header).is_err());
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
assert!(engine.verify_block_seal(&header).is_ok());
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
}
#[test]
fn rejects_future_block() {
let mut header: Header = Header::default();
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "0").unwrap();
let mut parent_header: Header = Header::default();
parent_header.set_seal(vec![encode(&0usize).to_vec()]);
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
let mut header: Header = Header::default();
header.set_number(1);
header.set_gas_limit(U256::from_str("222222").unwrap());
header.set_author(addr);
let engine = Spec::new_test_round().engine;
@@ -453,8 +491,8 @@ mod tests {
// Two validators.
// Spec starts with step 2.
header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
assert!(engine.verify_block_seal(&header).is_ok());
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
assert!(engine.verify_block_seal(&header).is_err());
assert!(engine.verify_block_family(&header, &parent_header, None).is_err());
}
}

View File

@@ -104,14 +104,14 @@ impl Engine for BasicAuthority {
}
fn seals_internally(&self) -> Option<bool> {
Some(self.validators.contains(&self.signer.address()))
Some(self.signer.address() != Address::default())
}
/// Attempt to seal the block internally.
fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
let header = block.header();
let author = header.author();
if self.validators.contains(author) {
if self.validators.contains(header.parent_hash(), author) {
// account should be pernamently unlocked, otherwise sealing will fail
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8])).to_vec()]);
@@ -133,20 +133,20 @@ impl Engine for BasicAuthority {
Ok(())
}
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
use rlp::{UntrustedRlp, View};
// check the signature is legit.
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
if !self.validators.contains(&signer) {
return Err(BlockError::InvalidSeal)?;
}
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
Ok(())
}
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// we should not calculate difficulty for genesis blocks
use rlp::{UntrustedRlp, View};
// Check if the signature belongs to a validator, can depend on parent state.
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
if !self.validators.contains(header.parent_hash(), &signer) {
return Err(BlockError::InvalidSeal)?;
}
// Do 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() })));
}
@@ -239,7 +239,7 @@ mod tests {
let mut header: Header = Header::default();
header.set_seal(vec![::rlp::encode(&H520::default()).to_vec()]);
let verify_result = engine.verify_block_unordered(&header, None);
let verify_result = engine.verify_block_family(&header, &Default::default(), None);
assert!(verify_result.is_err());
}

View File

@@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap;
use util::Address;
use util::{Address, HashMap};
use builtin::Builtin;
use engines::{Engine, Seal};
use env_info::EnvInfo;
@@ -26,14 +26,16 @@ use block::ExecutedBlock;
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
pub struct InstantSeal {
params: CommonParams,
registrar: Address,
builtins: BTreeMap<Address, Builtin>,
}
impl InstantSeal {
/// Returns new instance of InstantSeal with default VM Factory
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
pub fn new(params: CommonParams, registrar: Address, builtins: BTreeMap<Address, Builtin>) -> Self {
InstantSeal {
params: params,
registrar: registrar,
builtins: builtins,
}
}
@@ -48,6 +50,10 @@ impl Engine for InstantSeal {
&self.params
}
fn additional_params(&self) -> HashMap<String, String> {
hash_map!["registrar".to_owned() => self.registrar.hex()]
}
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
&self.builtins
}
@@ -76,9 +82,9 @@ mod tests {
fn instant_can_seal() {
let spec = Spec::new_instant();
let engine = &*spec.engine;
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_state_db();
let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
let genesis_header = spec.genesis_header();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();

View File

@@ -166,7 +166,9 @@ pub trait Engine : Sync + Send {
}
/// The network ID that transactions should be signed with.
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u64> { None }
fn signing_network_id(&self, _env_info: &EnvInfo) -> Option<u64> {
Some(self.params().chain_id)
}
/// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods
/// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer

View File

@@ -78,6 +78,7 @@ pub struct Tendermint {
step_service: IoService<Step>,
client: RwLock<Option<Weak<EngineClient>>>,
block_reward: U256,
registrar: Address,
/// Blockchain height.
height: AtomicUsize,
/// Consensus view.
@@ -94,6 +95,8 @@ pub struct Tendermint {
last_lock: AtomicUsize,
/// Bare hash of the proposed block, used for seal submission.
proposal: RwLock<Option<H256>>,
/// Hash of the proposal parent block.
proposal_parent: RwLock<H256>,
/// Set used to determine the current validators.
validators: Box<ValidatorSet + Send + Sync>,
}
@@ -109,14 +112,16 @@ impl Tendermint {
client: RwLock::new(None),
step_service: IoService::<Step>::start()?,
block_reward: our_params.block_reward,
registrar: our_params.registrar,
height: AtomicUsize::new(1),
view: AtomicUsize::new(0),
step: RwLock::new(Step::Propose),
votes: VoteCollector::default(),
votes: Default::default(),
signer: Default::default(),
lock_change: RwLock::new(None),
last_lock: AtomicUsize::new(0),
proposal: RwLock::new(None),
proposal_parent: Default::default(),
validators: new_validator_set(our_params.validators),
});
let handler = TransitionHandler::new(Arc::downgrade(&engine) as Weak<Engine>, Box::new(our_params.timeouts));
@@ -230,7 +235,7 @@ impl Tendermint {
let height = self.height.load(AtomicOrdering::SeqCst);
if let Some(block_hash) = *self.proposal.read() {
// Generate seal and remove old votes.
if self.is_signer_proposer() {
if self.is_signer_proposer(&*self.proposal_parent.read()) {
let proposal_step = VoteStep::new(height, view, Step::Propose);
let precommit_step = VoteStep::new(proposal_step.height, proposal_step.view, Step::Precommit);
if let Some(seal) = self.votes.seal_signatures(proposal_step, precommit_step, &block_hash) {
@@ -252,23 +257,23 @@ impl Tendermint {
}
fn is_authority(&self, address: &Address) -> bool {
self.validators.contains(address)
self.validators.contains(&*self.proposal_parent.read(), address)
}
fn is_above_threshold(&self, n: usize) -> bool {
n > self.validators.count() * 2/3
n > self.validators.count(&*self.proposal_parent.read()) * 2/3
}
/// Find the designated for the given view.
fn view_proposer(&self, height: Height, view: View) -> Address {
fn view_proposer(&self, bh: &H256, height: Height, view: View) -> Address {
let proposer_nonce = height + view;
trace!(target: "engine", "Proposer nonce: {}", proposer_nonce);
self.validators.get(proposer_nonce)
self.validators.get(bh, proposer_nonce)
}
/// Check if address is a proposer for given view.
fn is_view_proposer(&self, height: Height, view: View, address: &Address) -> Result<(), EngineError> {
let proposer = self.view_proposer(height, view);
fn is_view_proposer(&self, bh: &H256, height: Height, view: View, address: &Address) -> Result<(), EngineError> {
let proposer = self.view_proposer(bh, height, view);
if proposer == *address {
Ok(())
} else {
@@ -277,8 +282,8 @@ impl Tendermint {
}
/// Check if current signer is the current proposer.
fn is_signer_proposer(&self) -> bool {
let proposer = self.view_proposer(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
fn is_signer_proposer(&self, bh: &H256) -> bool {
let proposer = self.view_proposer(bh, self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
self.signer.is_address(&proposer)
}
@@ -369,14 +374,20 @@ impl Tendermint {
impl Engine for Tendermint {
fn name(&self) -> &str { "Tendermint" }
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
/// (consensus view, proposal signature, authority signatures)
fn seal_fields(&self) -> usize { 3 }
fn params(&self) -> &CommonParams { &self.params }
fn additional_params(&self) -> HashMap<String, String> { hash_map!["registrar".to_owned() => self.registrar.hex()] }
fn builtins(&self) -> &BTreeMap<Address, Builtin> { &self.builtins }
fn maximum_uncle_count(&self) -> usize { 0 }
fn maximum_uncle_age(&self) -> usize { 0 }
/// Additional engine-specific information for the user/developer concerning `header`.
@@ -411,7 +422,7 @@ impl Engine for Tendermint {
/// Should this node participate.
fn seals_internally(&self) -> Option<bool> {
Some(self.is_authority(&self.signer.address()))
Some(self.signer.address() != Address::default())
}
/// Attempt to seal generate a proposal seal.
@@ -419,7 +430,7 @@ impl Engine for Tendermint {
let header = block.header();
let author = header.author();
// Only proposer can generate seal if None was generated.
if !self.is_signer_proposer() || self.proposal.read().is_some() {
if !self.is_signer_proposer(header.parent_hash()) || self.proposal.read().is_some() {
return Seal::None;
}
@@ -433,6 +444,7 @@ impl Engine for Tendermint {
self.votes.vote(ConsensusMessage::new(signature, height, view, Step::Propose, bh), author);
// Remember proposal for later seal submission.
*self.proposal.write() = bh;
*self.proposal_parent.write() = header.parent_hash().clone();
Seal::Proposal(vec![
::rlp::encode(&view).to_vec(),
::rlp::encode(&signature).to_vec(),
@@ -497,7 +509,12 @@ impl Engine for Tendermint {
}
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
Ok(())
}
/// Verify validators and gas limit.
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
let proposal = ConsensusMessage::new_proposal(header)?;
let proposer = proposal.verify()?;
if !self.is_authority(&proposer) {
@@ -514,7 +531,7 @@ impl Engine for Tendermint {
Some(a) => a,
None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
};
if !self.validators.contains(&address) {
if !self.validators.contains(header.parent_hash(), &address) {
Err(EngineError::NotAuthorized(address.to_owned()))?
}
@@ -537,12 +554,9 @@ impl Engine for Tendermint {
found: signatures_len
}))?;
}
self.is_view_proposer(proposal.vote_step.height, proposal.vote_step.view, &proposer)?;
self.is_view_proposer(header.parent_hash(), proposal.vote_step.height, proposal.vote_step.view, &proposer)?;
}
Ok(())
}
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
if header.number() == 0 {
Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
}
@@ -587,6 +601,7 @@ impl Engine for Tendermint {
debug!(target: "engine", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
if self.is_view(&proposal) {
*self.proposal.write() = proposal.block_hash.clone();
*self.proposal_parent.write() = header.parent_hash().clone();
}
self.votes.vote(proposal, &proposer);
true
@@ -599,7 +614,7 @@ impl Engine for Tendermint {
trace!(target: "engine", "Propose timeout.");
if self.proposal.read().is_none() {
// Report the proposer if no proposal was received.
let current_proposer = self.view_proposer(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
let current_proposer = self.view_proposer(&*self.proposal_parent.read(), self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
self.validators.report_benign(&current_proposer);
}
Step::Prevote
@@ -757,20 +772,25 @@ mod tests {
let (spec, tap) = setup();
let engine = spec.engine;
let mut header = Header::default();
let validator = insert_and_unlock(&tap, "0");
header.set_author(validator);
let seal = proposal_seal(&tap, &header, 0);
header.set_seal(seal);
// Good proposer.
assert!(engine.verify_block_unordered(&header.clone(), None).is_ok());
let mut parent_header: Header = Header::default();
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
let mut header = Header::default();
header.set_number(1);
header.set_gas_limit(U256::from_str("222222").unwrap());
let validator = insert_and_unlock(&tap, "1");
header.set_author(validator);
let seal = proposal_seal(&tap, &header, 0);
header.set_seal(seal);
// Good proposer.
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
let validator = insert_and_unlock(&tap, "0");
header.set_author(validator);
let seal = proposal_seal(&tap, &header, 0);
header.set_seal(seal);
// Bad proposer.
match engine.verify_block_unordered(&header, None) {
match engine.verify_block_family(&header, &parent_header, None) {
Err(Error::Engine(EngineError::NotProposer(_))) => {},
_ => panic!(),
}
@@ -780,7 +800,7 @@ mod tests {
let seal = proposal_seal(&tap, &header, 0);
header.set_seal(seal);
// Not authority.
match engine.verify_block_unordered(&header, None) {
match engine.verify_block_family(&header, &parent_header, None) {
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
_ => panic!(),
};
@@ -792,19 +812,24 @@ mod tests {
let (spec, tap) = setup();
let engine = spec.engine;
let mut parent_header: Header = Header::default();
parent_header.set_gas_limit(U256::from_str("222222").unwrap());
let mut header = Header::default();
header.set_number(2);
header.set_gas_limit(U256::from_str("222222").unwrap());
let proposer = insert_and_unlock(&tap, "1");
header.set_author(proposer);
let mut seal = proposal_seal(&tap, &header, 0);
let vote_info = message_info_rlp(&VoteStep::new(0, 0, Step::Precommit), Some(header.bare_hash()));
let vote_info = message_info_rlp(&VoteStep::new(2, 0, Step::Precommit), Some(header.bare_hash()));
let signature1 = tap.sign(proposer, None, vote_info.sha3()).unwrap();
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone())]).to_vec();
header.set_seal(seal.clone());
// One good signature is not enough.
match engine.verify_block_unordered(&header, None) {
match engine.verify_block_family(&header, &parent_header, None) {
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
_ => panic!(),
}
@@ -815,7 +840,7 @@ mod tests {
seal[2] = ::rlp::encode(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).to_vec();
header.set_seal(seal.clone());
assert!(engine.verify_block_unordered(&header, None).is_ok());
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
let bad_voter = insert_and_unlock(&tap, "101");
let bad_signature = tap.sign(bad_voter, None, vote_info.sha3()).unwrap();
@@ -824,7 +849,7 @@ mod tests {
header.set_seal(seal);
// One good and one bad signature.
match engine.verify_block_unordered(&header, None) {
match engine.verify_block_family(&header, &parent_header, None) {
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
_ => panic!(),
};

View File

@@ -17,7 +17,7 @@
//! Tendermint specific parameters.
use ethjson;
use util::{U256, Uint};
use util::{U256, Uint, Address, FixedHash};
use time::Duration;
use super::super::transition::Timeouts;
use super::Step;
@@ -33,6 +33,8 @@ pub struct TendermintParams {
pub timeouts: TendermintTimeouts,
/// Block reward.
pub block_reward: U256,
/// Namereg contract address.
pub registrar: Address,
}
/// Base timeout of each step in ms.
@@ -88,6 +90,7 @@ impl From<ethjson::spec::TendermintParams> for TendermintParams {
commit: p.timeout_commit.map_or(dt.commit, to_duration),
},
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
registrar: p.registrar.map_or_else(Address::new, Into::into),
}
}
}

View File

@@ -26,30 +26,30 @@ use super::safe_contract::ValidatorSafeContract;
/// The validator contract should have the following interface:
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
pub struct ValidatorContract {
validators: Arc<ValidatorSafeContract>,
validators: ValidatorSafeContract,
provider: RwLock<Option<provider::Contract>>,
}
impl ValidatorContract {
pub fn new(contract_address: Address) -> Self {
ValidatorContract {
validators: Arc::new(ValidatorSafeContract::new(contract_address)),
validators: ValidatorSafeContract::new(contract_address),
provider: RwLock::new(None),
}
}
}
impl ValidatorSet for Arc<ValidatorContract> {
fn contains(&self, address: &Address) -> bool {
self.validators.contains(address)
impl ValidatorSet for ValidatorContract {
fn contains(&self, bh: &H256, address: &Address) -> bool {
self.validators.contains(bh, address)
}
fn get(&self, nonce: usize) -> Address {
self.validators.get(nonce)
fn get(&self, bh: &H256, nonce: usize) -> Address {
self.validators.get(bh, nonce)
}
fn count(&self) -> usize {
self.validators.count()
fn count(&self, bh: &H256) -> usize {
self.validators.count(bh)
}
fn report_malicious(&self, address: &Address) {
@@ -144,6 +144,7 @@ mod tests {
use header::Header;
use account_provider::AccountProvider;
use miner::MinerService;
use types::ids::BlockId;
use client::BlockChainClient;
use tests::helpers::generate_dummy_client_with_spec_and_accounts;
use super::super::ValidatorSet;
@@ -154,8 +155,9 @@ mod tests {
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, None);
let vc = Arc::new(ValidatorContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
vc.register_contract(Arc::downgrade(&client));
assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
let last_hash = client.best_block_header().hash();
assert!(vc.contains(&last_hash, &Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
assert!(vc.contains(&last_hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
}
#[test]
@@ -171,18 +173,21 @@ mod tests {
client.miner().set_engine_signer(v1, "".into()).unwrap();
let mut header = Header::default();
let seal = encode(&vec!(5u8)).to_vec();
header.set_seal(vec!(seal));
let seal = vec![encode(&5u8).to_vec(), encode(&(&H520::default() as &[u8])).to_vec()];
header.set_seal(seal);
header.set_author(v1);
header.set_number(1);
header.set_number(2);
header.set_parent_hash(client.chain_info().best_block_hash);
// `reportBenign` when the designated proposer releases block from the future (bad clock).
assert!(client.engine().verify_block_unordered(&header, None).is_err());
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
// Seal a block.
client.engine().step();
assert_eq!(client.chain_info().best_block_number, 1);
// Check if the unresponsive validator is `disliked`.
assert_eq!(client.call_contract(validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
assert_eq!(client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e");
// Simulate a misbehaving validator by handling a double proposal.
let header = client.best_block_header().decode();
assert!(client.engine().verify_block_family(&header, &header, None).is_err());
// Seal a block.
client.engine().step();

View File

@@ -21,7 +21,7 @@ mod safe_contract;
mod contract;
use std::sync::Weak;
use util::{Address, Arc};
use util::{Address, H256};
use ethjson::spec::ValidatorSet as ValidatorSpec;
use client::Client;
use self::simple_list::SimpleList;
@@ -32,18 +32,18 @@ use self::safe_contract::ValidatorSafeContract;
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet + Send + Sync> {
match spec {
ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
ValidatorSpec::SafeContract(address) => Box::new(Arc::new(ValidatorSafeContract::new(address.into()))),
ValidatorSpec::Contract(address) => Box::new(Arc::new(ValidatorContract::new(address.into()))),
ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())),
ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())),
}
}
pub trait ValidatorSet {
/// Checks if a given address is a validator.
fn contains(&self, address: &Address) -> bool;
fn contains(&self, bh: &H256, address: &Address) -> bool;
/// Draws an validator nonce modulo number of validators.
fn get(&self, nonce: usize) -> Address;
fn get(&self, bh: &H256, nonce: usize) -> Address;
/// Returns the current number of validators.
fn count(&self) -> usize;
fn count(&self, bh: &H256) -> usize;
/// Notifies about malicious behaviour.
fn report_malicious(&self, _validator: &Address) {}
/// Notifies about benign misbehaviour.

View File

@@ -17,17 +17,23 @@
/// Validator set maintained in a contract, updated using `getValidators` method.
use std::sync::Weak;
use ethabi;
use util::*;
use util::cache::MemoryLruCache;
use types::ids::BlockId;
use client::{Client, BlockChainClient};
use client::chain_notify::ChainNotify;
use super::ValidatorSet;
use super::simple_list::SimpleList;
const MEMOIZE_CAPACITY: usize = 500;
const CONTRACT_INTERFACE: &'static [u8] = b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]";
const GET_VALIDATORS: &'static str = "getValidators";
/// The validator contract should have the following interface:
/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
pub struct ValidatorSafeContract {
pub address: Address,
validators: RwLock<SimpleList>,
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
provider: RwLock<Option<provider::Contract>>,
}
@@ -35,102 +41,127 @@ impl ValidatorSafeContract {
pub fn new(contract_address: Address) -> Self {
ValidatorSafeContract {
address: contract_address,
validators: Default::default(),
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
provider: RwLock::new(None),
}
}
/// Queries the state and updates the set of validators.
pub fn update(&self) {
/// Queries the state and gets the set of validators.
fn get_list(&self, block_hash: H256) -> Option<SimpleList> {
if let Some(ref provider) = *self.provider.read() {
match provider.get_validators() {
match provider.get_validators(BlockId::Hash(block_hash)) {
Ok(new) => {
debug!(target: "engine", "Set of validators obtained: {:?}", new);
*self.validators.write() = SimpleList::new(new);
Some(SimpleList::new(new))
},
Err(s) => {
debug!(target: "engine", "Set of validators could not be updated: {}", s);
None
},
Err(s) => warn!(target: "engine", "Set of validators could not be updated: {}", s),
}
} else {
warn!(target: "engine", "Set of validators could not be updated: no provider contract.")
warn!(target: "engine", "Set of validators could not be updated: no provider contract.");
None
}
}
}
/// Checks validators on every block.
impl ChainNotify for ValidatorSafeContract {
fn new_blocks(
&self,
_: Vec<H256>,
_: Vec<H256>,
enacted: Vec<H256>,
_: Vec<H256>,
_: Vec<H256>,
_: Vec<Bytes>,
_duration: u64) {
if !enacted.is_empty() {
self.update();
}
}
}
impl ValidatorSet for Arc<ValidatorSafeContract> {
fn contains(&self, address: &Address) -> bool {
self.validators.read().contains(address)
impl ValidatorSet for ValidatorSafeContract {
fn contains(&self, block_hash: &H256, address: &Address) -> bool {
let mut guard = self.validators.write();
let maybe_existing = guard
.get_mut(block_hash)
.map(|list| list.contains(block_hash, address));
maybe_existing
.unwrap_or_else(|| self
.get_list(block_hash.clone())
.map_or(false, |list| {
let contains = list.contains(block_hash, address);
guard.insert(block_hash.clone(), list);
contains
}))
}
fn get(&self, nonce: usize) -> Address {
self.validators.read().get(nonce)
fn get(&self, block_hash: &H256, nonce: usize) -> Address {
let mut guard = self.validators.write();
let maybe_existing = guard
.get_mut(block_hash)
.map(|list| list.get(block_hash, nonce));
maybe_existing
.unwrap_or_else(|| self
.get_list(block_hash.clone())
.map_or_else(Default::default, |list| {
let address = list.get(block_hash, nonce);
guard.insert(block_hash.clone(), list);
address
}))
}
fn count(&self) -> usize {
self.validators.read().count()
fn count(&self, block_hash: &H256) -> usize {
let mut guard = self.validators.write();
let maybe_existing = guard
.get_mut(block_hash)
.map(|list| list.count(block_hash));
maybe_existing
.unwrap_or_else(|| self
.get_list(block_hash.clone())
.map_or_else(usize::max_value, |list| {
let address = list.count(block_hash);
guard.insert(block_hash.clone(), list);
address
}))
}
fn register_contract(&self, client: Weak<Client>) {
if let Some(c) = client.upgrade() {
c.add_notify(self.clone());
}
{
*self.provider.write() = Some(provider::Contract::new(self.address, move |a, d| client.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d))));
}
self.update();
trace!(target: "engine", "Setting up contract caller.");
let contract = ethabi::Contract::new(ethabi::Interface::load(CONTRACT_INTERFACE).expect("JSON interface is valid; qed"));
let call = contract.function(GET_VALIDATORS.into()).expect("Method name is valid; qed");
let data = call.encode_call(vec![]).expect("get_validators does not take any arguments; qed");
let contract_address = self.address.clone();
let do_call = move |id| client
.upgrade()
.ok_or("No client!".into())
.and_then(|c| c.call_contract(id, contract_address.clone(), data.clone()))
.map(|raw_output| call.decode_output(raw_output).expect("ethabi is correct; qed"));
*self.provider.write() = Some(provider::Contract::new(do_call));
}
}
mod provider {
// Autogenerated from JSON contract definition using Rust contract convertor.
#![allow(unused_imports)]
use std::string::String;
use std::result::Result;
use std::fmt;
use {util, ethabi};
use util::{FixedHash, Uint};
use types::ids::BlockId;
pub struct Contract {
contract: ethabi::Contract,
address: util::Address,
do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static>,
do_call: Box<Fn(BlockId) -> Result<Vec<ethabi::Token>, String> + Send + Sync + 'static>,
}
impl Contract {
pub fn new<F>(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static {
pub fn new<F>(do_call: F) -> Self where F: Fn(BlockId) -> Result<Vec<ethabi::Token>, String> + Send + Sync + 'static {
Contract {
contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")),
address: address,
do_call: Box::new(do_call),
}
}
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
#[allow(dead_code)]
pub fn get_validators(&self) -> Result<Vec<util::Address>, String> {
let call = self.contract.function("getValidators".into()).map_err(Self::as_string)?;
let data = call.encode_call(
vec![]
).map_err(Self::as_string)?;
let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
let mut result = output.into_iter().rev().collect::<Vec<_>>();
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::<Option<Vec<[u8; 20]>>>()).ok_or("Invalid type returned")?; r.into_iter().map(|a| util::Address::from(a)).collect::<Vec<_>>() }))
/// Gets validators from contract with interface: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
pub fn get_validators(&self, id: BlockId) -> Result<Vec<util::Address>, String> {
Ok((self.do_call)(id)?
.into_iter()
.rev()
.collect::<Vec<_>>()
.pop()
.expect("get_validators returns one argument; qed")
.to_array()
.and_then(|v| v
.into_iter()
.map(|a| a.to_address())
.collect::<Option<Vec<[u8; 20]>>>())
.expect("get_validators returns a list of addresses; qed")
.into_iter()
.map(util::Address::from)
.collect::<Vec<_>>()
)
}
}
}
@@ -138,13 +169,14 @@ mod provider {
#[cfg(test)]
mod tests {
use util::*;
use types::ids::BlockId;
use spec::Spec;
use account_provider::AccountProvider;
use transaction::{Transaction, Action};
use client::{BlockChainClient, EngineClient};
use ethkey::Secret;
use miner::MinerService;
use tests::helpers::generate_dummy_client_with_spec_and_accounts;
use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data};
use super::super::ValidatorSet;
use super::ValidatorSafeContract;
@@ -153,12 +185,13 @@ mod tests {
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None);
let vc = Arc::new(ValidatorSafeContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
vc.register_contract(Arc::downgrade(&client));
assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
let last_hash = client.best_block_header().hash();
assert!(vc.contains(&last_hash, &Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
assert!(vc.contains(&last_hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
}
#[test]
fn updates_validators() {
fn knows_validators() {
let tap = Arc::new(AccountProvider::transient_provider());
let s0 = Secret::from_slice(&"1".sha3()).unwrap();
let v0 = tap.insert_account(s0.clone(), "").unwrap();
@@ -212,5 +245,14 @@ mod tests {
client.update_sealing();
// Able to seal again.
assert_eq!(client.chain_info().best_block_number, 3);
// Check syncing.
let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]);
sync_client.engine().register_client(Arc::downgrade(&sync_client));
for i in 1..4 {
sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap();
}
sync_client.flush_queue();
assert_eq!(sync_client.chain_info().best_block_number, 3);
}
}

View File

@@ -16,7 +16,7 @@
/// Preconfigured validator list.
use util::Address;
use util::{H256, Address, HeapSizeOf};
use super::ValidatorSet;
#[derive(Debug, PartialEq, Eq, Default)]
@@ -34,16 +34,22 @@ impl SimpleList {
}
}
impl HeapSizeOf for SimpleList {
fn heap_size_of_children(&self) -> usize {
self.validators.heap_size_of_children() + self.validator_n.heap_size_of_children()
}
}
impl ValidatorSet for SimpleList {
fn contains(&self, address: &Address) -> bool {
fn contains(&self, _bh: &H256, address: &Address) -> bool {
self.validators.contains(address)
}
fn get(&self, nonce: usize) -> Address {
fn get(&self, _bh: &H256, nonce: usize) -> Address {
self.validators.get(nonce % self.validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
}
fn count(&self) -> usize {
fn count(&self, _bh: &H256) -> usize {
self.validator_n
}
}
@@ -60,9 +66,9 @@ mod tests {
let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let list = SimpleList::new(vec![a1.clone(), a2.clone()]);
assert!(list.contains(&a1));
assert_eq!(list.get(0), a1);
assert_eq!(list.get(1), a2);
assert_eq!(list.get(2), a1);
assert!(list.contains(&Default::default(), &a1));
assert_eq!(list.get(&Default::default(), 0), a1);
assert_eq!(list.get(&Default::default(), 1), a2);
assert_eq!(list.get(&Default::default(), 2), a1);
}
}

View File

@@ -30,46 +30,52 @@ pub use self::denominations::*;
use super::spec::*;
/// Most recent fork block that we support on Mainnet.
pub const FORK_SUPPORTED_FRONTIER: u64 = 2675000;
pub const FORK_SUPPORTED_FOUNDATION: u64 = 2675000;
/// Most recent fork block that we support on Ropsten.
pub const FORK_SUPPORTED_ROPSTEN: u64 = 10;
/// Most recent fork block that we support on Kovan.
pub const FORK_SUPPORTED_KOVAN: u64 = 0;
fn load(b: &[u8]) -> Spec {
Spec::load(b).expect("chain spec is invalid")
}
/// Create a new Olympic chain spec.
/// Create a new Foundation Olympic chain spec.
pub fn new_olympic() -> Spec { load(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec.
pub fn new_frontier() -> Spec { load(include_bytes!("../../res/ethereum/frontier.json")) }
/// Create a new Foundation Mainnet chain spec.
pub fn new_foundation() -> Spec { load(include_bytes!("../../res/ethereum/foundation.json")) }
/// Create a new Frontier mainnet chain spec without the DAO hardfork.
/// Create a new Classic Mainnet chain spec without the DAO hardfork.
pub fn new_classic() -> Spec { load(include_bytes!("../../res/ethereum/classic.json")) }
/// Create a new Frontier mainnet chain spec without the DAO hardfork.
/// Create a new Expanse mainnet chain spec.
pub fn new_expanse() -> Spec { load(include_bytes!("../../res/ethereum/expanse.json")) }
/// Create a new Frontier chain spec as though it never changes to Homestead.
/// Create a new Kovan testnet chain spec.
pub fn new_kovan() -> Spec { load(include_bytes!("../../res/ethereum/kovan.json")) }
/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead.
pub fn new_frontier_test() -> Spec { load(include_bytes!("../../res/ethereum/frontier_test.json")) }
/// Create a new Homestead chain spec as though it never changed from Frontier.
/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier.
pub fn new_homestead_test() -> Spec { load(include_bytes!("../../res/ethereum/homestead_test.json")) }
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
/// Create a new Foundation Homestead-EIP150-era chain spec as though it never changed from Homestead/Frontier.
pub fn new_eip150_test() -> Spec { load(include_bytes!("../../res/ethereum/eip150_test.json")) }
/// Create a new Homestead-EIP150 chain spec as though it never changed from Homestead/Frontier.
/// Create a new Foundation Homestead-EIP161-era chain spec as though it never changed from Homestead/Frontier.
pub fn new_eip161_test() -> Spec { load(include_bytes!("../../res/ethereum/eip161_test.json")) }
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
/// Create a new Foundation Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
pub fn new_transition_test() -> Spec { load(include_bytes!("../../res/ethereum/transition_test.json")) }
/// Create a new Frontier main net chain spec without genesis accounts.
/// Create a new Foundation Mainnet chain spec without genesis accounts.
pub fn new_mainnet_like() -> Spec { load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
/// Create a new Ropsten chain spec.
/// Create a new Foundation Ropsten chain spec.
pub fn new_ropsten() -> Spec { load(include_bytes!("../../res/ethereum/ropsten.json")) }
/// Create a new Morden chain spec.
@@ -112,7 +118,7 @@ mod tests {
#[test]
fn frontier() {
let frontier = new_frontier();
let frontier = new_foundation();
assert_eq!(frontier.state_root(), "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into());
let genesis = frontier.genesis_block();

View File

@@ -28,4 +28,4 @@ mod v10;
pub use self::v10::ToV10;
mod v11;
pub use self::v11::ToV11;
pub use self::v11::TO_V11;

View File

@@ -14,33 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Adds a seventh column for node information.
use util::kvdb::Database;
use util::migration::{Batch, Config, Error, Migration, Progress};
use std::sync::Arc;
use util::migration::ChangeColumns;
/// Copies over data for all existing columns.
#[derive(Default)]
pub struct ToV11(Progress);
impl Migration for ToV11 {
fn pre_columns(&self) -> Option<u32> { Some(6) }
fn columns(&self) -> Option<u32> { Some(7) }
fn version(&self) -> u32 { 11 }
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
// just copy everything over.
let mut batch = Batch::new(config, col);
for (key, value) in source.iter(col) {
self.0.tick();
batch.insert(key.to_vec(), value.to_vec(), dest)?
}
batch.commit(dest)
}
}
/// The migration from v10 to v11.
pub const TO_V11: ChangeColumns = ChangeColumns {
pre_columns: Some(6),
post_columns: Some(7),
version: 11,
};

View File

@@ -32,7 +32,7 @@
//! use ethcore::miner::{Miner, MinerService};
//!
//! fn main() {
//! let miner: Miner = Miner::with_spec(&ethereum::new_frontier());
//! let miner: Miner = Miner::with_spec(&ethereum::new_foundation());
//! // get status
//! assert_eq!(miner.status().transactions_in_pending_queue, 0);
//!

View File

@@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use types::ids::BlockId;
use client::MiningBlockChainClient;
use transaction::SignedTransaction;
use util::{U256, Uint, Mutex};
@@ -45,7 +46,7 @@ impl ServiceTransactionChecker {
debug_assert_eq!(tx.gas_price, U256::zero());
if let Some(ref contract) = *self.contract.lock() {
let do_call = |a, d| client.call_contract(a, d);
let do_call = |a, d| client.call_contract(BlockId::Latest, a, d);
contract.certified(&do_call, &tx.sender())
} else {
Err("contract is not configured".to_owned())

View File

@@ -160,7 +160,7 @@ impl Spec {
fn engine(engine_spec: ethjson::spec::Engine, params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Arc<Engine> {
match engine_spec {
ethjson::spec::Engine::Null => Arc::new(NullEngine::new(params, builtins)),
ethjson::spec::Engine::InstantSeal => Arc::new(InstantSeal::new(params, builtins)),
ethjson::spec::Engine::InstantSeal(instant) => Arc::new(InstantSeal::new(params, instant.params.registrar.map_or_else(Address::new, Into::into), builtins)),
ethjson::spec::Engine::Ethash(ethash) => Arc::new(ethereum::Ethash::new(params, From::from(ethash.params), builtins)),
ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(params, From::from(basic_authority.params), builtins)),
ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(params, From::from(authority_round.params), builtins).expect("Failed to start AuthorityRound consensus engine."),