diff --git a/Cargo.lock b/Cargo.lock
index c2ade4a62..d3611b47a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -952,6 +952,7 @@ dependencies = [
"common-types",
"criterion 0.2.11",
"crossbeam-utils 0.6.6",
+ "derive_more",
"eip-152",
"env_logger",
"error-chain",
diff --git a/crates/accounts/ethkey/src/error.rs b/crates/accounts/ethkey/src/error.rs
new file mode 100644
index 000000000..6d26c1fbe
--- /dev/null
+++ b/crates/accounts/ethkey/src/error.rs
@@ -0,0 +1,88 @@
+// Copyright 2015-2020 Parity Technologies (UK) Ltd.
+// This file is part of OpenEthereum.
+
+// OpenEthereum 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.
+
+// OpenEthereum 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 OpenEthereum. If not, see .
+
+use crypto::Error as CryptoError;
+use std::{error, fmt};
+
+#[derive(Debug)]
+/// Crypto error
+pub enum Error {
+ /// Invalid secret key
+ InvalidSecret,
+ /// Invalid public key
+ InvalidPublic,
+ /// Invalid address
+ InvalidAddress,
+ /// Invalid EC signature
+ InvalidSignature,
+ /// Invalid AES message
+ InvalidMessage,
+ /// IO Error
+ Io(::std::io::Error),
+ /// Custom
+ Custom(String),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let msg = match *self {
+ Error::InvalidSecret => "Invalid secret".into(),
+ Error::InvalidPublic => "Invalid public".into(),
+ Error::InvalidAddress => "Invalid address".into(),
+ Error::InvalidSignature => "Invalid EC signature".into(),
+ Error::InvalidMessage => "Invalid AES message".into(),
+ Error::Io(ref err) => format!("I/O error: {}", err),
+ Error::Custom(ref s) => s.clone(),
+ };
+
+ f.write_fmt(format_args!("Crypto error ({})", msg))
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ format!("{:?}", &self)
+ }
+}
+
+impl Into for Error {
+ fn into(self) -> String {
+ format!("{}", self)
+ }
+}
+
+impl From for Error {
+ fn from(e: CryptoError) -> Error {
+ Error::Custom(e.to_string())
+ }
+}
+
+impl From<::secp256k1::Error> for Error {
+ fn from(e: ::secp256k1::Error) -> Error {
+ match e {
+ ::secp256k1::Error::InvalidMessage => Error::InvalidMessage,
+ ::secp256k1::Error::InvalidPublicKey => Error::InvalidPublic,
+ ::secp256k1::Error::InvalidSecretKey => Error::InvalidSecret,
+ _ => Error::InvalidSignature,
+ }
+ }
+}
+
+impl From<::std::io::Error> for Error {
+ fn from(err: ::std::io::Error) -> Error {
+ Error::Io(err)
+ }
+}
diff --git a/crates/ethcore/Cargo.toml b/crates/ethcore/Cargo.toml
index 1d18f1648..e1b83ebc9 100644
--- a/crates/ethcore/Cargo.toml
+++ b/crates/ethcore/Cargo.toml
@@ -76,6 +76,7 @@ using_queue = { path = "../concensus/miner/using-queue" }
vm = { path = "../vm/vm" }
walkdir = "2.3"
wasm = { path = "../vm/wasm" }
+derive_more = "0.99"
scopeguard = "1.1.0"
[dev-dependencies]
diff --git a/crates/ethcore/res/chainspec/kovan.json b/crates/ethcore/res/chainspec/kovan.json
index e41127c3c..89e988c2e 100644
--- a/crates/ethcore/res/chainspec/kovan.json
+++ b/crates/ethcore/res/chainspec/kovan.json
@@ -88,9 +88,13 @@
"builtin": {
"name": "ecrecover",
"pricing": {
- "linear": {
- "base": 3000,
- "word": 0
+ "0": {
+ "price": {
+ "linear": {
+ "base": 3000,
+ "word": 0
+ }
+ }
}
}
}
@@ -100,9 +104,13 @@
"builtin": {
"name": "sha256",
"pricing": {
- "linear": {
- "base": 60,
- "word": 12
+ "0": {
+ "price": {
+ "linear": {
+ "base": 60,
+ "word": 12
+ }
+ }
}
}
}
@@ -112,9 +120,13 @@
"builtin": {
"name": "ripemd160",
"pricing": {
- "linear": {
- "base": 600,
- "word": 120
+ "0": {
+ "price": {
+ "linear": {
+ "base": 600,
+ "word": 120
+ }
+ }
}
}
}
@@ -124,9 +136,13 @@
"builtin": {
"name": "identity",
"pricing": {
- "linear": {
- "base": 15,
- "word": 3
+ "0": {
+ "price": {
+ "linear": {
+ "base": 15,
+ "word": 3
+ }
+ }
}
}
}
@@ -134,10 +150,13 @@
"0x0000000000000000000000000000000000000005": {
"builtin": {
"name": "modexp",
- "activate_at": "0x4d50f8",
"pricing": {
- "modexp": {
- "divisor": 20
+ "0x4d50f8": {
+ "price": {
+ "modexp": {
+ "divisor": 20
+ }
+ }
}
}
}
@@ -147,11 +166,19 @@
"name": "alt_bn128_add",
"pricing": {
"0x4d50f8": {
- "price": { "alt_bn128_const_operations": { "price": 500 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 500
+ }
+ }
},
"0xd751a5": {
"info": "EIP 1108 transition at block 14_111_141 (0xd751a5)",
- "price": { "alt_bn128_const_operations": { "price": 150 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 150
+ }
+ }
}
}
}
@@ -161,11 +188,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0x4d50f8": {
- "price": { "alt_bn128_const_operations": { "price": 40000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 40000
+ }
+ }
},
"0xd751a5": {
"info": "EIP 1108 transition at block 14_111_141 (0xd751a5)",
- "price": { "alt_bn128_const_operations": { "price": 6000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 6000
+ }
+ }
}
}
}
@@ -175,11 +210,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0x4d50f8": {
- "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 100000,
+ "pair": 80000
+ }
+ }
},
"0xd751a5": {
"info": "EIP 1108 transition at block 14_111_141 (0xd751a5)",
- "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 45000,
+ "pair": 34000
+ }
+ }
}
}
}
@@ -187,10 +232,13 @@
"0x0000000000000000000000000000000000000009": {
"builtin": {
"name": "blake2_f",
- "activate_at": "0xd751a5",
"pricing": {
- "blake2_f": {
- "gas_per_round": 1
+ "0xd751a5": {
+ "price": {
+ "blake2_f": {
+ "gas_per_round": 1
+ }
+ }
}
}
}
@@ -200,13 +248,9 @@
}
},
"nodes": [
- "enode://f6e37b943bad3a78cb8589b1798d30d210ffd39cfcd2c8f2de4f098467fd49c667980100d919da7ca46cd50505d30989abda87f0b9339377de13d6592c22caf8@34.198.49.72:30303",
"enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303",
"enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@3.217.96.11:30303",
- "enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303",
- "enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303",
- "enode://38e6e7fd416293ed120d567a2675fe078c0205ab0671abf16982ce969823bd1f3443d590c18b321dfae7dcbe1f6ba98ef8702f255c3c9822a188abb82c53adca@51.77.66.187:30303",
- "enode://6f289111f7c77c68651b0f4803c3a47bcec801f9c618bb41231a1a24a6dbb9c76f2fdb63ba7a21357c41ebb7f6922c17397c1b5c8f71f7d3ef7965505d4945de@144.217.72.209:30303",
- "enode://b6340eb94c3db1362ee517801389fe21cce6354275376b1006f8ce84f8a5cfa2b836268b3727be9db7cd3e581f356f39da39418c4ec1d63d959abc235d99cd86@145.239.7.213:30303"
+ "enode://740e1c8ea64e71762c71a463a04e2046070a0c9394fcab5891d41301dc473c0cff00ebab5a9bc87fbcb610ab98ac18225ff897bc8b7b38def5975d5ceb0a7d7c@108.61.170.124:30303",
+ "enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@157.230.31.163:30303"
]
}
diff --git a/crates/ethcore/res/chainspec/poacore.json b/crates/ethcore/res/chainspec/poacore.json
index ecdd04c51..83591aee4 100644
--- a/crates/ethcore/res/chainspec/poacore.json
+++ b/crates/ethcore/res/chainspec/poacore.json
@@ -22,7 +22,10 @@
}
},
"blockRewardContractAddress": "0x4d0153D434384128D17243409e02fca1B3EE21D6",
- "blockRewardContractTransition": 5761140
+ "blockRewardContractTransition": 5761140,
+ "randomnessContractAddress": {
+ "14350721": "0x67e90a54AeEA85f21949c645082FE95d77BC1E70"
+ }
}
}
},
@@ -55,12 +58,12 @@
"gasLimit": "0x663BE0"
},
"nodes": [
+ "enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@178.128.175.244:30303",
+ "enode://24f3bf1307b8e9cd53abaaa339db5653aa3db64d579f7f37788b8a9b938420cc10004dc9af4edbb0f0611a57b741a4912dd081917b3d373e4409d788c422e7e6@134.209.22.144:30303",
+ "enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@142.93.3.70:30303",
"enode://6e3d1b39cbd2a9c4f053a27e68fd90d0bac83691dfdc4a13c59f2555078a71e63c5daaee5a82aa6db500512760a5456f86076bf8bbe8011c27c82ed7d6f5fb26@45.77.140.210:30303",
"enode://31dffed97f8fed1f34fe66453280a89cbeeda60cf28f6fbb212ebbefd7c7566a02c1c7d5c00bbbb49b9fa8a49f157e0f786f379ca9bcbf2fea24de70d70a22b6@206.156.242.61:30303",
- "enode://6bdc7553ab2e4914cb47774c1e6d8c8f47ac7c3981891f85f65d06f208ea1bc4d3bf982b330950e0a0cd127efd7145c4df7113159a1d4a06ed722e6c16d0ac6c@45.32.215.190:30303",
- "enode://872d82a24144bc007658fb6fac0dcdfb9b63aeb05ef563a06d0186f2d1e5ffbfc5c4f1244891a8a86ef70682b9d24382e654b305224883698862e2df647a4d23@45.76.236.247:30303",
- "enode://b11fbc6cde81c80be69508aca8ffea8460680a25a9c151b683293f8617282062b8e8e139bf91e88cedf60068a3cf927b0d48832fda5169b58a8f7ce442de6fb4@206.189.76.132:30303",
- "enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@35.175.15.164:30303"
+ "enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@178.128.175.244:30303"
],
"accounts": {
"0x0000000000000000000000000000000000000005": {
@@ -82,11 +85,19 @@
"name": "alt_bn128_add",
"pricing": {
"0": {
- "price": { "alt_bn128_const_operations": { "price": 500 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 500
+ }
+ }
},
"12598600": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_const_operations": { "price": 150 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 150
+ }
+ }
}
}
}
@@ -96,11 +107,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0": {
- "price": { "alt_bn128_const_operations": { "price": 40000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 40000
+ }
+ }
},
"12598600": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_const_operations": { "price": 6000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 6000
+ }
+ }
}
}
}
@@ -110,11 +129,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0": {
- "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 100000,
+ "pair": 80000
+ }
+ }
},
"12598600": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 45000,
+ "pair": 34000
+ }
+ }
}
}
}
diff --git a/crates/ethcore/res/chainspec/poasokol.json b/crates/ethcore/res/chainspec/poasokol.json
index 31fb85c49..5629aaf99 100644
--- a/crates/ethcore/res/chainspec/poasokol.json
+++ b/crates/ethcore/res/chainspec/poasokol.json
@@ -25,7 +25,10 @@
}
},
"blockRewardContractAddress": "0x3145197AD50D7083D0222DE4fCCf67d9BD05C30D",
- "blockRewardContractTransition": 4639000
+ "blockRewardContractTransition": 4639000,
+ "randomnessContractAddress": {
+ "13391641": "0x8f2b78169B0970F11a762e56659Db52B59CBCf1B"
+ }
}
}
},
@@ -60,14 +63,10 @@
"gasLimit": "0x663BE0"
},
"nodes": [
- "enode://bdcd6f875583df2bd8094f08ae58c7c2db6ed67795ca8c0e6415a30721d3657291aec9b933d15e17e0b36ad7a76424a1447ddbfc75809a04f7a0ffef5617dd56@3.91.206.172:30303",
- "enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303",
+ "enode://f11a0f80939b49a28bf99581da9b351a592ec1504b9d32a7dfda79b36510a891e96631239c4166e5c73368c21e9bb3241e7fd6929b899772e5a8fe9a7b7c3af6@45.77.52.149:30303",
+ "enode://e08adce358fc26dfbe1f24ee578dceaa29575ca44a39d9041203131db5135aceba6241840a9b57b1540eeaf7b4eff1aead28a74641be43342c35af454abb31b3@199.247.18.10:30313",
"enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303",
- "enode://0d1e0372f63a3f0b82d66635ea101ecc0f6797788a078805cc933dd93e6a22f7c9fa51ab4e2d21da02d04480ef19f3bbb9a2b41dd1c262085d295a354bb8b0f9@18.217.47.209:30303",
- "enode://875e1bd1b98019a5d6d588c23f68534b75462dd6ecbb3dd058221dbf7aa923f0ab782ab93bb82d42edc9996f7f0816a318bdc761e55c02b95e1169cef66f7edc@159.203.24.35:30303",
- "enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303",
- "enode://182ee200ca134dc4d6390f3d5aadbcd80df0f7f24335830335d142573eacce4eeb919d30e82c5df588034e167e6ba6dd11187502ac9264a71005127f6b146a99@159.203.95.241:30303",
- "enode://b022ff70b5fcaf9596ae5efed99a8198b4ae0578ee9d17b733609d803a75cef95d3a2a18e50dca9a7c3b26139f158c59eaf8b5fb8d1d331c9a46934a78acabe8@206.189.76.128:30303"
+ "enode://f11a0f80939b49a28bf99581da9b351a592ec1504b9d32a7dfda79b36510a891e96631239c4166e5c73368c21e9bb3241e7fd6929b899772e5a8fe9a7b7c3af6@45.77.52.149:30303"
],
"accounts": {
"0000000000000000000000000000000000000005": {
@@ -89,11 +88,19 @@
"name": "alt_bn128_add",
"pricing": {
"0": {
- "price": { "alt_bn128_const_operations": { "price": 500 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 500
+ }
+ }
},
"12095200": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_const_operations": { "price": 150 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 150
+ }
+ }
}
}
}
@@ -103,11 +110,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0": {
- "price": { "alt_bn128_const_operations": { "price": 40000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 40000
+ }
+ }
},
"12095200": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_const_operations": { "price": 6000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 6000
+ }
+ }
}
}
}
@@ -117,11 +132,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0": {
- "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 100000,
+ "pair": 80000
+ }
+ }
},
"12095200": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 45000,
+ "pair": 34000
+ }
+ }
}
}
}
diff --git a/crates/ethcore/res/chainspec/test/authority_round_randomness_contract.json b/crates/ethcore/res/chainspec/test/authority_round_randomness_contract.json
new file mode 100644
index 000000000..2c820ee77
--- /dev/null
+++ b/crates/ethcore/res/chainspec/test/authority_round_randomness_contract.json
@@ -0,0 +1,100 @@
+{
+ "name": "TestAuthorityRoundRandomnessContract",
+ "engine": {
+ "authorityRound": {
+ "params": {
+ "stepDuration": 1,
+ "startStep": 2,
+ "validators": {
+ "list": [
+ "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"
+ ]
+ },
+ "immediateTransitions": true,
+ "maximumEmptySteps": "2",
+ "randomnessContractAddress": {
+ "0": "0x0000000000000000000000000000000000000042"
+ }
+ }
+ }
+ },
+ "params": {
+ "gasLimitBoundDivisor": "0x0400",
+ "accountStartNonce": "0x0",
+ "maximumExtraDataSize": "0x20",
+ "minGasLimit": "0x1388",
+ "networkID" : "0x69",
+ "eip140Transition": "0x0",
+ "eip211Transition": "0x0",
+ "eip214Transition": "0x0",
+ "eip658Transition": "0x0"
+ },
+ "genesis": {
+ "seal": {
+ "authorityRound": {
+ "step": "0x0",
+ "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ "difficulty": "0x20000",
+ "author": "0x0000000000000000000000000000000000000000",
+ "timestamp": "0x00",
+ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "extraData": "0x",
+ "gasLimit": "0x222222"
+ },
+ "accounts": {
+ "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "100000000000" },
+ "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
+ "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 } } } },
+ "0000000000000000000000000000000000000005": { "balance": "1", "builtin": { "name": "modexp", "activate_at": 0, "pricing": { "modexp": { "divisor": 20 } } } },
+ "0000000000000000000000000000000000000006": {
+ "balance": "1",
+ "builtin": {
+ "name": "alt_bn128_add",
+ "pricing": {
+ "0x0": {
+ "price": { "linear": { "base": 500, "word": 0 }}
+ },
+ "0x7fffffffffffff": {
+ "price": { "linear": { "base": 150, "word": 0 }}
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000007": {
+ "balance": "1",
+ "builtin": {
+ "name": "alt_bn128_mul",
+ "pricing": {
+ "0x0": {
+ "price": { "linear": { "base": 40000, "word": 0 }}
+ },
+ "0x7fffffffffffff": {
+ "price": { "linear": { "base": 6000, "word": 0 }}
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000008": {
+ "balance": "1",
+ "builtin": {
+ "name": "alt_bn128_pairing",
+ "pricing": {
+ "0x0": {
+ "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
+ },
+ "0x7fffffffffffff": {
+ "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000042": {
+ "balance": "1",
+ "constructor": "608060405234801561001057600080fd5b50610820806100206000396000f3fe608060405234801561001057600080fd5b50600436106100ec576000357c01000000000000000000000000000000000000000000000000000000009004806363f160e6116100a95780637a3e286b116100835780637a3e286b14610378578063baf11cab14610380578063c358ced0146103ac578063fe7d567d146103b4576100ec565b806363f160e614610285578063695e89f6146102c557806374ce906714610370576100ec565b806304fdb016146100f15780630b61ba8514610192578063209652551461020b5780632e8a8dd5146102255780633fa4f245146102515780635580e58b14610259575b600080fd5b61011d6004803603604081101561010757600080fd5b5080359060200135600160a060020a03166103d1565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561015757818101518382015260200161013f565b50505050905090810190601f1680156101845780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610209600480360360408110156101a857600080fd5b813591908101906040810160208201356401000000008111156101ca57600080fd5b8201836020820111156101dc57600080fd5b803590602001918460018302840111640100000000831117156101fe57600080fd5b509092509050610475565b005b6102136104fa565b60408051918252519081900360200190f35b6102136004803603604081101561023b57600080fd5b5080359060200135600160a060020a0316610501565b61021361051b565b6102136004803603604081101561026f57600080fd5b5080359060200135600160a060020a0316610521565b6102b16004803603604081101561029b57600080fd5b5080359060200135600160a060020a031661053e565b604080519115158252519081900360200190f35b6102f1600480360360408110156102db57600080fd5b5080359060200135600160a060020a0316610568565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561033457818101518382015260200161031c565b50505050905090810190601f1680156103615780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b6102b1610639565b610213610649565b6102b16004803603604081101561039657600080fd5b5080359060200135600160a060020a0316610654565b6102b161067c565b610209600480360360208110156103ca57600080fd5b5035610687565b600160208181526000938452604080852082529284529282902080548351600293821615610100026000190190911692909204601f8101859004850283018501909352828252909290919083018282801561046d5780601f106104425761010080835404028352916020019161046d565b820191906000526020600020905b81548152906001019060200180831161045057829003601f168201915b505050505081565b41331461048157600080fd5b61048d60014303610735565b61049657600080fd5b60006104a460014303610740565b90506104b08133610654565b156104ba57600080fd5b600081815260208181526040808320338085529083528184208890558484526001835281842090845290915290206104f3908484610753565b5050505050565b6003545b90565b600060208181529281526040808220909352908152205481565b60035481565b600260209081526000928352604080842090915290825290205481565b6000918252600260209081526040808420600160a060020a03939093168452919052902054151590565b600082815260208181526040808320600160a060020a03851680855290835281842054868552600180855283862092865291845282852080548451600294821615610100026000190190911693909304601f810186900486028401860190945283835260609491939092918391908301828280156106275780601f106105fc57610100808354040283529160200191610627565b820191906000526020600020905b81548152906001019060200180831161060a57829003601f168201915b50505050509050915091509250929050565b600061064443610735565b905090565b600061064443610740565b600091825260208281526040808420600160a060020a03939093168452919052902054151590565b600061064443610747565b41331461069357600080fd5b61069f60014303610747565b6106a857600080fd5b60006106b660014303610740565b90506106c2813361053e565b156106cc57600080fd5b60408051602080820185905282518083038201815291830183528151918101919091206000848152808352838120338252909252919020541461070e57600080fd5b60009081526002602090815260408083203384529091529020819055600380549091189055565b600360069091061090565b6006900490565b60036006909106101590565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106107945782800160ff198235161785556107c1565b828001600101855582156107c1579182015b828111156107c15782358255916020019190600101906107a6565b506107cd9291506107d1565b5090565b6104fe91905b808211156107cd57600081556001016107d756fea265627a7a7230582008bb7311af9026bd70ddb998741333d414a366275b9b433a2943bbd6bedc27ae64736f6c634300050a0032"
+ }
+ }
+}
\ No newline at end of file
diff --git a/crates/ethcore/res/chainspec/test/contract_ver_3.sol b/crates/ethcore/res/chainspec/test/contract_ver_3.sol
new file mode 100644
index 000000000..dd28c8e33
--- /dev/null
+++ b/crates/ethcore/res/chainspec/test/contract_ver_3.sol
@@ -0,0 +1,60 @@
+pragma solidity ^0.4.20;
+
+// Adapted from https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
+// and: https://github.com/poanetwork/posdao-contracts/blob/master/contracts/TxPermission.sol
+
+contract TxPermission {
+ /// Allowed transaction types mask
+ uint32 constant None = 0;
+ uint32 constant All = 0xffffffff;
+ uint32 constant Basic = 0x01;
+ uint32 constant Call = 0x02;
+ uint32 constant Create = 0x04;
+ uint32 constant Private = 0x08;
+
+ /// Contract name
+ function contractName() public constant returns (string) {
+ return "TX_PERMISSION_CONTRACT";
+ }
+
+ /// Contract name hash
+ function contractNameHash() public constant returns (bytes32) {
+ return keccak256(contractName());
+ }
+
+ /// Contract version
+ function contractVersion() public constant returns (uint256) {
+ return 3;
+ }
+
+ /// @dev Defines the allowed transaction types which may be initiated by the specified sender with
+ /// the specified gas price and data. Used by the Parity engine each time a transaction is about to be
+ /// included into a block. See https://wiki.parity.io/Permissioning.html#how-it-works-1
+ /// @param _sender Transaction sender address.
+ /// @param _to Transaction recipient address. If creating a contract, the `_to` address is zero.
+ /// @param _value Transaction amount in wei.
+ /// @param _gasPrice Gas price in wei for the transaction.
+ /// @param _data Transaction data.
+ /// @return `uint32 typesMask` - Set of allowed transactions for `_sender` depending on tx `_to` address,
+ /// `_gasPrice`, and `_data`. The result is represented as a set of flags:
+ /// 0x01 - basic transaction (e.g. ether transferring to user wallet);
+ /// 0x02 - contract call;
+ /// 0x04 - contract creation;
+ /// 0x08 - private transaction.
+ /// `bool cache` - If `true` is returned, the same permissions will be applied from the same
+ /// `_sender` without calling this contract again.
+ function allowedTxTypes(
+ address _sender,
+ address _to,
+ uint256 _value,
+ uint256 _gasPrice,
+ bytes memory _data
+ )
+ public
+ view
+ returns(uint32 typesMask, bool cache)
+ {
+ if (_gasPrice > 0 || _data.length < 4) return (All, false);
+ return (None, false);
+ }
+}
\ No newline at end of file
diff --git a/crates/ethcore/res/chainspec/test/contract_ver_3_genesis.json b/crates/ethcore/res/chainspec/test/contract_ver_3_genesis.json
new file mode 100644
index 000000000..28c44d8de
--- /dev/null
+++ b/crates/ethcore/res/chainspec/test/contract_ver_3_genesis.json
@@ -0,0 +1,44 @@
+{
+ "name": "TestNodeFilterContract",
+ "engine": {
+ "authorityRound": {
+ "params": {
+ "stepDuration": 1,
+ "startStep": 2,
+ "validators": {
+ "contract": "0x0000000000000000000000000000000000000000"
+ }
+ }
+ }
+ },
+ "params": {
+ "accountStartNonce": "0x0",
+ "maximumExtraDataSize": "0x20",
+ "minGasLimit": "0x1388",
+ "networkID" : "0x69",
+ "gasLimitBoundDivisor": "0x0400",
+ "transactionPermissionContract": "0x0000000000000000000000000000000000000005",
+ "transactionPermissionContractTransition": "1"
+ },
+ "genesis": {
+ "seal": {
+ "generic": "0xc180"
+ },
+ "difficulty": "0x20000",
+ "author": "0x0000000000000000000000000000000000000000",
+ "timestamp": "0x00",
+ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "extraData": "0x",
+ "gasLimit": "0x222222"
+ },
+ "accounts": {
+ "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
+ "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
+ "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
+ "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
+ "0000000000000000000000000000000000000005": {
+ "balance": "1",
+ "constructor": "6060604052341561000f57600080fd5b61035e8061001e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063469ab1e31461006757806375d0c0dc14610098578063a0a8e46014610126578063b9056afa1461014f575b600080fd5b341561007257600080fd5b61007a610227565b60405180826000191660001916815260200191505060405180910390f35b34156100a357600080fd5b6100ab610298565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100eb5780820151818401526020810190506100d0565b50505050905090810190601f1680156101185780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013157600080fd5b6101396102db565b6040518082815260200191505060405180910390f35b341561015a57600080fd5b6101fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001909190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506102e4565b604051808363ffffffff1663ffffffff168152602001821515151581526020019250505060405180910390f35b6000610231610298565b6040518082805190602001908083835b6020831015156102665780518252602082019150602081019050602083039250610241565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905090565b6102a061031e565b6040805190810160405280601681526020017f54585f5045524d495353494f4e5f434f4e545241435400000000000000000000815250905090565b60006003905090565b60008060008411806102f7575060048351105b1561030c5763ffffffff600091509150610314565b600080915091505b9550959350505050565b6020604051908101604052806000815250905600a165627a7a72305820be61565bc09fec6e9223a1fecd2e94783ca5c6f506c03f71d479a8c3285493310029"
+ }
+ }
+}
\ No newline at end of file
diff --git a/crates/ethcore/res/chainspec/test/validator_contract.json b/crates/ethcore/res/chainspec/test/validator_contract.json
index 9d007ac23..841fd2249 100644
--- a/crates/ethcore/res/chainspec/test/validator_contract.json
+++ b/crates/ethcore/res/chainspec/test/validator_contract.json
@@ -37,7 +37,7 @@
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0000000000000000000000000000000000000005": {
"balance": "1",
- "constructor": "60a06040819052737d577a597b2742b498cb5cf0c26cdcd726d39e6e60609081527382a978b3f5962a5b0957d9ee9eef472ee55b42f1608052600080546002825581805290927f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639182019291905b828111156100a25782518254600160a060020a031916600160a060020a0390911617825560209092019160019091019061006d565b5b506100cd9291505b808211156100c9578054600160a060020a03191681556001016100ab565b5090565b505034610000575b60005b60005481101561012f578060016000600084815481101561000057906000526020600020900160005b9054600160a060020a036101009290920a90041681526020810191909152604001600020555b6001016100d8565b5b505b610453806101416000396000f3006060604052361561005c5763ffffffff60e060020a60003504166335aa2e4481146100615780634d238c8e1461008d578063b7ab4db5146100a8578063c476dd4014610110578063d69f13bb14610172578063d8f2e0bf14610190575b610000565b34610000576100716004356101b9565b60408051600160a060020a039092168252519081900360200190f35b34610000576100a6600160a060020a03600435166101e9565b005b34610000576100b5610260565b60408051602080825283518183015283519192839290830191858101910280838382156100fd575b8051825260208311156100fd57601f1990920191602091820191016100dd565b5050509050019250505060405180910390f35b3461000057604080516020600460443581810135601f81018490048402850184019095528484526100a6948235600160a060020a03169460248035956064949293919092019181908401838280828437509496506102ca95505050505050565b005b34610000576100a6600160a060020a03600435166024356103eb565b005b3461000057610071610418565b60408051600160a060020a039092168252519081900360200190f35b600081815481101561000057906000526020600020900160005b915054906101000a9004600160a060020a031681565b6000805480600101828181548183558181151161022b5760008381526020902061022b9181019083015b808211156102275760008155600101610213565b5090565b5b505050916000526020600020900160005b8154600160a060020a038086166101009390930a92830292021916179055505b50565b60408051602081810183526000808352805484518184028101840190955280855292939290918301828280156102bf57602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116102a1575b505050505090505b90565b6000805460001981019081101561000057906000526020600020900160005b9054906101000a9004600160a060020a031660006001600086600160a060020a0316600160a060020a0316815260200190815260200160002054815481101561000057906000526020600020900160005b8154600160a060020a039384166101009290920a918202918402191617905583166000908152600160205260408120819055805460001981019081101561000057906000526020600020900160005b6101000a815490600160a060020a03021916905560008054809190600190038154818355818115116103e0576000838152602090206103e09181019083015b808211156102275760008155600101610213565b5090565b5b505050505b505050565b6002805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0384161790555b5050565b600254600160a060020a0316815600a165627a7a72305820f7876e17abd5f0927fff16788b4b3c9028ed64e6db740d788b07fc5f0a8f10920029"
+ "constructor": "60c0604052737d577a597b2742b498cb5cf0c26cdcd726d39e6e60809081527382a978b3f5962a5b0957d9ee9eef472ee55b42f160a05261004390600290816100a3565b5034801561005057600080fd5b5060005b60025481101561009d5780600560006002848154811061007057fe5b6000918252602080832090910154600160a060020a03168352820192909252604001902055600101610054565b5061012f565b8280548282559060005260206000209081019282156100f8579160200282015b828111156100f85782518254600160a060020a031916600160a060020a039091161782556020909201916001909101906100c3565b50610104929150610108565b5090565b61012c91905b80821115610104578054600160a060020a031916815560010161010e565b90565b6108e28061013e6000396000f3fe608060405234801561001057600080fd5b50600436106100d1576000357c010000000000000000000000000000000000000000000000000000000090048063b56b366b1161008e578063b56b366b14610206578063b7ab4db514610282578063c476dd401461028a578063cbd2d5281461030f578063d69f13bb14610345578063d8f2e0bf14610371576100d1565b806335aa2e44146100d65780633d3b54581461010f578063752862111461012b5780639300c9261461013557806393b4e25e146101d8578063a92252ae146101e0575b600080fd5b6100f3600480360360208110156100ec57600080fd5b5035610379565b60408051600160a060020a039092168252519081900360200190f35b6101176103a0565b604080519115158252519081900360200190f35b6101336103a7565b005b6101336004803603602081101561014b57600080fd5b81019060208101813564010000000081111561016657600080fd5b82018360208201111561017857600080fd5b8035906020019184602083028401116401000000008311171561019a57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506103a9945050505050565b6101336103c8565b610117600480360360208110156101f657600080fd5b5035600160a060020a031661044f565b6102326004803603604081101561021c57600080fd5b50600160a060020a038135169060200135610464565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561026e578181015183820152602001610256565b505050509050019250505060405180910390f35b6102326104e3565b610133600480360360608110156102a057600080fd5b600160a060020a03823516916020810135918101906060810160408201356401000000008111156102d057600080fd5b8201836020820111156102e257600080fd5b8035906020019184600183028401116401000000008311171561030457600080fd5b509092509050610545565b6101176004803603606081101561032557600080fd5b50600160a060020a038135811691602081013590911690604001356106eb565b6101336004803603604081101561035b57600080fd5b50600160a060020a038135169060200135610788565b6100f36107b8565b6002818154811061038657fe5b600091825260209091200154600160a060020a0316905081565b4315155b90565b565b80516103bc9060029060208401906107c7565b506103c56103c8565b50565b60014303407f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c8960026040518080602001828103825283818154815260200191508054801561043f57602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610421575b50509250505060405180910390a2565b60016020526000908152604090205460ff1681565b600160a060020a03821660009081526003602090815260408083208484528252918290208054835181840281018401909452808452606093928301828280156104d657602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116104b8575b5050505050905092915050565b6060600280548060200260200160405190810160405280929190818152602001828054801561053b57602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161051d575b5050505050905090565b600160a060020a0384166000818152600360209081526040808320878452825280832080546001818101835591855283852001805473ffffffffffffffffffffffffffffffffffffffff1916339081179091558585526004845282852089865284528285208186528452828520805460ff19908116841790915586865282855283862080549091169092179091556005909252909120546002805492939290919081106105ee57fe5b600091825260209091200154600160a060020a031614156106e45760028054600019810190811061061b57fe5b6000918252602080832090910154600160a060020a038881168452600590925260409092205460028054929093169291811061065357fe5b6000918252602080832091909101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039485161790559187168152600590915260408120556002805460001981019081106106a957fe5b6000918252602090912001805473ffffffffffffffffffffffffffffffffffffffff1916905560028054906106e2906000198301610839565b505b5050505050565b60004380831115610700576000915050610781565b60648111801561071257508260648203115b15610721576000915050610781565b600160a060020a03841660009081526001602052604090205460ff161561074c576000915050610781565b5050600160a060020a03808316600090815260046020908152604080832085845282528083209387168352929052205460ff16155b9392505050565b506000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054600160a060020a031681565b828054828255906000526020600020908101928215610829579160200282015b82811115610829578251825473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039091161782556020909201916001909101906107e7565b50610835929150610862565b5090565b81548183558181111561085d5760008381526020902061085d918101908301610893565b505050565b6103a491905b8082111561083557805473ffffffffffffffffffffffffffffffffffffffff19168155600101610868565b6103a491905b80821115610835576000815560010161089956fea265627a7a7231582073e77a97d79ab4382d5a28644654c335489310e72aa6c8f737e424bdb6c2bbd664736f6c63430005100032"
},
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "1606938044258990275541962092341162602522202993782792835301376" },
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
diff --git a/crates/ethcore/res/chainspec/test/validator_contract.sol b/crates/ethcore/res/chainspec/test/validator_contract.sol
new file mode 100644
index 000000000..568c8dd36
--- /dev/null
+++ b/crates/ethcore/res/chainspec/test/validator_contract.sol
@@ -0,0 +1,119 @@
+// Source for the test AuRa validator set contract. DO NOT USE IN PRODUCTION.
+//
+// Contains POSDAO features. The full POSDAO ValidatorSet contract production code is available at
+// https://github.com/poanetwork/posdao-contracts/blob/master/contracts/ValidatorSetAuRa.sol
+//
+// The bytecode of this contract is included in `validator_contract.json` as the
+// constructor of address `0x0000..0005`.
+
+pragma solidity ^0.5.0;
+
+contract TestValidatorSet {
+
+ address public disliked; // contains the address of validator reported by `reportBenign`
+ mapping(address => bool) public isValidatorBanned; // if the validator is banned by `reportMalicious`
+
+ // The initial set of validators
+ address[] public validators = [
+ 0x7d577a597B2742b498Cb5Cf0C26cDCD726d39E6e,
+ 0x82A978B3f5962A5b0957d9ee9eEf472EE55B42F1
+ ];
+
+ // The mappings used by POSDAO features testing (see `reportMalicious` and `shouldValidatorReport` functions below)
+ mapping(address => mapping(uint256 => address[])) private _maliceReportedForBlock;
+ mapping(address => mapping(uint256 => mapping(address => bool))) private _maliceReportedForBlockMapped;
+ mapping(address => uint256) private _validatorIndex;
+
+ // The standard event to notify the engine about the validator set changing in the contract
+ event InitiateChange(bytes32 indexed parentHash, address[] newSet);
+
+ constructor() public {
+ // Initialize validator indices to be able to correctly remove
+ // a malicious validator from the validator set later
+ for (uint i = 0; i < validators.length; i++) {
+ _validatorIndex[validators[i]] = i;
+ }
+ }
+
+ // Emits an `InitiateChange` event with the current (or new) validator set
+ function emitInitiateChange() public {
+ emit InitiateChange(blockhash(block.number - 1), validators);
+ }
+
+ // Applies a validator set change in production code. Does nothing in the test
+ function finalizeChange() pure public {}
+
+ // Benign validator behaviour report. Kept here for regression testing
+ function reportBenign(address _validator, uint256) public {
+ disliked = _validator;
+ }
+
+ // Removes a malicious validator from the list
+ function reportMalicious(address _validator, uint256 _blockNum, bytes calldata) external {
+ address reportingValidator = msg.sender;
+
+ // Mark the `_validator` as reported by `reportingValidator` for the block `_blockNum`
+ _maliceReportedForBlock[_validator][_blockNum].push(reportingValidator);
+ _maliceReportedForBlockMapped[_validator][_blockNum][reportingValidator] = true;
+ isValidatorBanned[_validator] = true;
+
+ // If the passed validator is in the validator set
+ if (validators[_validatorIndex[_validator]] == _validator) {
+ // Remove the validator from the set
+ validators[_validatorIndex[_validator]] = validators[validators.length - 1];
+ delete _validatorIndex[_validator];
+ delete validators[validators.length - 1];
+ validators.length--;
+ }
+ }
+
+ // Tests validator set changing and emitting the `InitiateChange` event
+ function setValidators(address[] memory _validators) public {
+ validators = _validators;
+ emitInitiateChange();
+ }
+
+ // Checks if `emitInitiateChange` can be called (used by POSDAO tests)
+ function emitInitiateChangeCallable() view public returns(bool) {
+ return block.number > 0;
+ }
+
+ // Returns the current validator set
+ function getValidators() public view returns(address[] memory) {
+ return validators;
+ }
+
+ // Returns the list of all validators that reported the given validator
+ // as malicious for the given block. Used by POSDAO tests
+ function maliceReportedForBlock(address _validator, uint256 _blockNum) public view returns(address[] memory) {
+ return _maliceReportedForBlock[_validator][_blockNum];
+ }
+
+ // Returns a boolean flag indicating whether the specified validator
+ // should report about some validator's misbehaviour at the specified block.
+ // Used by POSDAO tests.
+ // `_reportingValidator` is the address of validator who reports.
+ // `_maliciousValidator` is the address of malicious validator.
+ // `_blockNumber` is the block number at which the malicious validator misbehaved.
+ function shouldValidatorReport(
+ address _reportingValidator,
+ address _maliciousValidator,
+ uint256 _blockNumber
+ ) public view returns(bool) {
+ uint256 currentBlock = block.number;
+ if (_blockNumber > currentBlock) {
+ return false;
+ }
+ if (currentBlock > 100 && currentBlock - 100 > _blockNumber) {
+ return false;
+ }
+ if (isValidatorBanned[_maliciousValidator]) {
+ // We shouldn't report the malicious validator
+ // as it has already been reported and banned
+ return false;
+ }
+ // Return `false` if already reported by the same `_reportingValidator` for the same `_blockNumber`
+ return !_maliceReportedForBlockMapped[_maliciousValidator][_blockNumber][_reportingValidator];
+ }
+
+}
\ No newline at end of file
diff --git a/crates/ethcore/res/chainspec/xdai.json b/crates/ethcore/res/chainspec/xdai.json
index cecc79171..2bb2d2480 100644
--- a/crates/ethcore/res/chainspec/xdai.json
+++ b/crates/ethcore/res/chainspec/xdai.json
@@ -11,15 +11,27 @@
"validators": {
"multi": {
"0": {
- "list": ["0xcace5b3c29211740e595850e80478416ee77ca21"]
+ "list": [
+ "0xcace5b3c29211740e595850e80478416ee77ca21"
+ ]
},
"1300": {
"safeContract": "0x22e1229a2c5b95a60983b5577f745a603284f535"
+ },
+ "9186425": {
+ "contract": "0xB87BE9f7196F2AE084Ca1DE6af5264292976e013"
}
}
},
"blockRewardContractAddress": "0x867305d19606aadba405ce534e303d0e225f9556",
- "blockRewardContractTransition": 1310
+ "blockRewardContractTransition": 1310,
+ "blockRewardContractTransitions": {
+ "9186425": "0x481c034c6d9441db23Ea48De68BCAe812C5d39bA"
+ },
+ "randomnessContractAddress": {
+ "9186425": "0x5870b0527DeDB1cFBD9534343Feda1a41Ce47766"
+ },
+ "posdaoTransition": 9186425
}
}
},
@@ -42,7 +54,9 @@
"eip1706Transition": 7298030,
"eip1884Transition": 7298030,
"eip2028Transition": 7298030,
- "registrar": "0x1ec97dc137f5168af053c24460a1200502e1a9d2"
+ "registrar": "0x6B53721D4f2Fb9514B85f5C49b197D857e36Cf03",
+ "transactionPermissionContract": "0x7Dd7032AA75A37ea0b150f57F899119C7379A78b",
+ "transactionPermissionContractTransition": 9186425
},
"genesis": {
"seal": {
@@ -54,6 +68,25 @@
"difficulty": "0x20000",
"gasLimit": "0x989680"
},
+ "nodes": [
+ "enode://4716883567b5317aad93ea28e707fad0631fb4aa5ac7c5fbd485380b01d8801c21a8cbf4d6ee3a2c9b2b070a270a49d4a2a0da9e1d47a1f433dafbaf7b2edd06@157.245.92.222:30303",
+ "enode://ab7f6c633ba2dc54795dfd2c739ba7d964f499541c0b8d8ba9d275bd3df1b789470a21a921a469fa515a3dfccc96a434a3fd016a169d88d0043fc6744f34288e@67.205.180.17:30303",
+ "enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@67.205.145.143:30303",
+ "enode://bd75111424c42c349fc255db017ac0be370b37b558627e3bbc41319071ef7642c04cdbd2b674193a99aa35d67a83016ab293b8ab87ed4a4606e69f114ac95535@157.230.185.80:30303",
+ "enode://bd75111424c42c349fc255db017ac0be370b37b558627e3bbc41319071ef7642c04cdbd2b674193a99aa35d67a83016ab293b8ab87ed4a4606e69f114ac95535@161.35.51.60:30303",
+ "enode://ef94ffb10c440dd990c5c4be1c85046f5f7329ba60d23db7a68c8b91b6a721081f8190369f3a32f3c02d213127b2066eb42ee0444998d354ba0923378522acb3@161.35.62.72:30303",
+ "enode://4a0eadf22d6a37c5596fd2df2a53a26a5b59dd863e67246ab94e6a81b31765e08d9f70a4dd9683221e63cc2120c8a808a6a457455bd658bdf49c688c62db2011@51.81.244.170:30303",
+ "enode://e75a1e9f080bd6012b39321c0f2d984567172625280b3e7362e962a42578c5e79c847b3eb83aa7e2a4cdeefbfadf0c36ed2719cad1d5e6377ccd6ebe314cc6bc@64.227.97.130:30303",
+ "enode://6674773f7aac78d5527fa90c847dcbca198de4081306406a8fec5c15f7a2e141362344041291dd10d0aafa7706a3d8f21a08b6f6834a5b1aab9cccd8ca35ccee@143.110.226.15:30303",
+ "enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@134.122.24.231:30303",
+ "enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@67.205.145.143:30303",
+ "enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@162.243.164.98:30303",
+ "enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@167.99.4.175:30303",
+ "enode://da2449aaba873c40c6daf764de55f4b9eae24c4738daec893ef95b6ada96463c6b9624f8e376e1073d21dd820c5bb361e14575121b09bbd7735b6b556ee1b768@67.205.176.117:30303",
+ "enode://e8c7a0db430429bb374c981438c0dbd95e565088a483388aa46d8377a3bd62f02cd83d7e2c7e5fc77606141bfef29d23d4285a7c1d9b7e743cf3029314506df7@80.240.16.221:30303",
+ "enode://2cde5ae04ed57bba5bac8311a97be056838d5304bc3bcee698066e5fc532e846f785198f87e8b84b57b03622a22aac5dd2853c5203d1ece2c9f25b48487d145b@149.28.57.8:30303",
+ "enode://90b0a0e74a9a1ad258531b4ceec25587d8b52ff2cfb36206a34bf6ba1a8d21b2abd20da13260102508a2ac67afbeb2d2ab7a5e9d6bea3bce845cd81e655585cc@45.77.110.159:30303"
+ ],
"accounts": {
"0x0000000000000000000000000000000000000005": {
"builtin": {
@@ -74,11 +107,19 @@
"name": "alt_bn128_add",
"pricing": {
"0": {
- "price": { "alt_bn128_const_operations": { "price": 500 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 500
+ }
+ }
},
"7298030": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_const_operations": { "price": 150 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 150
+ }
+ }
}
}
}
@@ -88,11 +129,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0": {
- "price": { "alt_bn128_const_operations": { "price": 40000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 40000
+ }
+ }
},
"7298030": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_const_operations": { "price": 6000 }}
+ "price": {
+ "alt_bn128_const_operations": {
+ "price": 6000
+ }
+ }
}
}
}
@@ -102,11 +151,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0": {
- "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 100000,
+ "pair": 80000
+ }
+ }
},
"7298030": {
"info": "EIP 1108 transition",
- "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
+ "price": {
+ "alt_bn128_pairing": {
+ "base": 45000,
+ "pair": 34000
+ }
+ }
}
}
}
@@ -190,10 +249,6 @@
}
}
}
- },
- "nodes": [
- "enode://1c19ba0a77dd663b843c33beb9020e7eb41fc34b47b98424dbc427f74692115d74c7c27b6c0aa2b59bb1d8f710650cae1090153d10b6909ca7bdcdfb183b1c59@54.39.190.172:30303",
- "enode://c1c3a604950119f82d78189792b73f5a82a239017c77465e3c32fc51c1d758a9a772ffddd58436d465342f2cfa6d4a442a49e526743f4d8354d7c5ce794c3ee5@95.179.222.48:30303"
- ]
+ }
}
diff --git a/crates/ethcore/res/contracts/authority_round_random.json b/crates/ethcore/res/contracts/authority_round_random.json
new file mode 100644
index 000000000..5b41014a5
--- /dev/null
+++ b/crates/ethcore/res/contracts/authority_round_random.json
@@ -0,0 +1,133 @@
+[{
+ "constant": false,
+ "inputs": [{
+ "name": "_secretHash",
+ "type": "bytes32"
+ },
+ {
+ "name": "_cipher",
+ "type": "bytes"
+ }
+ ],
+ "name": "commitHash",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+},
+{
+ "constant": false,
+ "inputs": [{
+ "name": "_number",
+ "type": "uint256"
+ }],
+ "name": "revealNumber",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+},
+{
+ "constant": true,
+ "inputs": [],
+ "name": "currentCollectRound",
+ "outputs": [{
+ "name": "",
+ "type": "uint256"
+ }],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+},
+{
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_collectRound",
+ "type": "uint256"
+ },
+ {
+ "name": "_miningAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getCommitAndCipher",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ },
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+},
+{
+ "constant": true,
+ "inputs": [{
+ "name": "_collectRound",
+ "type": "uint256"
+ },
+ {
+ "name": "_validator",
+ "type": "address"
+ }
+ ],
+ "name": "isCommitted",
+ "outputs": [{
+ "name": "",
+ "type": "bool"
+ }],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+},
+{
+ "constant": true,
+ "inputs": [],
+ "name": "isCommitPhase",
+ "outputs": [{
+ "name": "",
+ "type": "bool"
+ }],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+},
+{
+ "constant": true,
+ "inputs": [],
+ "name": "isRevealPhase",
+ "outputs": [{
+ "name": "",
+ "type": "bool"
+ }],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+},
+{
+ "constant": true,
+ "inputs": [{
+ "name": "_collectRound",
+ "type": "uint256"
+ },
+ {
+ "name": "_validator",
+ "type": "address"
+ }
+ ],
+ "name": "sentReveal",
+ "outputs": [{
+ "name": "",
+ "type": "bool"
+ }],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+}
+]
\ No newline at end of file
diff --git a/crates/ethcore/res/contracts/block_gas_limit.json b/crates/ethcore/res/contracts/block_gas_limit.json
new file mode 100644
index 000000000..8ca9cf969
--- /dev/null
+++ b/crates/ethcore/res/contracts/block_gas_limit.json
@@ -0,0 +1,16 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "blockGasLimit",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/crates/ethcore/res/contracts/test_authority_round_random.json b/crates/ethcore/res/contracts/test_authority_round_random.json
new file mode 100644
index 000000000..beab82ab4
--- /dev/null
+++ b/crates/ethcore/res/contracts/test_authority_round_random.json
@@ -0,0 +1,265 @@
+[
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "ciphers",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_collectRound",
+ "type": "uint256"
+ },
+ {
+ "name": "_miningAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getCipher",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_secretHash",
+ "type": "bytes32"
+ },
+ {
+ "name": "_cipher",
+ "type": "bytes"
+ }
+ ],
+ "name": "commitHash",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getValue",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "hashes",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "value",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "secrets",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_collectRound",
+ "type": "uint256"
+ },
+ {
+ "name": "_miningAddress",
+ "type": "address"
+ }
+ ],
+ "name": "sentReveal",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "isCommitPhase",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "_number",
+ "type": "uint256"
+ }
+ ],
+ "name": "revealNumber",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getRound",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_collectRound",
+ "type": "uint256"
+ },
+ {
+ "name": "_miningAddress",
+ "type": "address"
+ }
+ ],
+ "name": "isCommitted",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "isRevealPhase",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_collectRound",
+ "type": "uint256"
+ },
+ {
+ "name": "_miningAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getCommit",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/crates/ethcore/res/contracts/test_authority_round_random.sol b/crates/ethcore/res/contracts/test_authority_round_random.sol
new file mode 100644
index 000000000..66c971786
--- /dev/null
+++ b/crates/ethcore/res/contracts/test_authority_round_random.sol
@@ -0,0 +1,101 @@
+pragma solidity 0.5.10;
+
+/// @dev Randomness test contract based on https://github.com/poanetwork/posdao-contracts.
+/// Generates and stores random numbers in a RANDAO manner and accumulates a random seed.
+contract Random {
+ mapping(uint256 => mapping(address => bytes32)) public hashes;
+ mapping(uint256 => mapping(address => bytes)) public ciphers;
+ mapping(uint256 => mapping(address => uint256)) public secrets;
+ uint256 public value;
+
+ /// @dev Called by the validator's node to store a hash and a cipher of the validator's secret on each collection
+ /// round. The validator's node must use its mining address to call this function.
+ /// This function can only be called once per collection round (during the `commits phase`).
+ /// @param _secretHash The Keccak-256 hash of the validator's secret.
+ /// @param _cipher The cipher of the validator's secret. Can be used by the node to decrypt and reveal.
+ function commitHash(bytes32 _secretHash, bytes calldata _cipher) external {
+ require(block.coinbase == msg.sender);
+ require(_isCommitPhase(block.number - 1));
+ uint256 round = _collectRound(block.number - 1);
+ require(!isCommitted(round, msg.sender));
+ hashes[round][msg.sender] = _secretHash;
+ ciphers[round][msg.sender] = _cipher;
+ }
+
+ /// @dev Called by the validator's node to XOR its secret with the current random seed.
+ /// The validator's node must use its mining address to call this function.
+ /// This function can only be called once per collection round (during the `reveals phase`).
+ /// @param _number The validator's secret.
+ function revealNumber(uint256 _number) external {
+ require(block.coinbase == msg.sender);
+ require(_isRevealPhase(block.number - 1));
+ uint256 round = _collectRound(block.number - 1);
+ require(!sentReveal(round, msg.sender));
+ require(hashes[round][msg.sender] == keccak256(abi.encodePacked(_number)));
+ secrets[round][msg.sender] = _number;
+ value ^= _number;
+ }
+
+ /// @dev Returns the Keccak-256 hash and cipher of the validator's secret for the specified collection round
+ /// and the specified validator stored by the validator through the `commitHash` function.
+ /// @param _collectRound The serial number of the collection round for which hash and cipher should be retrieved.
+ /// @param _miningAddress The mining address of validator.
+ function getCommitAndCipher(
+ uint256 _collectRound,
+ address _miningAddress
+ ) public view returns(bytes32, bytes memory) {
+ return (hashes[_collectRound][_miningAddress], ciphers[_collectRound][_miningAddress]);
+ }
+
+ /// @dev Returns a boolean flag indicating whether the specified validator has committed their secret's hash for the
+ /// specified collection round.
+ /// @param _collectRound The serial number of the collection round for which the checkup should be done.
+ /// @param _miningAddress The mining address of the validator.
+ function isCommitted(uint256 _collectRound, address _miningAddress) public view returns(bool) {
+ return hashes[_collectRound][_miningAddress] != bytes32(0);
+ }
+
+ /// @dev Returns a boolean flag indicating whether the current phase of the current collection round
+ /// is a `commits phase`. Used by the validator's node to determine if it should commit the hash of
+ /// the secret during the current collection round.
+ function isCommitPhase() public view returns(bool) {
+ return _isCommitPhase(block.number);
+ }
+
+ /// @dev Returns a boolean flag indicating whether the current phase of the current collection round
+ /// is a `reveals phase`. Used by the validator's node to determine if it should reveal the secret during
+ /// the current collection round.
+ function isRevealPhase() public view returns(bool) {
+ return _isRevealPhase(block.number);
+ }
+
+ /// @dev Returns a boolean flag of whether the specified validator has revealed their secret for the
+ /// specified collection round.
+ /// @param _collectRound The serial number of the collection round for which the checkup should be done.
+ /// @param _miningAddress The mining address of the validator.
+ function sentReveal(uint256 _collectRound, address _miningAddress) public view returns(bool) {
+ return secrets[_collectRound][_miningAddress] != uint256(0);
+ }
+
+ /// @dev Returns the current collect round number.
+ function currentCollectRound() public view returns(uint256) {
+ return _collectRound(block.number);
+ }
+
+ /// @dev Returns the current random value.
+ function getValue() public view returns(uint256) {
+ return value;
+ }
+
+ function _collectRound(uint256 blockNumber) private pure returns(uint256) {
+ return blockNumber / 6;
+ }
+
+ function _isCommitPhase(uint256 blockNumber) private pure returns(bool) {
+ return blockNumber % 6 < 3;
+ }
+
+ function _isRevealPhase(uint256 blockNumber) private pure returns(bool) {
+ return blockNumber % 6 >= 3;
+ }
+}
diff --git a/crates/ethcore/res/contracts/tx_acl_gas_price.json b/crates/ethcore/res/contracts/tx_acl_gas_price.json
new file mode 100644
index 000000000..37b08e9f0
--- /dev/null
+++ b/crates/ethcore/res/contracts/tx_acl_gas_price.json
@@ -0,0 +1,83 @@
+[
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "contractNameHash",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "contractName",
+ "outputs": [
+ {
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "contractVersion",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "sender",
+ "type": "address"
+ },
+ {
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "name": "value",
+ "type": "uint256"
+ },
+ {
+ "name": "gasPrice",
+ "type": "uint256"
+ },
+ {
+ "name": "data",
+ "type": "bytes"
+ }
+ ],
+ "name": "allowedTxTypes",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint32"
+ },
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/crates/ethcore/res/contracts/validator_report.json b/crates/ethcore/res/contracts/validator_report.json
index 093f4bebd..e0c011432 100644
--- a/crates/ethcore/res/contracts/validator_report.json
+++ b/crates/ethcore/res/contracts/validator_report.json
@@ -1,4 +1,5 @@
[
{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"},{"name":"proof","type":"bytes"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"},
- {"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}
+ {"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"},
+ {"constant": true, "inputs": [ { "name": "validator", "type": "address" }, { "name": "blockNum", "type": "uint256" } ], "name": "maliceReportedForBlock", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }
]
diff --git a/crates/ethcore/res/contracts/validator_set.json b/crates/ethcore/res/contracts/validator_set.json
index d861e16fd..660e8b614 100644
--- a/crates/ethcore/res/contracts/validator_set.json
+++ b/crates/ethcore/res/contracts/validator_set.json
@@ -1,5 +1,55 @@
[
{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"type":"function"},
{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"validators","type":"address[]"}],"payable":false,"type":"function"},
- {"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"InitiateChange","type":"event"}
-]
+ {"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"InitiateChange","type":"event"},
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "emitInitiateChangeCallable",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "emitInitiateChange",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_reportingValidator",
+ "type": "address"
+ },
+ {
+ "name": "_maliciousValidator",
+ "type": "address"
+ },
+ {
+ "name": "_blockNumber",
+ "type": "uint256"
+ }
+ ],
+ "name": "shouldValidatorReport",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+ ]
\ No newline at end of file
diff --git a/crates/ethcore/src/client/client.rs b/crates/ethcore/src/client/client.rs
index 71966cd9c..7f707a915 100644
--- a/crates/ethcore/src/client/client.rs
+++ b/crates/ethcore/src/client/client.rs
@@ -64,17 +64,20 @@ use ansi_term::Colour;
use block::{enact_verified, ClosedBlock, Drain, LockedBlock, OpenBlock, SealedBlock};
use call_contract::RegistryInfo;
use client::{
- ancient_import::AncientVerifier, bad_blocks, traits::ForceUpdateSealing, AccountData,
- BadBlocks, Balance, BlockChain as BlockChainTrait, BlockChainClient, BlockChainReset, BlockId,
- BlockInfo, BlockProducer, BroadcastProposalBlock, Call, CallAnalytics, ChainInfo,
- ChainMessageType, ChainNotify, ChainRoute, ClientConfig, ClientIoMessage, EngineInfo,
- ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient, Mode, NewBlocks, Nonce,
- PrepareOpenBlock, ProvingBlockChainClient, PruningInfo, ReopenBlock, ScheduleInfo,
- SealedBlockImporter, StateClient, StateInfo, StateOrBlock, TraceFilter, TraceId, TransactionId,
- TransactionInfo, UncleId,
+ ancient_import::AncientVerifier,
+ bad_blocks,
+ traits::{ForceUpdateSealing, TransactionRequest},
+ AccountData, BadBlocks, Balance, BlockChain as BlockChainTrait, BlockChainClient,
+ BlockChainReset, BlockId, BlockInfo, BlockProducer, BroadcastProposalBlock, Call,
+ CallAnalytics, ChainInfo, ChainMessageType, ChainNotify, ChainRoute, ClientConfig,
+ ClientIoMessage, EngineInfo, ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient,
+ Mode, NewBlocks, Nonce, PrepareOpenBlock, ProvingBlockChainClient, PruningInfo, ReopenBlock,
+ ScheduleInfo, SealedBlockImporter, StateClient, StateInfo, StateOrBlock, TraceFilter, TraceId,
+ TransactionId, TransactionInfo, UncleId,
};
use engines::{
- epoch::PendingTransition, EngineError, EpochTransition, EthEngine, ForkChoice, MAX_UNCLE_AGE,
+ epoch::PendingTransition, EngineError, EpochTransition, EthEngine, ForkChoice, SealingState,
+ MAX_UNCLE_AGE,
};
use error::{
BlockError, CallError, Error as EthcoreError, ErrorKind as EthcoreErrorKind, EthcoreResult,
@@ -2613,31 +2616,46 @@ impl BlockChainClient for Client {
}
}
- fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
+ fn create_transaction(
+ &self,
+ TransactionRequest {
+ action,
+ data,
+ gas,
+ gas_price,
+ nonce,
+ }: TransactionRequest,
+ ) -> Result {
let authoring_params = self.importer.miner.authoring_params();
let service_transaction_checker = self.importer.miner.service_transaction_checker();
let gas_price = if let Some(checker) = service_transaction_checker {
match checker.check_address(self, authoring_params.author) {
Ok(true) => U256::zero(),
- _ => self.importer.miner.sensible_gas_price(),
+ _ => gas_price.unwrap_or_else(|| self.importer.miner.sensible_gas_price()),
}
} else {
self.importer.miner.sensible_gas_price()
};
let transaction = TypedTransaction::Legacy(transaction::Transaction {
- nonce: self.latest_nonce(&authoring_params.author),
- action: Action::Call(address),
- gas: self.importer.miner.sensible_gas_limit(),
+ nonce: nonce.unwrap_or_else(|| self.latest_nonce(&authoring_params.author)),
+ action,
+ gas: gas.unwrap_or_else(|| self.importer.miner.sensible_gas_limit()),
gas_price,
value: U256::zero(),
- data: data,
+ data,
});
let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
let signature = self
.engine
.sign(transaction.signature_hash(chain_id))
.map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?;
- let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
+ Ok(SignedTransaction::new(
+ transaction.with_signature(signature, chain_id),
+ )?)
+ }
+
+ fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> {
+ let signed = self.create_transaction(tx_request)?;
self.importer
.miner
.import_own_transaction(self, signed.into())
@@ -2906,7 +2924,7 @@ impl ImportSealedBlock for Client {
&[],
route.enacted(),
route.retracted(),
- self.engine.seals_internally().is_some(),
+ self.engine.sealing_state() != SealingState::External,
);
self.notify(|notify| {
notify.new_blocks(NewBlocks::new(
@@ -3599,8 +3617,13 @@ mod tests {
#[test]
fn should_mark_finalization_correctly_for_parent() {
- let client =
- generate_dummy_client_with_spec_and_data(Spec::new_test_with_finality, 2, 0, &[]);
+ let client = generate_dummy_client_with_spec_and_data(
+ Spec::new_test_with_finality,
+ 2,
+ 0,
+ &[],
+ false,
+ );
let chain = client.chain();
let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h));
diff --git a/crates/ethcore/src/client/test_client.rs b/crates/ethcore/src/client/test_client.rs
index e59b18631..09e880746 100644
--- a/crates/ethcore/src/client/test_client.rs
+++ b/crates/ethcore/src/client/test_client.rs
@@ -59,12 +59,12 @@ use vm::Schedule;
use block::{ClosedBlock, OpenBlock, SealedBlock};
use call_contract::{CallContract, RegistryInfo};
use client::{
- traits::ForceUpdateSealing, AccountData, BadBlocks, Balance, BlockChain, BlockChainClient,
- BlockChainInfo, BlockId, BlockInfo, BlockProducer, BlockStatus, BroadcastProposalBlock, Call,
- CallAnalytics, ChainInfo, EngineInfo, ImportBlock, ImportSealedBlock, IoClient, LastHashes,
- Mode, Nonce, PrepareOpenBlock, ProvingBlockChainClient, ReopenBlock, ScheduleInfo,
- SealedBlockImporter, StateClient, StateOrBlock, TraceFilter, TraceId, TransactionId,
- TransactionInfo, UncleId,
+ traits::{ForceUpdateSealing, TransactionRequest},
+ AccountData, BadBlocks, Balance, BlockChain, BlockChainClient, BlockChainInfo, BlockId,
+ BlockInfo, BlockProducer, BlockStatus, BroadcastProposalBlock, Call, CallAnalytics, ChainInfo,
+ EngineInfo, ImportBlock, ImportSealedBlock, IoClient, LastHashes, Mode, Nonce,
+ PrepareOpenBlock, ProvingBlockChainClient, ReopenBlock, ScheduleInfo, SealedBlockImporter,
+ StateClient, StateOrBlock, TraceFilter, TraceId, TransactionId, TransactionInfo, UncleId,
};
use engines::EthEngine;
use error::{Error, EthcoreResult};
@@ -1039,12 +1039,22 @@ impl BlockChainClient for TestBlockChainClient {
}
}
- fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
+ fn create_transaction(
+ &self,
+ TransactionRequest {
+ action,
+ data,
+ gas,
+ gas_price,
+ nonce,
+ }: TransactionRequest,
+ ) -> Result {
let transaction = TypedTransaction::Legacy(Transaction {
- nonce: self.latest_nonce(&self.miner.authoring_params().author),
- action: Action::Call(address),
- gas: self.spec.gas_limit,
- gas_price: U256::zero(),
+ nonce: nonce
+ .unwrap_or_else(|| self.latest_nonce(&self.miner.authoring_params().author)),
+ action,
+ gas: gas.unwrap_or(self.spec.gas_limit),
+ gas_price: gas_price.unwrap_or_else(U256::zero),
value: U256::default(),
data: data,
});
@@ -1054,7 +1064,11 @@ impl BlockChainClient for TestBlockChainClient {
.engine
.sign(transaction.signature_hash(chain_id))
.unwrap();
- let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap();
+ Ok(SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap())
+ }
+
+ fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> {
+ let signed = self.create_transaction(tx_request)?;
self.miner.import_own_transaction(self, signed.into())
}
diff --git a/crates/ethcore/src/client/traits.rs b/crates/ethcore/src/client/traits.rs
index 62ce8ef38..f314741e4 100644
--- a/crates/ethcore/src/client/traits.rs
+++ b/crates/ethcore/src/client/traits.rs
@@ -40,7 +40,7 @@ use types::{
pruning_info::PruningInfo,
receipt::LocalizedReceipt,
trace_filter::Filter as TraceFilter,
- transaction::{self, LocalizedTransaction, SignedTransaction},
+ transaction::{self, Action, LocalizedTransaction, SignedTransaction},
BlockNumber,
};
use vm::LastHashes;
@@ -427,8 +427,15 @@ pub trait BlockChainClient:
/// Returns information about pruning/data availability.
fn pruning_info(&self) -> PruningInfo;
- /// Schedule state-altering transaction to be executed on the next pending block.
- fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error>;
+ /// Returns a transaction signed with the key configured in the engine signer.
+ fn create_transaction(
+ &self,
+ tx_request: TransactionRequest,
+ ) -> Result;
+
+ /// Schedule state-altering transaction to be executed on the next pending
+ /// block with the given gas and nonce parameters.
+ fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error>;
/// Get the address of the registry itself.
fn registrar_address(&self) -> Option;
@@ -437,6 +444,55 @@ pub trait BlockChainClient:
fn is_processing_fork(&self) -> bool;
}
+/// The data required for a `Client` to create a transaction.
+///
+/// Gas limit, gas price, or nonce can be set explicitly, e.g. to create service
+/// transactions with zero gas price, or sequences of transactions with consecutive nonces.
+/// Added for AuRa needs.
+pub struct TransactionRequest {
+ /// Transaction action
+ pub action: Action,
+ /// Transaction data
+ pub data: Bytes,
+ /// Transaction gas usage
+ pub gas: Option,
+ /// Transaction gas price
+ pub gas_price: Option,
+ /// Transaction nonce
+ pub nonce: Option,
+}
+
+impl TransactionRequest {
+ /// Creates a request to call a contract at `address` with the specified call data.
+ pub fn call(address: Address, data: Bytes) -> TransactionRequest {
+ TransactionRequest {
+ action: Action::Call(address),
+ data,
+ gas: None,
+ gas_price: None,
+ nonce: None,
+ }
+ }
+
+ /// Sets a gas limit. If this is not specified, a sensible default is used.
+ pub fn gas(mut self, gas: U256) -> TransactionRequest {
+ self.gas = Some(gas);
+ self
+ }
+
+ /// Sets a gas price. If this is not specified or `None`, a sensible default is used.
+ pub fn gas_price>>(mut self, gas_price: T) -> TransactionRequest {
+ self.gas_price = gas_price.into();
+ self
+ }
+
+ /// Sets a nonce. If this is not specified, the appropriate latest nonce for the author is used.
+ pub fn nonce(mut self, nonce: U256) -> TransactionRequest {
+ self.nonce = Some(nonce);
+ self
+ }
+}
+
/// Provides `reopen_block` method
pub trait ReopenBlock {
/// Reopens an OpenBlock and updates uncles.
diff --git a/crates/ethcore/src/engines/authority_round/block_gas_limit.rs b/crates/ethcore/src/engines/authority_round/block_gas_limit.rs
new file mode 100644
index 000000000..b4f98dc75
--- /dev/null
+++ b/crates/ethcore/src/engines/authority_round/block_gas_limit.rs
@@ -0,0 +1,39 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity Ethereum.
+
+// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see .
+
+//! A client interface for interacting with the block gas limit contract.
+
+use client::{BlockChainClient, BlockId};
+use types::header::Header;
+use ethabi::FunctionOutputDecoder;
+use ethabi_contract::use_contract;
+use ethereum_types::{Address, U256};
+use log::{debug, error};
+
+use_contract!(contract, "res/contracts/block_gas_limit.json");
+
+pub fn block_gas_limit(full_client: &dyn BlockChainClient, header: &Header, address: Address) -> Option {
+ let (data, decoder) = contract::functions::block_gas_limit::call();
+ let value = full_client.call_contract(BlockId::Hash(*header.parent_hash()), address, data).map_err(|err| {
+ error!(target: "block_gas_limit", "Contract call failed. Not changing the block gas limit. {:?}", err);
+ }).ok()?;
+ if value.is_empty() {
+ debug!(target: "block_gas_limit", "Contract call returned nothing. Not changing the block gas limit.");
+ None
+ } else {
+ decoder.decode(&value).ok()
+ }
+}
\ No newline at end of file
diff --git a/crates/ethcore/src/engines/authority_round/finality.rs b/crates/ethcore/src/engines/authority_round/finality.rs
index b75425254..cd7e9128d 100644
--- a/crates/ethcore/src/engines/authority_round/finality.rs
+++ b/crates/ethcore/src/engines/authority_round/finality.rs
@@ -22,6 +22,7 @@ use std::collections::{
};
use ethereum_types::{Address, H256};
+use types::BlockNumber;
use engines::validator_set::SimpleList;
@@ -32,20 +33,23 @@ pub struct UnknownValidator;
/// Rolling finality checker for authority round consensus.
/// Stores a chain of unfinalized hashes that can be pushed onto.
pub struct RollingFinality {
- headers: VecDeque<(H256, Vec)>,
+ headers: VecDeque<(H256, BlockNumber, Vec)>,
signers: SimpleList,
sign_count: HashMap,
last_pushed: Option,
+ /// First block for which a 2/3 quorum (instead of 1/2) is required.
+ two_thirds_majority_transition: BlockNumber,
}
impl RollingFinality {
/// Create a blank finality checker under the given validator set.
- pub fn blank(signers: Vec) -> Self {
+ pub fn blank(signers: Vec, two_thirds_majority_transition: BlockNumber) -> Self {
RollingFinality {
headers: VecDeque::new(),
signers: SimpleList::new(signers),
sign_count: HashMap::new(),
last_pushed: None,
+ two_thirds_majority_transition,
}
}
@@ -55,45 +59,35 @@ impl RollingFinality {
/// Fails if any provided signature isn't part of the signers set.
pub fn build_ancestry_subchain(&mut self, iterable: I) -> Result<(), UnknownValidator>
where
- I: IntoIterator- )>,
+ I: IntoIterator
- )>,
{
self.clear();
- for (hash, signers) in iterable {
+ for (hash, number, signers) in iterable {
if signers.iter().any(|s| !self.signers.contains(s)) {
return Err(UnknownValidator);
}
if self.last_pushed.is_none() {
self.last_pushed = Some(hash)
}
-
+ self.add_signers(&signers);
+ self.headers.push_front((hash, number, signers));
// break when we've got our first finalized block.
- {
- let current_signed = self.sign_count.len();
-
- let new_signers = signers
- .iter()
- .filter(|s| !self.sign_count.contains_key(s))
- .count();
- let would_be_finalized = (current_signed + new_signers) * 2 > self.signers.len();
-
- if would_be_finalized {
- trace!(target: "finality", "Encountered already finalized block {}", hash);
- break;
- }
-
- for signer in signers.iter() {
- *self.sign_count.entry(*signer).or_insert(0) += 1;
- }
+ if self.is_finalized() {
+ let (hash, _, signers) = self
+ .headers
+ .pop_front()
+ .expect("we just pushed a block; qed");
+ self.remove_signers(&signers);
+ trace!(target: "finality", "Encountered already finalized block {}", hash);
+ break;
}
-
- self.headers.push_front((hash, signers));
}
trace!(target: "finality", "Rolling finality state: {:?}", self.headers);
Ok(())
}
- /// Clear the finality status, but keeps the validator set.
+ /// Clears the finality status, but keeps the validator set.
pub fn clear(&mut self) {
self.headers.clear();
self.sign_count.clear();
@@ -108,7 +102,7 @@ impl RollingFinality {
/// Get an iterator over stored hashes in order.
#[cfg(test)]
pub fn unfinalized_hashes(&self) -> impl Iterator
- {
- self.headers.iter().map(|(h, _)| h)
+ self.headers.iter().map(|(h, _, _)| h)
}
/// Get the validator set.
@@ -124,41 +118,26 @@ impl RollingFinality {
pub fn push_hash(
&mut self,
head: H256,
+ number: BlockNumber,
signers: Vec,
) -> Result, UnknownValidator> {
if signers.iter().any(|s| !self.signers.contains(s)) {
return Err(UnknownValidator);
}
- for signer in signers.iter() {
- *self.sign_count.entry(*signer).or_insert(0) += 1;
- }
-
- self.headers.push_back((head, signers));
+ self.add_signers(&signers);
+ self.headers.push_back((head, number, signers));
let mut newly_finalized = Vec::new();
- while self.sign_count.len() * 2 > self.signers.len() {
- let (hash, signers) = self
+ while self.is_finalized() {
+ let (hash, _, signers) = self
.headers
.pop_front()
.expect("headers length always greater than sign count length; qed");
+ self.remove_signers(&signers);
newly_finalized.push(hash);
-
- for signer in signers {
- match self.sign_count.entry(signer) {
- Entry::Occupied(mut entry) => {
- // decrement count for this signer and purge on zero.
- *entry.get_mut() -= 1;
-
- if *entry.get() == 0 {
- entry.remove();
- }
- }
- Entry::Vacant(_) => panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed"),
- }
- }
}
trace!(target: "finality", "Blocks finalized by {:?}: {:?}", head, newly_finalized);
@@ -166,19 +145,62 @@ impl RollingFinality {
self.last_pushed = Some(head);
Ok(newly_finalized)
}
+
+ /// Returns the first block for which a 2/3 quorum (instead of 1/2) is required.
+ pub fn two_thirds_majority_transition(&self) -> BlockNumber {
+ self.two_thirds_majority_transition
+ }
+
+ /// Returns whether the first entry in `self.headers` is finalized.
+ fn is_finalized(&self) -> bool {
+ match self.headers.front() {
+ None => false,
+ Some((_, number, _)) if *number < self.two_thirds_majority_transition => {
+ self.sign_count.len() * 2 > self.signers.len()
+ }
+ Some((_, _, _)) => self.sign_count.len() * 3 > self.signers.len() * 2,
+ }
+ }
+
+ /// Adds the signers to the sign count.
+ fn add_signers(&mut self, signers: &[Address]) {
+ for signer in signers {
+ *self.sign_count.entry(*signer).or_insert(0) += 1;
+ }
+ }
+
+ /// Removes the signers from the sign count.
+ fn remove_signers(&mut self, signers: &[Address]) {
+ for signer in signers {
+ match self.sign_count.entry(*signer) {
+ Entry::Occupied(mut entry) => {
+ // decrement count for this signer and purge on zero.
+ if *entry.get() <= 1 {
+ entry.remove();
+ } else {
+ *entry.get_mut() -= 1;
+ }
+ }
+ Entry::Vacant(_) => {
+ panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed");
+ }
+ }
+ }
+ }
}
#[cfg(test)]
mod tests {
use super::RollingFinality;
use ethereum_types::{Address, H256};
+ use types::BlockNumber;
#[test]
fn rejects_unknown_signers() {
let signers = (0..3).map(|_| Address::random()).collect::>();
- let mut finality = RollingFinality::blank(signers.clone());
+ let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
assert!(finality
- .push_hash(H256::random(), vec![signers[0], Address::random()])
+ .push_hash(H256::random(), 0, vec![signers[0], Address::random()])
.is_err());
}
@@ -186,19 +208,25 @@ mod tests {
fn finalize_multiple() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
- let mut finality = RollingFinality::blank(signers.clone());
+ let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect();
// 3 / 6 signers is < 51% so no finality.
for (i, hash) in hashes.iter().take(6).cloned().enumerate() {
let i = i % 3;
- assert!(finality.push_hash(hash, vec![signers[i]]).unwrap().len() == 0);
+ assert!(
+ finality
+ .push_hash(hash, i as u64, vec![signers[i]])
+ .unwrap()
+ .len()
+ == 0
+ );
}
// after pushing a block signed by a fourth validator, the first four
// blocks of the unverified chain become verified.
assert_eq!(
- finality.push_hash(hashes[6], vec![signers[4]]).unwrap(),
+ finality.push_hash(hashes[6], 6, vec![signers[4]]).unwrap(),
vec![hashes[0], hashes[1], hashes[2], hashes[3]]
);
}
@@ -206,12 +234,12 @@ mod tests {
#[test]
fn finalize_multiple_signers() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
- let mut finality = RollingFinality::blank(signers.clone());
+ let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
let hash = H256::random();
// after pushing a block signed by four validators, it becomes verified right away.
assert_eq!(
- finality.push_hash(hash, signers[0..4].to_vec()).unwrap(),
+ finality.push_hash(hash, 0, signers[0..4].to_vec()).unwrap(),
vec![hash]
);
}
@@ -220,10 +248,9 @@ mod tests {
fn from_ancestry() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let hashes: Vec<_> = (0..12)
- .map(|i| (H256::random(), vec![signers[i % 6]]))
+ .map(|i| (H256::random(), i as u64, vec![signers[i % 6]]))
.collect();
-
- let mut finality = RollingFinality::blank(signers.clone());
+ let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
finality
.build_ancestry_subchain(hashes.iter().rev().cloned())
.unwrap();
@@ -239,12 +266,13 @@ mod tests {
.map(|i| {
(
H256::random(),
+ i as u64,
vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]],
)
})
.collect();
- let mut finality = RollingFinality::blank(signers.clone());
+ let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
finality
.build_ancestry_subchain(hashes.iter().rev().cloned())
.unwrap();
@@ -254,4 +282,91 @@ mod tests {
assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0));
assert_eq!(finality.subchain_head(), Some(hashes[11].0));
}
+
+ #[test]
+ fn rejects_unknown_signers_2_3() {
+ let signers = (0..3).map(|_| Address::random()).collect::>();
+ let mut finality = RollingFinality::blank(signers.clone(), 0);
+ assert!(finality
+ .push_hash(H256::random(), 0, vec![signers[0], Address::random()])
+ .is_err());
+ }
+
+ #[test]
+ fn finalize_multiple_2_3() {
+ let signers: Vec<_> = (0..7).map(|_| Address::random()).collect();
+
+ let mut finality = RollingFinality::blank(signers.clone(), 0);
+ let hashes: Vec<_> = (0..9).map(|_| H256::random()).collect();
+
+ // 4 / 7 signers is < 67% so no finality.
+ for (i, hash) in hashes.iter().take(8).cloned().enumerate() {
+ let i = i % 4;
+ assert!(
+ finality
+ .push_hash(hash, i as u64, vec![signers[i]])
+ .unwrap()
+ .len()
+ == 0
+ );
+ }
+
+ // after pushing a block signed by a fifth validator, the first five
+ // blocks of the unverified chain become verified.
+ assert_eq!(
+ finality.push_hash(hashes[8], 8, vec![signers[4]]).unwrap(),
+ vec![hashes[0], hashes[1], hashes[2], hashes[3], hashes[4]]
+ );
+ }
+
+ #[test]
+ fn finalize_multiple_signers_2_3() {
+ let signers: Vec<_> = (0..5).map(|_| Address::random()).collect();
+ let mut finality = RollingFinality::blank(signers.clone(), 0);
+ let hash = H256::random();
+
+ // after pushing a block signed by four validators, it becomes verified right away.
+ assert_eq!(
+ finality.push_hash(hash, 0, signers[0..4].to_vec()).unwrap(),
+ vec![hash]
+ );
+ }
+
+ #[test]
+ fn from_ancestry_2_3() {
+ let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
+ let hashes: Vec<_> = (0..12)
+ .map(|i| (H256::random(), i as u64, vec![signers[i % 6]]))
+ .collect();
+
+ let mut finality = RollingFinality::blank(signers, 0);
+ finality
+ .build_ancestry_subchain(hashes.iter().rev().cloned())
+ .unwrap();
+
+ // The last four hashes, with index 11, 10, 9, and 8, have been pushed. 7 would have finalized a block.
+ assert_eq!(finality.unfinalized_hashes().count(), 4);
+ assert_eq!(finality.subchain_head(), Some(hashes[11].0));
+ }
+
+ #[test]
+ fn from_ancestry_multiple_signers_2_3() {
+ let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
+ let hashes: Vec<_> = (0..12)
+ .map(|i| {
+ let hash_signers = signers.iter().cycle().skip(i).take(4).cloned().collect();
+ (H256::random(), i as u64, hash_signers)
+ })
+ .collect();
+
+ let mut finality = RollingFinality::blank(signers.clone(), 0);
+ finality
+ .build_ancestry_subchain(hashes.iter().rev().cloned())
+ .unwrap();
+
+ // only the last hash has < 67% of authorities' signatures
+ assert_eq!(finality.unfinalized_hashes().count(), 1);
+ assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0));
+ assert_eq!(finality.subchain_head(), Some(hashes[11].0));
+ }
}
diff --git a/crates/ethcore/src/engines/authority_round/mod.rs b/crates/ethcore/src/engines/authority_round/mod.rs
index 9afdedfc0..4591e5e25 100644
--- a/crates/ethcore/src/engines/authority_round/mod.rs
+++ b/crates/ethcore/src/engines/authority_round/mod.rs
@@ -15,32 +15,52 @@
// along with OpenEthereum. If not, see .
//! A blockchain engine that supports a non-instant BFT proof-of-authority.
+//!
+//! It is recommended to use the `two_thirds_majority_transition` option, to defend against the
+//! ["Attack of the Clones"](https://arxiv.org/pdf/1902.10244.pdf). Newly started networks can
+//! set this option to `0`, to use a 2/3 quorum from the beginning.
+//!
+//! To support on-chain governance, the [ValidatorSet] is pluggable: Aura supports simple
+//! constant lists of validators as well as smart contract-based dynamic validator sets.
+//! Misbehavior is reported to the [ValidatorSet] as well, so that e.g. governance contracts
+//! can penalize or ban attacker's nodes.
+//!
+//! * "Benign" misbehavior are faults that can happen in normal operation, like failing
+//! to propose a block in your slot, which could be due to a temporary network outage, or
+//! wrong timestamps (due to out-of-sync clocks).
+//! * "Malicious" reports are made only if the sender misbehaved deliberately (or due to a
+//! software bug), e.g. if they proposed multiple blocks with the same step number.
use std::{
cmp,
collections::{BTreeMap, BTreeSet, HashSet},
fmt,
- iter::FromIterator,
+ iter::{self, FromIterator},
ops::Deref,
sync::{
- atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering},
+ atomic::{AtomicBool, AtomicU64, Ordering as AtomicOrdering},
Arc, Weak,
},
time::{Duration, UNIX_EPOCH},
+ u64,
};
use self::finality::RollingFinality;
use super::{
signer::EngineSigner,
- validator_set::{new_validator_set, SimpleList, ValidatorSet},
+ validator_set::{new_validator_set_posdao, SimpleList, ValidatorSet},
};
use block::*;
-use client::{traits::ForceUpdateSealing, EngineClient};
+use bytes::Bytes;
+use client::{
+ traits::{ForceUpdateSealing, TransactionRequest},
+ EngineClient,
+};
use crypto::publickey::{self, Signature};
use engines::{
block_reward,
block_reward::{BlockRewardContract, RewardKind},
- ConstructedVerifier, Engine, EngineError, Seal,
+ ConstructedVerifier, Engine, EngineError, Seal, SealingState,
};
use error::{BlockError, Error, ErrorKind};
use ethereum_types::{Address, H256, H520, U128, U256};
@@ -49,27 +69,35 @@ use ethjson::{self, uint::Uint};
use hash::keccak;
use io::{IoContext, IoHandler, IoService, TimerToken};
use itertools::{self, Itertools};
+use lru_cache::LruCache;
use machine::{AuxiliaryData, Call, EthereumMachine};
use parking_lot::{Mutex, RwLock};
+use rand::rngs::OsRng;
use rlp::{encode, Decodable, DecoderError, Encodable, Rlp, RlpStream};
use time_utils::CheckedSystemTime;
use types::{
ancestry_action::AncestryAction,
header::{ExtendedHeader, Header},
+ ids::BlockId,
+ transaction::SignedTransaction,
BlockNumber,
};
use unexpected::{Mismatch, OutOfBounds};
+//mod block_gas_limit as crate_block_gas_limit;
mod finality;
+mod randomness;
+pub(crate) mod util;
/// `AuthorityRound` params.
pub struct AuthorityRoundParams {
- /// Time to wait before next block or authority switching,
- /// in seconds.
+ /// A map defining intervals of blocks with the given times (in seconds) to wait before next
+ /// block or authority switching. The keys in the map are steps of starting blocks of those
+ /// periods. The entry at `0` should be defined.
///
- /// Deliberately typed as u16 as too high of a value leads
- /// to slow block issuance.
- pub step_duration: u16,
+ /// Wait times (durations) are additionally required to be less than 65535 since larger values
+ /// lead to slow block issuance.
+ pub step_durations: BTreeMap,
/// Starting step,
pub start_step: Option,
/// Valid validators.
@@ -82,34 +110,110 @@ pub struct AuthorityRoundParams {
pub immediate_transitions: bool,
/// Block reward in base units.
pub block_reward: BTreeMap,
- /// Block reward contract transition block.
- pub block_reward_contract_transition: u64,
- /// Block reward contract.
- pub block_reward_contract: Option,
+ /// Block reward contract addresses with their associated starting block numbers.
+ pub block_reward_contract_transitions: BTreeMap,
/// Number of accepted uncles transition block.
pub maximum_uncle_count_transition: u64,
/// Number of accepted uncles.
pub maximum_uncle_count: usize,
/// Empty step messages transition block.
pub empty_steps_transition: u64,
+ /// First block for which a 2/3 quorum (instead of 1/2) is required.
+ pub two_thirds_majority_transition: BlockNumber,
/// Number of accepted empty steps.
pub maximum_empty_steps: usize,
/// Transition block to strict empty steps validation.
pub strict_empty_steps_transition: u64,
+ /// If set, enables random number contract integration. It maps the transition block to the contract address.
+ pub randomness_contract_address: BTreeMap,
+ /// The addresses of contracts that determine the block gas limit with their associated block
+ /// numbers.
+ pub block_gas_limit_contract_transitions: BTreeMap,
+ /// If set, this is the block number at which the consensus engine switches from AuRa to AuRa
+ /// with POSDAO modifications.
+ pub posdao_transition: Option,
}
const U16_MAX: usize = ::std::u16::MAX as usize;
+/// The number of recent block hashes for which the gas limit override is memoized.
+const GAS_LIMIT_OVERRIDE_CACHE_CAPACITY: usize = 10;
+
impl From for AuthorityRoundParams {
fn from(p: ethjson::spec::AuthorityRoundParams) -> Self {
- let mut step_duration_usize: usize = p.step_duration.into();
- if step_duration_usize > U16_MAX {
- step_duration_usize = U16_MAX;
- warn!(target: "engine", "step_duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX);
+ let map_step_duration = |u: ethjson::uint::Uint| {
+ let mut step_duration_usize: usize = u.into();
+ if step_duration_usize == 0 {
+ panic!("AuthorityRoundParams: step duration cannot be 0");
+ }
+ if step_duration_usize > U16_MAX {
+ warn!(target: "engine", "step duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX);
+ step_duration_usize = U16_MAX;
+ }
+ step_duration_usize as u64
+ };
+ let step_durations: BTreeMap<_, _> = match p.step_duration {
+ ethjson::spec::StepDuration::Single(u) => {
+ iter::once((0, map_step_duration(u))).collect()
+ }
+ ethjson::spec::StepDuration::Transitions(tr) => {
+ if tr.is_empty() {
+ panic!("AuthorityRoundParams: step duration transitions cannot be empty");
+ }
+ tr.into_iter()
+ .map(|(timestamp, u)| (timestamp.into(), map_step_duration(u)))
+ .collect()
+ }
+ };
+ let transition_block_num = p.block_reward_contract_transition.map_or(0, Into::into);
+ let mut br_transitions: BTreeMap<_, _> = p
+ .block_reward_contract_transitions
+ .unwrap_or_default()
+ .into_iter()
+ .map(|(block_num, address)| {
+ (
+ block_num.into(),
+ BlockRewardContract::new_from_address(address.into()),
+ )
+ })
+ .collect();
+ if (p.block_reward_contract_code.is_some() || p.block_reward_contract_address.is_some())
+ && br_transitions
+ .keys()
+ .next()
+ .map_or(false, |&block_num| block_num <= transition_block_num)
+ {
+ let s = "blockRewardContractTransition";
+ panic!("{} should be less than any of the keys in {}s", s, s);
}
+ if let Some(code) = p.block_reward_contract_code {
+ br_transitions.insert(
+ transition_block_num,
+ BlockRewardContract::new_from_code(Arc::new(code.into())),
+ );
+ } else if let Some(address) = p.block_reward_contract_address {
+ br_transitions.insert(
+ transition_block_num,
+ BlockRewardContract::new_from_address(address.into()),
+ );
+ }
+ let randomness_contract_address =
+ p.randomness_contract_address
+ .map_or_else(BTreeMap::new, |transitions| {
+ transitions
+ .into_iter()
+ .map(|(ethjson::uint::Uint(block), addr)| (block.as_u64(), addr.into()))
+ .collect()
+ });
+ let block_gas_limit_contract_transitions: BTreeMap<_, _> = p
+ .block_gas_limit_contract_transitions
+ .unwrap_or_default()
+ .into_iter()
+ .map(|(block_num, address)| (block_num.into(), address.into()))
+ .collect();
AuthorityRoundParams {
- step_duration: step_duration_usize as u16,
- validators: new_validator_set(p.validators),
+ step_durations,
+ validators: new_validator_set_posdao(p.validators, p.posdao_transition.map(Into::into)),
start_step: p.start_step.map(Into::into),
validate_score_transition: p.validate_score_transition.map_or(0, Into::into),
validate_step_transition: p.validate_step_transition.map_or(0, Into::into),
@@ -141,77 +245,122 @@ impl From for AuthorityRoundParams {
}
},
),
- block_reward_contract_transition: p
- .block_reward_contract_transition
- .map_or(0, Into::into),
- block_reward_contract: match (
- p.block_reward_contract_code,
- p.block_reward_contract_address,
- ) {
- (Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))),
- (_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())),
- (None, None) => None,
- },
+ block_reward_contract_transitions: br_transitions,
maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into),
maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into),
empty_steps_transition: p
.empty_steps_transition
.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)),
maximum_empty_steps: p.maximum_empty_steps.map_or(0, Into::into),
+ two_thirds_majority_transition: p
+ .two_thirds_majority_transition
+ .map_or_else(BlockNumber::max_value, Into::into),
strict_empty_steps_transition: p.strict_empty_steps_transition.map_or(0, Into::into),
+ randomness_contract_address,
+ block_gas_limit_contract_transitions,
+ posdao_transition: p.posdao_transition.map(Into::into),
}
}
}
-// Helper for managing the step.
+/// A triple containing the first step number and the starting timestamp of the given step duration.
+#[derive(Clone, Copy, Debug)]
+struct StepDurationInfo {
+ transition_step: u64,
+ transition_timestamp: u64,
+ step_duration: u64,
+}
+
+/// Helper for managing the step.
#[derive(Debug)]
struct Step {
calibrate: bool, // whether calibration is enabled.
- inner: AtomicUsize,
- duration: u16,
+ inner: AtomicU64,
+ /// Planned durations of steps.
+ durations: Vec,
}
impl Step {
fn load(&self) -> u64 {
- self.inner.load(AtomicOrdering::SeqCst) as u64
+ self.inner.load(AtomicOrdering::SeqCst)
}
+
fn duration_remaining(&self) -> Duration {
- let now = unix_now();
- let expected_seconds = self
- .load()
- .checked_add(1)
- .and_then(|ctr| ctr.checked_mul(self.duration as u64))
- .map(Duration::from_secs);
-
- match expected_seconds {
- Some(step_end) if step_end > now => step_end - now,
- Some(_) => Duration::from_secs(0),
- None => {
- let ctr = self.load();
- error!(target: "engine", "Step counter is too high: {}, aborting", ctr);
- panic!("step counter is too high: {}", ctr)
- }
- }
+ self.opt_duration_remaining().unwrap_or_else(|| {
+ let ctr = self.load();
+ error!(target: "engine", "Step counter under- or overflow: {}, aborting", ctr);
+ panic!("step counter under- or overflow: {}", ctr)
+ })
}
+ /// Finds the remaining duration of the current step. Returns `None` if there was a counter
+ /// under- or overflow.
+ fn opt_duration_remaining(&self) -> Option {
+ let next_step = self.load().checked_add(1)?;
+ let StepDurationInfo {
+ transition_step,
+ transition_timestamp,
+ step_duration,
+ } = self
+ .durations
+ .iter()
+ .take_while(|info| info.transition_step < next_step)
+ .last()
+ .expect("durations cannot be empty")
+ .clone();
+ let next_time = transition_timestamp.checked_add(
+ next_step
+ .checked_sub(transition_step)?
+ .checked_mul(step_duration)?,
+ )?;
+ Some(Duration::from_secs(
+ next_time.saturating_sub(unix_now().as_secs()),
+ ))
+ }
+
+ /// Increments the step number.
+ ///
+ /// Panics if the new step number is `u64::MAX`.
fn increment(&self) {
- use std::usize;
// fetch_add won't panic on overflow but will rather wrap
// around, leading to zero as the step counter, which might
// lead to unexpected situations, so it's better to shut down.
- if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == usize::MAX {
- error!(target: "engine", "Step counter is too high: {}, aborting", usize::MAX);
- panic!("step counter is too high: {}", usize::MAX);
+ if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == u64::MAX {
+ error!(target: "engine", "Step counter is too high: {}, aborting", u64::MAX);
+ panic!("step counter is too high: {}", u64::MAX);
}
}
fn calibrate(&self) {
if self.calibrate {
- let new_step = unix_now().as_secs() / (self.duration as u64);
- self.inner.store(new_step as usize, AtomicOrdering::SeqCst);
+ if self.opt_calibrate().is_none() {
+ let ctr = self.load();
+ error!(target: "engine", "Step counter under- or overflow: {}, aborting", ctr);
+ panic!("step counter under- or overflow: {}", ctr)
+ }
}
}
+ /// Calibrates the AuRa step number according to the current time.
+ fn opt_calibrate(&self) -> Option<()> {
+ let now = unix_now().as_secs();
+ let StepDurationInfo {
+ transition_step,
+ transition_timestamp,
+ step_duration,
+ } = self
+ .durations
+ .iter()
+ .take_while(|info| info.transition_timestamp < now)
+ .last()
+ .expect("durations cannot be empty")
+ .clone();
+ let new_step = (now.checked_sub(transition_timestamp)? / step_duration)
+ .checked_add(transition_step)?;
+ self.inner.store(new_step, AtomicOrdering::SeqCst);
+ Some(())
+ }
+
fn check_future(&self, given: u64) -> Result<(), Option>> {
const REJECTED_STEP_DRIFT: u64 = 4;
@@ -229,7 +378,13 @@ impl Step {
Err(None)
// wait a bit for blocks in near future
} else if given > current {
- let d = self.duration as u64;
+ let d = self
+ .durations
+ .iter()
+ .take_while(|info| info.transition_step <= current)
+ .last()
+ .expect("Duration map has at least a 0 entry.")
+ .step_duration;
Err(Some(OutOfBounds {
min: None,
max: Some(d * current),
@@ -255,24 +410,24 @@ struct EpochManager {
}
impl EpochManager {
- fn blank() -> Self {
+ fn blank(two_thirds_majority_transition: BlockNumber) -> Self {
EpochManager {
epoch_transition_hash: H256::default(),
epoch_transition_number: 0,
- finality_checker: RollingFinality::blank(Vec::new()),
+ finality_checker: RollingFinality::blank(Vec::new(), two_thirds_majority_transition),
force: true,
}
}
- // zoom to epoch for given header. returns true if succeeded, false otherwise.
- fn zoom_to(
+ /// Zooms to the epoch after the header with the given hash. Returns true if succeeded, false otherwise.
+ fn zoom_to_after(
&mut self,
client: &dyn EngineClient,
machine: &EthereumMachine,
validators: &dyn ValidatorSet,
- header: &Header,
+ hash: H256,
) -> bool {
- let last_was_parent = self.finality_checker.subchain_head() == Some(*header.parent_hash());
+ let last_was_parent = self.finality_checker.subchain_head() == Some(hash);
// early exit for current target == chain head, but only if the epochs are
// the same.
@@ -281,13 +436,13 @@ impl EpochManager {
}
self.force = false;
- debug!(target: "engine", "Zooming to epoch for block {}", header.hash());
+ debug!(target: "engine", "Zooming to epoch after block {}", hash);
// epoch_transition_for can be an expensive call, but in the absence of
// forks it will only need to be called for the block directly after
// epoch transition, in which case it will be O(1) and require a single
// DB lookup.
- let last_transition = match client.epoch_transition_for(*header.parent_hash()) {
+ let last_transition = match client.epoch_transition_for(hash) {
Some(t) => t,
None => {
// this really should never happen unless the block passed
@@ -302,22 +457,31 @@ impl EpochManager {
let (signal_number, set_proof, _) = destructure_proofs(&last_transition.proof)
.expect("proof produced by this engine; therefore it is valid; qed");
- trace!(target: "engine", "extracting epoch set for epoch ({}, {}) signalled at #{}",
- last_transition.block_number, last_transition.block_hash, signal_number);
+ trace!(
+ target: "engine",
+ "extracting epoch validator set for epoch ({}, {}) signalled at #{}",
+ last_transition.block_number, last_transition.block_hash, signal_number
+ );
let first = signal_number == 0;
- let epoch_set = validators
+ let (list, _) = validators
.epoch_set(
first,
machine,
signal_number, // use signal number so multi-set first calculation is correct.
set_proof,
)
- .ok()
- .map(|(list, _)| list.into_inner())
.expect("proof produced by this engine; therefore it is valid; qed");
-
- self.finality_checker = RollingFinality::blank(epoch_set);
+ trace!(
+ target: "engine",
+ "Updating finality checker with new validator set extracted from epoch ({}, {}): {:?}",
+ last_transition.block_number, last_transition.block_hash, &list
+ );
+ let epoch_set = list.into_inner();
+ let two_thirds_majority_transition =
+ self.finality_checker.two_thirds_majority_transition();
+ self.finality_checker =
+ RollingFinality::blank(epoch_set, two_thirds_majority_transition);
}
self.epoch_transition_hash = last_transition.block_hash;
@@ -342,10 +506,22 @@ impl EpochManager {
/// A message broadcast by authorities when it's their turn to seal a block but there are no
/// transactions. Other authorities accumulate these messages and later include them in the seal as
/// proof.
+///
+/// An empty step message is created _instead of_ a block if there are no pending transactions.
+/// It cannot itself be a parent, and `parent_hash` always points to the most recent block. E.g.:
+/// * Validator A creates block `bA`.
+/// * Validator B has no pending transactions, so it signs an empty step message `mB`
+/// instead whose hash points to block `bA`.
+/// * Validator C also has no pending transactions, so it also signs an empty step message `mC`
+/// instead whose hash points to block `bA`.
+/// * Validator D creates block `bD`. The parent is block `bA`, and the header includes `mB` and `mC`.
#[derive(Clone, Debug, PartialEq, Eq)]
struct EmptyStep {
+ /// The signature of the other two fields, by the message's author.
signature: H520,
+ /// This message's step number.
step: u64,
+ /// The hash of the most recent block.
parent_hash: H256,
}
@@ -354,6 +530,7 @@ impl PartialOrd for EmptyStep {
Some(self.cmp(other))
}
}
+
impl Ord for EmptyStep {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.step
@@ -375,6 +552,7 @@ impl EmptyStep {
}
}
+ /// Returns `true` if the message has a valid signature by the expected proposer in the message's step.
fn verify(&self, validators: &dyn ValidatorSet) -> Result {
let message = keccak(empty_step_rlp(self.step, &self.parent_hash));
let correct_proposer = step_proposer(validators, &self.parent_hash, self.step);
@@ -485,14 +663,26 @@ pub struct AuthorityRound {
epoch_manager: Mutex,
immediate_transitions: bool,
block_reward: BTreeMap,
- block_reward_contract_transition: u64,
- block_reward_contract: Option,
+ block_reward_contract_transitions: BTreeMap,
maximum_uncle_count_transition: u64,
maximum_uncle_count: usize,
empty_steps_transition: u64,
strict_empty_steps_transition: u64,
+ two_thirds_majority_transition: BlockNumber,
maximum_empty_steps: usize,
machine: EthereumMachine,
+ /// History of step hashes recently received from peers.
+ received_step_hashes: RwLock>,
+ /// If set, enables random number contract integration. It maps the transition block to the contract address.
+ randomness_contract_address: BTreeMap,
+ /// The addresses of contracts that determine the block gas limit.
+ block_gas_limit_contract_transitions: BTreeMap,
+ /// Memoized gas limit overrides, by block hash.
+ gas_limit_override_cache: Mutex>>,
+ /// The block number at which the consensus engine switches from AuRa to AuRa with POSDAO
+ /// modifications. For details about POSDAO, see the whitepaper:
+ /// https://www.xdaichain.com/for-validators/posdao-whitepaper
+ posdao_transition: Option,
}
// header-chain validator.
@@ -500,6 +690,8 @@ struct EpochVerifier {
step: Arc,
subchain_validators: SimpleList,
empty_steps_transition: u64,
+ /// First block for which a 2/3 quorum (instead of 1/2) is required.
+ two_thirds_majority_transition: BlockNumber,
}
impl super::EpochVerifier for EpochVerifier {
@@ -519,8 +711,9 @@ impl super::EpochVerifier for EpochVerifier {
}
fn check_finality_proof(&self, proof: &[u8]) -> Option> {
+ let signers = self.subchain_validators.clone().into_inner();
let mut finality_checker =
- RollingFinality::blank(self.subchain_validators.clone().into_inner());
+ RollingFinality::blank(signers, self.two_thirds_majority_transition);
let mut finalized = Vec::new();
let headers: Vec = Rlp::new(proof).as_list().ok()?;
@@ -557,7 +750,7 @@ impl super::EpochVerifier for EpochVerifier {
signers.push(*parent_header.author());
let newly_finalized = finality_checker
- .push_hash(parent_header.hash(), signers)
+ .push_hash(parent_header.hash(), parent_header.number(), signers)
.ok()?;
finalized.extend(newly_finalized);
@@ -602,8 +795,8 @@ fn header_expected_seal_fields(header: &Header, empty_steps_transition: u64) ->
fn header_step(header: &Header, empty_steps_transition: u64) -> Result {
Rlp::new(&header.seal().get(0).unwrap_or_else(||
- panic!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec
- file has a correct genesis seal)", header_expected_seal_fields(header, empty_steps_transition))
+ panic!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec \
+ file has a correct genesis seal)", header_expected_seal_fields(header, empty_steps_transition))
))
.as_val()
}
@@ -733,7 +926,7 @@ fn verify_external(
}
fn combine_proofs(signal_number: BlockNumber, set_proof: &[u8], finality_proof: &[u8]) -> Vec {
- let mut stream = ::rlp::RlpStream::new_list(3);
+ let mut stream = RlpStream::new_list(3);
stream
.append(&signal_number)
.append(&set_proof)
@@ -782,22 +975,52 @@ impl AuthorityRound {
our_params: AuthorityRoundParams,
machine: EthereumMachine,
) -> Result, Error> {
- if our_params.step_duration == 0 {
- error!(target: "engine", "Authority Round step duration can't be zero, aborting");
- panic!("authority_round: step duration can't be zero")
+ if !our_params.step_durations.contains_key(&0) {
+ error!(target: "engine", "Authority Round step 0 duration is undefined, aborting");
+ return Err(Error::from_kind(ErrorKind::Engine(EngineError::Custom(
+ String::from("step 0 duration is undefined"),
+ ))));
}
+ if our_params.step_durations.values().any(|v| *v == 0) {
+ error!(target: "engine", "Authority Round step duration cannot be 0");
+ return Err(Error::from_kind(ErrorKind::Engine(EngineError::Custom(
+ String::from("step duration cannot be 0"),
+ ))));
+ }
+
let should_timeout = our_params.start_step.is_none();
- let initial_step = our_params
- .start_step
- .unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64)));
+
+ let initial_step = our_params.start_step.unwrap_or(0);
+
+ let mut durations = Vec::new();
+ {
+ let mut dur_info = StepDurationInfo {
+ transition_step: 0u64,
+ transition_timestamp: 0u64,
+ step_duration: our_params.step_durations[&0],
+ };
+ durations.push(dur_info);
+ for (time, dur) in our_params.step_durations.iter().skip(1) {
+ let (step, time) = next_step_time_duration(dur_info, *time)
+ .ok_or(BlockError::TimestampOverflow)?;
+ dur_info.transition_step = step;
+ dur_info.transition_timestamp = time;
+ dur_info.step_duration = *dur;
+ durations.push(dur_info);
+ }
+ }
+
+ let step = Step {
+ inner: AtomicU64::new(initial_step),
+ calibrate: our_params.start_step.is_none(),
+ durations,
+ };
+ step.calibrate();
+
let engine = Arc::new(AuthorityRound {
transition_service: IoService::<()>::start("AuRa")?,
step: Arc::new(PermissionedStep {
- inner: Step {
- inner: AtomicUsize::new(initial_step as usize),
- calibrate: our_params.start_step.is_none(),
- duration: our_params.step_duration,
- },
+ inner: step,
can_propose: AtomicBool::new(true),
}),
client: Arc::new(RwLock::new(None)),
@@ -806,17 +1029,24 @@ impl AuthorityRound {
validate_score_transition: our_params.validate_score_transition,
validate_step_transition: our_params.validate_step_transition,
empty_steps: Default::default(),
- epoch_manager: Mutex::new(EpochManager::blank()),
+ epoch_manager: Mutex::new(EpochManager::blank(
+ our_params.two_thirds_majority_transition,
+ )),
immediate_transitions: our_params.immediate_transitions,
block_reward: our_params.block_reward,
- block_reward_contract_transition: our_params.block_reward_contract_transition,
- block_reward_contract: our_params.block_reward_contract,
+ block_reward_contract_transitions: our_params.block_reward_contract_transitions,
maximum_uncle_count_transition: our_params.maximum_uncle_count_transition,
maximum_uncle_count: our_params.maximum_uncle_count,
empty_steps_transition: our_params.empty_steps_transition,
maximum_empty_steps: our_params.maximum_empty_steps,
+ two_thirds_majority_transition: our_params.two_thirds_majority_transition,
strict_empty_steps_transition: our_params.strict_empty_steps_transition,
machine: machine,
+ received_step_hashes: RwLock::new(Default::default()),
+ randomness_contract_address: our_params.randomness_contract_address,
+ block_gas_limit_contract_transitions: our_params.block_gas_limit_contract_transitions,
+ gas_limit_override_cache: Mutex::new(LruCache::new(GAS_LIMIT_OVERRIDE_CACHE_CAPACITY)),
+ posdao_transition: our_params.posdao_transition,
});
// Do not initialize timeouts for tests.
@@ -842,15 +1072,14 @@ impl AuthorityRound {
(CowLike::Borrowed(&*self.validators), header.number())
} else {
let mut epoch_manager = self.epoch_manager.lock();
- let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) {
- Some(client) => client,
- None => {
- debug!(target: "engine", "Unable to verify sig: missing client ref.");
- return Err(EngineError::RequiresClient.into());
- }
- };
+ let client = self.upgrade_client_or("Unable to verify sig")?;
- if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) {
+ if !epoch_manager.zoom_to_after(
+ &*client,
+ &self.machine,
+ &*self.validators,
+ *header.parent_hash(),
+ ) {
debug!(target: "engine", "Unable to zoom to epoch.");
return Err(EngineError::RequiresClient.into());
}
@@ -923,10 +1152,8 @@ impl AuthorityRound {
}
fn broadcast_message(&self, message: Vec) {
- if let Some(ref weak) = *self.client.read() {
- if let Some(c) = weak.upgrade() {
- c.broadcast_consensus_message(message);
- }
+ if let Ok(c) = self.upgrade_client_or(None) {
+ c.broadcast_consensus_message(message);
}
}
@@ -958,6 +1185,12 @@ impl AuthorityRound {
if !reported.insert(skipped_primary) {
break;
}
+ trace!(
+ target: "engine",
+ "Reporting benign misbehaviour (cause: skipped step) at block #{}, \
+ epoch set number {}, step proposer={:#x}. Own address: {}",
+ header.number(), set_number, skipped_primary, me
+ );
self.validators
.report_benign(&skipped_primary, set_number, header.number());
}
@@ -975,16 +1208,18 @@ impl AuthorityRound {
return Vec::new();
}
- let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) {
- Some(client) => client,
- None => {
- warn!(target: "engine", "Unable to apply ancestry actions: missing client ref.");
- return Vec::new();
- }
+ let client = match self.upgrade_client_or("Unable to apply ancestry actions") {
+ Ok(client) => client,
+ Err(_) => return Vec::new(),
};
let mut epoch_manager = self.epoch_manager.lock();
- if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) {
+ if !epoch_manager.zoom_to_after(
+ &*client,
+ &self.machine,
+ &*self.validators,
+ *chain_head.parent_hash(),
+ ) {
return Vec::new();
}
@@ -1012,7 +1247,7 @@ impl AuthorityRound {
signers.extend(parent_empty_steps_signers.drain(..));
if let Ok(empty_step_signers) = header_empty_steps_signers(&header, self.empty_steps_transition) {
- let res = (header.hash(), signers);
+ let res = (header.hash(), header.number(), signers);
trace!(target: "finality", "Ancestry iteration: yielding {:?}", res);
parent_empty_steps_signers = empty_step_signers;
@@ -1025,7 +1260,7 @@ impl AuthorityRound {
}
})
.while_some()
- .take_while(|&(h, _)| h != epoch_transition_hash);
+ .take_while(|&(h, _, _)| h != epoch_transition_hash);
if let Err(e) = epoch_manager
.finality_checker
@@ -1036,11 +1271,140 @@ impl AuthorityRound {
}
}
- let finalized = epoch_manager
- .finality_checker
- .push_hash(chain_head.hash(), vec![*chain_head.author()]);
+ let finalized = epoch_manager.finality_checker.push_hash(
+ chain_head.hash(),
+ chain_head.number(),
+ vec![*chain_head.author()],
+ );
finalized.unwrap_or_default()
}
+
+ fn address(&self) -> Option {
+ self.signer.read().as_ref().map(|s| s.address())
+ }
+
+ /// Make calls to the randomness contract.
+ fn run_randomness_phase(&self, block: &ExecutedBlock) -> Result, Error> {
+ let contract_addr = match self
+ .randomness_contract_address
+ .range(..=block.header.number())
+ .last()
+ {
+ Some((_, &contract_addr)) => contract_addr,
+ None => return Ok(Vec::new()), // No randomness contract.
+ };
+
+ let opt_signer = self.signer.read();
+ let signer = match opt_signer.as_ref() {
+ Some(signer) => signer,
+ None => return Ok(Vec::new()), // We are not a validator, so we shouldn't call the contracts.
+ };
+ let our_addr = signer.address();
+ let client = self.upgrade_client_or("Unable to prepare block")?;
+ let full_client = client.as_full_client().ok_or_else(|| {
+ EngineError::FailedSystemCall("Failed to upgrade to BlockchainClient.".to_string())
+ })?;
+
+ // Random number generation
+ let contract = util::BoundContract::new(&*client, BlockId::Latest, contract_addr);
+ let phase = randomness::RandomnessPhase::load(&contract, our_addr)
+ .map_err(|err| EngineError::Custom(format!("Randomness error in load(): {:?}", err)))?;
+ let data = match phase
+ .advance(&contract, &mut OsRng, signer.as_ref())
+ .map_err(|err| {
+ EngineError::Custom(format!("Randomness error in advance(): {:?}", err))
+ })? {
+ Some(data) => data,
+ None => return Ok(Vec::new()), // Nothing to commit or reveal at the moment.
+ };
+
+ let nonce = block.state.nonce(&our_addr)?;
+ let tx_request = TransactionRequest::call(contract_addr, data)
+ .gas_price(U256::zero())
+ .nonce(nonce);
+ Ok(vec![full_client.create_transaction(tx_request)?])
+ }
+
+ /// Returns the reference to the client, if registered.
+ fn upgrade_client_or<'a, T>(
+ &self,
+ opt_error_msg: T,
+ ) -> Result, EngineError>
+ where
+ T: Into