Backport AuRa commits for xdai (#330)
* Add SealingState; don't prepare block when not ready. (#10529) * Fix a few typos and unused warnings. #10803 * Configuration map of block reward contract addresses (#10875) * Step duration map configuration parameter ported from the POA Network fork (#10902) * Add a 2/3 quorum option to Authority Round. (#10909) * Additional arithmetic EVM opcode benchmarks (#10916) * RPC method for clearing the engine signer (#10920) * authority_round: Fix next_step_time_duration. (#11379) * Aura: Report malice on sibling blocks from the same validator (#11160) * TxPermissions ver 3: gas price & data (#11170) * Add randomness contract support to AuthorityRound. (#10946) * Set the block gas limit to the value returned by a contract call (#10928) * AuthorityEngine: Minor cleanups. (#11408) * Add POSDAO transition and malice report queue. (#11245) * PoA call validators on_close_block * Actualize spec files for POA Networks * Some fixes after merge * Crypto error desc * AuRa on_close_block Error::Old fix Co-authored-by: POA <33550681+poa@users.noreply.github.com>
This commit is contained in:
parent
561ed8df3c
commit
85391f99ac
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -952,6 +952,7 @@ dependencies = [
|
|||||||
"common-types",
|
"common-types",
|
||||||
"criterion 0.2.11",
|
"criterion 0.2.11",
|
||||||
"crossbeam-utils 0.6.6",
|
"crossbeam-utils 0.6.6",
|
||||||
|
"derive_more",
|
||||||
"eip-152",
|
"eip-152",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"error-chain",
|
"error-chain",
|
||||||
|
88
crates/accounts/ethkey/src/error.rs
Normal file
88
crates/accounts/ethkey/src/error.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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<String> for Error {
|
||||||
|
fn into(self) -> String {
|
||||||
|
format!("{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CryptoError> 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)
|
||||||
|
}
|
||||||
|
}
|
@ -76,6 +76,7 @@ using_queue = { path = "../concensus/miner/using-queue" }
|
|||||||
vm = { path = "../vm/vm" }
|
vm = { path = "../vm/vm" }
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
wasm = { path = "../vm/wasm" }
|
wasm = { path = "../vm/wasm" }
|
||||||
|
derive_more = "0.99"
|
||||||
scopeguard = "1.1.0"
|
scopeguard = "1.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -88,9 +88,13 @@
|
|||||||
"builtin": {
|
"builtin": {
|
||||||
"name": "ecrecover",
|
"name": "ecrecover",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"linear": {
|
"0": {
|
||||||
"base": 3000,
|
"price": {
|
||||||
"word": 0
|
"linear": {
|
||||||
|
"base": 3000,
|
||||||
|
"word": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,9 +104,13 @@
|
|||||||
"builtin": {
|
"builtin": {
|
||||||
"name": "sha256",
|
"name": "sha256",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"linear": {
|
"0": {
|
||||||
"base": 60,
|
"price": {
|
||||||
"word": 12
|
"linear": {
|
||||||
|
"base": 60,
|
||||||
|
"word": 12
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,9 +120,13 @@
|
|||||||
"builtin": {
|
"builtin": {
|
||||||
"name": "ripemd160",
|
"name": "ripemd160",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"linear": {
|
"0": {
|
||||||
"base": 600,
|
"price": {
|
||||||
"word": 120
|
"linear": {
|
||||||
|
"base": 600,
|
||||||
|
"word": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,9 +136,13 @@
|
|||||||
"builtin": {
|
"builtin": {
|
||||||
"name": "identity",
|
"name": "identity",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"linear": {
|
"0": {
|
||||||
"base": 15,
|
"price": {
|
||||||
"word": 3
|
"linear": {
|
||||||
|
"base": 15,
|
||||||
|
"word": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,10 +150,13 @@
|
|||||||
"0x0000000000000000000000000000000000000005": {
|
"0x0000000000000000000000000000000000000005": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"name": "modexp",
|
"name": "modexp",
|
||||||
"activate_at": "0x4d50f8",
|
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"modexp": {
|
"0x4d50f8": {
|
||||||
"divisor": 20
|
"price": {
|
||||||
|
"modexp": {
|
||||||
|
"divisor": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,11 +166,19 @@
|
|||||||
"name": "alt_bn128_add",
|
"name": "alt_bn128_add",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0x4d50f8": {
|
"0x4d50f8": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 500 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 500
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"0xd751a5": {
|
"0xd751a5": {
|
||||||
"info": "EIP 1108 transition at block 14_111_141 (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",
|
"name": "alt_bn128_mul",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0x4d50f8": {
|
"0x4d50f8": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 40000 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 40000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"0xd751a5": {
|
"0xd751a5": {
|
||||||
"info": "EIP 1108 transition at block 14_111_141 (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",
|
"name": "alt_bn128_pairing",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0x4d50f8": {
|
"0x4d50f8": {
|
||||||
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
|
"price": {
|
||||||
|
"alt_bn128_pairing": {
|
||||||
|
"base": 100000,
|
||||||
|
"pair": 80000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"0xd751a5": {
|
"0xd751a5": {
|
||||||
"info": "EIP 1108 transition at block 14_111_141 (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": {
|
"0x0000000000000000000000000000000000000009": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
"name": "blake2_f",
|
"name": "blake2_f",
|
||||||
"activate_at": "0xd751a5",
|
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"blake2_f": {
|
"0xd751a5": {
|
||||||
"gas_per_round": 1
|
"price": {
|
||||||
|
"blake2_f": {
|
||||||
|
"gas_per_round": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,13 +248,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nodes": [
|
"nodes": [
|
||||||
"enode://f6e37b943bad3a78cb8589b1798d30d210ffd39cfcd2c8f2de4f098467fd49c667980100d919da7ca46cd50505d30989abda87f0b9339377de13d6592c22caf8@34.198.49.72:30303",
|
|
||||||
"enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303",
|
"enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303",
|
||||||
"enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@3.217.96.11:30303",
|
"enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@3.217.96.11:30303",
|
||||||
"enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303",
|
"enode://740e1c8ea64e71762c71a463a04e2046070a0c9394fcab5891d41301dc473c0cff00ebab5a9bc87fbcb610ab98ac18225ff897bc8b7b38def5975d5ceb0a7d7c@108.61.170.124:30303",
|
||||||
"enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303",
|
"enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@157.230.31.163:30303"
|
||||||
"enode://38e6e7fd416293ed120d567a2675fe078c0205ab0671abf16982ce969823bd1f3443d590c18b321dfae7dcbe1f6ba98ef8702f255c3c9822a188abb82c53adca@51.77.66.187:30303",
|
|
||||||
"enode://6f289111f7c77c68651b0f4803c3a47bcec801f9c618bb41231a1a24a6dbb9c76f2fdb63ba7a21357c41ebb7f6922c17397c1b5c8f71f7d3ef7965505d4945de@144.217.72.209:30303",
|
|
||||||
"enode://b6340eb94c3db1362ee517801389fe21cce6354275376b1006f8ce84f8a5cfa2b836268b3727be9db7cd3e581f356f39da39418c4ec1d63d959abc235d99cd86@145.239.7.213:30303"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"blockRewardContractAddress": "0x4d0153D434384128D17243409e02fca1B3EE21D6",
|
"blockRewardContractAddress": "0x4d0153D434384128D17243409e02fca1B3EE21D6",
|
||||||
"blockRewardContractTransition": 5761140
|
"blockRewardContractTransition": 5761140,
|
||||||
|
"randomnessContractAddress": {
|
||||||
|
"14350721": "0x67e90a54AeEA85f21949c645082FE95d77BC1E70"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -55,12 +58,12 @@
|
|||||||
"gasLimit": "0x663BE0"
|
"gasLimit": "0x663BE0"
|
||||||
},
|
},
|
||||||
"nodes": [
|
"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://6e3d1b39cbd2a9c4f053a27e68fd90d0bac83691dfdc4a13c59f2555078a71e63c5daaee5a82aa6db500512760a5456f86076bf8bbe8011c27c82ed7d6f5fb26@45.77.140.210:30303",
|
||||||
"enode://31dffed97f8fed1f34fe66453280a89cbeeda60cf28f6fbb212ebbefd7c7566a02c1c7d5c00bbbb49b9fa8a49f157e0f786f379ca9bcbf2fea24de70d70a22b6@206.156.242.61:30303",
|
"enode://31dffed97f8fed1f34fe66453280a89cbeeda60cf28f6fbb212ebbefd7c7566a02c1c7d5c00bbbb49b9fa8a49f157e0f786f379ca9bcbf2fea24de70d70a22b6@206.156.242.61:30303",
|
||||||
"enode://6bdc7553ab2e4914cb47774c1e6d8c8f47ac7c3981891f85f65d06f208ea1bc4d3bf982b330950e0a0cd127efd7145c4df7113159a1d4a06ed722e6c16d0ac6c@45.32.215.190:30303",
|
"enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@178.128.175.244:30303"
|
||||||
"enode://872d82a24144bc007658fb6fac0dcdfb9b63aeb05ef563a06d0186f2d1e5ffbfc5c4f1244891a8a86ef70682b9d24382e654b305224883698862e2df647a4d23@45.76.236.247:30303",
|
|
||||||
"enode://b11fbc6cde81c80be69508aca8ffea8460680a25a9c151b683293f8617282062b8e8e139bf91e88cedf60068a3cf927b0d48832fda5169b58a8f7ce442de6fb4@206.189.76.132:30303",
|
|
||||||
"enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@35.175.15.164:30303"
|
|
||||||
],
|
],
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0x0000000000000000000000000000000000000005": {
|
"0x0000000000000000000000000000000000000005": {
|
||||||
@ -82,11 +85,19 @@
|
|||||||
"name": "alt_bn128_add",
|
"name": "alt_bn128_add",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 500 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 500
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"12598600": {
|
"12598600": {
|
||||||
"info": "EIP 1108 transition",
|
"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",
|
"name": "alt_bn128_mul",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 40000 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 40000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"12598600": {
|
"12598600": {
|
||||||
"info": "EIP 1108 transition",
|
"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",
|
"name": "alt_bn128_pairing",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
|
"price": {
|
||||||
|
"alt_bn128_pairing": {
|
||||||
|
"base": 100000,
|
||||||
|
"pair": 80000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"12598600": {
|
"12598600": {
|
||||||
"info": "EIP 1108 transition",
|
"info": "EIP 1108 transition",
|
||||||
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
|
"price": {
|
||||||
|
"alt_bn128_pairing": {
|
||||||
|
"base": 45000,
|
||||||
|
"pair": 34000
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"blockRewardContractAddress": "0x3145197AD50D7083D0222DE4fCCf67d9BD05C30D",
|
"blockRewardContractAddress": "0x3145197AD50D7083D0222DE4fCCf67d9BD05C30D",
|
||||||
"blockRewardContractTransition": 4639000
|
"blockRewardContractTransition": 4639000,
|
||||||
|
"randomnessContractAddress": {
|
||||||
|
"13391641": "0x8f2b78169B0970F11a762e56659Db52B59CBCf1B"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -60,14 +63,10 @@
|
|||||||
"gasLimit": "0x663BE0"
|
"gasLimit": "0x663BE0"
|
||||||
},
|
},
|
||||||
"nodes": [
|
"nodes": [
|
||||||
"enode://bdcd6f875583df2bd8094f08ae58c7c2db6ed67795ca8c0e6415a30721d3657291aec9b933d15e17e0b36ad7a76424a1447ddbfc75809a04f7a0ffef5617dd56@3.91.206.172:30303",
|
"enode://f11a0f80939b49a28bf99581da9b351a592ec1504b9d32a7dfda79b36510a891e96631239c4166e5c73368c21e9bb3241e7fd6929b899772e5a8fe9a7b7c3af6@45.77.52.149:30303",
|
||||||
"enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303",
|
"enode://e08adce358fc26dfbe1f24ee578dceaa29575ca44a39d9041203131db5135aceba6241840a9b57b1540eeaf7b4eff1aead28a74641be43342c35af454abb31b3@199.247.18.10:30313",
|
||||||
"enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303",
|
"enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303",
|
||||||
"enode://0d1e0372f63a3f0b82d66635ea101ecc0f6797788a078805cc933dd93e6a22f7c9fa51ab4e2d21da02d04480ef19f3bbb9a2b41dd1c262085d295a354bb8b0f9@18.217.47.209:30303",
|
"enode://f11a0f80939b49a28bf99581da9b351a592ec1504b9d32a7dfda79b36510a891e96631239c4166e5c73368c21e9bb3241e7fd6929b899772e5a8fe9a7b7c3af6@45.77.52.149: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"
|
|
||||||
],
|
],
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0000000000000000000000000000000000000005": {
|
"0000000000000000000000000000000000000005": {
|
||||||
@ -89,11 +88,19 @@
|
|||||||
"name": "alt_bn128_add",
|
"name": "alt_bn128_add",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 500 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 500
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"12095200": {
|
"12095200": {
|
||||||
"info": "EIP 1108 transition",
|
"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",
|
"name": "alt_bn128_mul",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 40000 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 40000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"12095200": {
|
"12095200": {
|
||||||
"info": "EIP 1108 transition",
|
"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",
|
"name": "alt_bn128_pairing",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
|
"price": {
|
||||||
|
"alt_bn128_pairing": {
|
||||||
|
"base": 100000,
|
||||||
|
"pair": 80000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"12095200": {
|
"12095200": {
|
||||||
"info": "EIP 1108 transition",
|
"info": "EIP 1108 transition",
|
||||||
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
|
"price": {
|
||||||
|
"alt_bn128_pairing": {
|
||||||
|
"base": 45000,
|
||||||
|
"pair": 34000
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
crates/ethcore/res/chainspec/test/contract_ver_3.sol
Normal file
60
crates/ethcore/res/chainspec/test/contract_ver_3.sol
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
119
crates/ethcore/res/chainspec/test/validator_contract.sol
Normal file
119
crates/ethcore/res/chainspec/test/validator_contract.sol
Normal file
@ -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];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,15 +11,27 @@
|
|||||||
"validators": {
|
"validators": {
|
||||||
"multi": {
|
"multi": {
|
||||||
"0": {
|
"0": {
|
||||||
"list": ["0xcace5b3c29211740e595850e80478416ee77ca21"]
|
"list": [
|
||||||
|
"0xcace5b3c29211740e595850e80478416ee77ca21"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"1300": {
|
"1300": {
|
||||||
"safeContract": "0x22e1229a2c5b95a60983b5577f745a603284f535"
|
"safeContract": "0x22e1229a2c5b95a60983b5577f745a603284f535"
|
||||||
|
},
|
||||||
|
"9186425": {
|
||||||
|
"contract": "0xB87BE9f7196F2AE084Ca1DE6af5264292976e013"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"blockRewardContractAddress": "0x867305d19606aadba405ce534e303d0e225f9556",
|
"blockRewardContractAddress": "0x867305d19606aadba405ce534e303d0e225f9556",
|
||||||
"blockRewardContractTransition": 1310
|
"blockRewardContractTransition": 1310,
|
||||||
|
"blockRewardContractTransitions": {
|
||||||
|
"9186425": "0x481c034c6d9441db23Ea48De68BCAe812C5d39bA"
|
||||||
|
},
|
||||||
|
"randomnessContractAddress": {
|
||||||
|
"9186425": "0x5870b0527DeDB1cFBD9534343Feda1a41Ce47766"
|
||||||
|
},
|
||||||
|
"posdaoTransition": 9186425
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -42,7 +54,9 @@
|
|||||||
"eip1706Transition": 7298030,
|
"eip1706Transition": 7298030,
|
||||||
"eip1884Transition": 7298030,
|
"eip1884Transition": 7298030,
|
||||||
"eip2028Transition": 7298030,
|
"eip2028Transition": 7298030,
|
||||||
"registrar": "0x1ec97dc137f5168af053c24460a1200502e1a9d2"
|
"registrar": "0x6B53721D4f2Fb9514B85f5C49b197D857e36Cf03",
|
||||||
|
"transactionPermissionContract": "0x7Dd7032AA75A37ea0b150f57F899119C7379A78b",
|
||||||
|
"transactionPermissionContractTransition": 9186425
|
||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
@ -54,6 +68,25 @@
|
|||||||
"difficulty": "0x20000",
|
"difficulty": "0x20000",
|
||||||
"gasLimit": "0x989680"
|
"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": {
|
"accounts": {
|
||||||
"0x0000000000000000000000000000000000000005": {
|
"0x0000000000000000000000000000000000000005": {
|
||||||
"builtin": {
|
"builtin": {
|
||||||
@ -74,11 +107,19 @@
|
|||||||
"name": "alt_bn128_add",
|
"name": "alt_bn128_add",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 500 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 500
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"7298030": {
|
"7298030": {
|
||||||
"info": "EIP 1108 transition",
|
"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",
|
"name": "alt_bn128_mul",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_const_operations": { "price": 40000 }}
|
"price": {
|
||||||
|
"alt_bn128_const_operations": {
|
||||||
|
"price": 40000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"7298030": {
|
"7298030": {
|
||||||
"info": "EIP 1108 transition",
|
"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",
|
"name": "alt_bn128_pairing",
|
||||||
"pricing": {
|
"pricing": {
|
||||||
"0": {
|
"0": {
|
||||||
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
|
"price": {
|
||||||
|
"alt_bn128_pairing": {
|
||||||
|
"base": 100000,
|
||||||
|
"pair": 80000
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"7298030": {
|
"7298030": {
|
||||||
"info": "EIP 1108 transition",
|
"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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
133
crates/ethcore/res/contracts/authority_round_random.json
Normal file
133
crates/ethcore/res/contracts/authority_round_random.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
16
crates/ethcore/res/contracts/block_gas_limit.json
Normal file
16
crates/ethcore/res/contracts/block_gas_limit.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "blockGasLimit",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]
|
265
crates/ethcore/res/contracts/test_authority_round_random.json
Normal file
265
crates/ethcore/res/contracts/test_authority_round_random.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
101
crates/ethcore/res/contracts/test_authority_round_random.sol
Normal file
101
crates/ethcore/res/contracts/test_authority_round_random.sol
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
83
crates/ethcore/res/contracts/tx_acl_gas_price.json
Normal file
83
crates/ethcore/res/contracts/tx_acl_gas_price.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
@ -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":"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" }
|
||||||
]
|
]
|
||||||
|
@ -1,5 +1,55 @@
|
|||||||
[
|
[
|
||||||
{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"type":"function"},
|
{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"type":"function"},
|
||||||
{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"validators","type":"address[]"}],"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"
|
||||||
|
}
|
||||||
|
]
|
@ -64,17 +64,20 @@ use ansi_term::Colour;
|
|||||||
use block::{enact_verified, ClosedBlock, Drain, LockedBlock, OpenBlock, SealedBlock};
|
use block::{enact_verified, ClosedBlock, Drain, LockedBlock, OpenBlock, SealedBlock};
|
||||||
use call_contract::RegistryInfo;
|
use call_contract::RegistryInfo;
|
||||||
use client::{
|
use client::{
|
||||||
ancient_import::AncientVerifier, bad_blocks, traits::ForceUpdateSealing, AccountData,
|
ancient_import::AncientVerifier,
|
||||||
BadBlocks, Balance, BlockChain as BlockChainTrait, BlockChainClient, BlockChainReset, BlockId,
|
bad_blocks,
|
||||||
BlockInfo, BlockProducer, BroadcastProposalBlock, Call, CallAnalytics, ChainInfo,
|
traits::{ForceUpdateSealing, TransactionRequest},
|
||||||
ChainMessageType, ChainNotify, ChainRoute, ClientConfig, ClientIoMessage, EngineInfo,
|
AccountData, BadBlocks, Balance, BlockChain as BlockChainTrait, BlockChainClient,
|
||||||
ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient, Mode, NewBlocks, Nonce,
|
BlockChainReset, BlockId, BlockInfo, BlockProducer, BroadcastProposalBlock, Call,
|
||||||
PrepareOpenBlock, ProvingBlockChainClient, PruningInfo, ReopenBlock, ScheduleInfo,
|
CallAnalytics, ChainInfo, ChainMessageType, ChainNotify, ChainRoute, ClientConfig,
|
||||||
SealedBlockImporter, StateClient, StateInfo, StateOrBlock, TraceFilter, TraceId, TransactionId,
|
ClientIoMessage, EngineInfo, ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient,
|
||||||
TransactionInfo, UncleId,
|
Mode, NewBlocks, Nonce, PrepareOpenBlock, ProvingBlockChainClient, PruningInfo, ReopenBlock,
|
||||||
|
ScheduleInfo, SealedBlockImporter, StateClient, StateInfo, StateOrBlock, TraceFilter, TraceId,
|
||||||
|
TransactionId, TransactionInfo, UncleId,
|
||||||
};
|
};
|
||||||
use engines::{
|
use engines::{
|
||||||
epoch::PendingTransition, EngineError, EpochTransition, EthEngine, ForkChoice, MAX_UNCLE_AGE,
|
epoch::PendingTransition, EngineError, EpochTransition, EthEngine, ForkChoice, SealingState,
|
||||||
|
MAX_UNCLE_AGE,
|
||||||
};
|
};
|
||||||
use error::{
|
use error::{
|
||||||
BlockError, CallError, Error as EthcoreError, ErrorKind as EthcoreErrorKind, EthcoreResult,
|
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<SignedTransaction, transaction::Error> {
|
||||||
let authoring_params = self.importer.miner.authoring_params();
|
let authoring_params = self.importer.miner.authoring_params();
|
||||||
let service_transaction_checker = self.importer.miner.service_transaction_checker();
|
let service_transaction_checker = self.importer.miner.service_transaction_checker();
|
||||||
let gas_price = if let Some(checker) = service_transaction_checker {
|
let gas_price = if let Some(checker) = service_transaction_checker {
|
||||||
match checker.check_address(self, authoring_params.author) {
|
match checker.check_address(self, authoring_params.author) {
|
||||||
Ok(true) => U256::zero(),
|
Ok(true) => U256::zero(),
|
||||||
_ => self.importer.miner.sensible_gas_price(),
|
_ => gas_price.unwrap_or_else(|| self.importer.miner.sensible_gas_price()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.importer.miner.sensible_gas_price()
|
self.importer.miner.sensible_gas_price()
|
||||||
};
|
};
|
||||||
let transaction = TypedTransaction::Legacy(transaction::Transaction {
|
let transaction = TypedTransaction::Legacy(transaction::Transaction {
|
||||||
nonce: self.latest_nonce(&authoring_params.author),
|
nonce: nonce.unwrap_or_else(|| self.latest_nonce(&authoring_params.author)),
|
||||||
action: Action::Call(address),
|
action,
|
||||||
gas: self.importer.miner.sensible_gas_limit(),
|
gas: gas.unwrap_or_else(|| self.importer.miner.sensible_gas_limit()),
|
||||||
gas_price,
|
gas_price,
|
||||||
value: U256::zero(),
|
value: U256::zero(),
|
||||||
data: data,
|
data,
|
||||||
});
|
});
|
||||||
let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
|
let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
|
||||||
let signature = self
|
let signature = self
|
||||||
.engine
|
.engine
|
||||||
.sign(transaction.signature_hash(chain_id))
|
.sign(transaction.signature_hash(chain_id))
|
||||||
.map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?;
|
.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
|
self.importer
|
||||||
.miner
|
.miner
|
||||||
.import_own_transaction(self, signed.into())
|
.import_own_transaction(self, signed.into())
|
||||||
@ -2906,7 +2924,7 @@ impl ImportSealedBlock for Client {
|
|||||||
&[],
|
&[],
|
||||||
route.enacted(),
|
route.enacted(),
|
||||||
route.retracted(),
|
route.retracted(),
|
||||||
self.engine.seals_internally().is_some(),
|
self.engine.sealing_state() != SealingState::External,
|
||||||
);
|
);
|
||||||
self.notify(|notify| {
|
self.notify(|notify| {
|
||||||
notify.new_blocks(NewBlocks::new(
|
notify.new_blocks(NewBlocks::new(
|
||||||
@ -3599,8 +3617,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_mark_finalization_correctly_for_parent() {
|
fn should_mark_finalization_correctly_for_parent() {
|
||||||
let client =
|
let client = generate_dummy_client_with_spec_and_data(
|
||||||
generate_dummy_client_with_spec_and_data(Spec::new_test_with_finality, 2, 0, &[]);
|
Spec::new_test_with_finality,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
&[],
|
||||||
|
false,
|
||||||
|
);
|
||||||
let chain = client.chain();
|
let chain = client.chain();
|
||||||
|
|
||||||
let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h));
|
let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h));
|
||||||
|
@ -59,12 +59,12 @@ use vm::Schedule;
|
|||||||
use block::{ClosedBlock, OpenBlock, SealedBlock};
|
use block::{ClosedBlock, OpenBlock, SealedBlock};
|
||||||
use call_contract::{CallContract, RegistryInfo};
|
use call_contract::{CallContract, RegistryInfo};
|
||||||
use client::{
|
use client::{
|
||||||
traits::ForceUpdateSealing, AccountData, BadBlocks, Balance, BlockChain, BlockChainClient,
|
traits::{ForceUpdateSealing, TransactionRequest},
|
||||||
BlockChainInfo, BlockId, BlockInfo, BlockProducer, BlockStatus, BroadcastProposalBlock, Call,
|
AccountData, BadBlocks, Balance, BlockChain, BlockChainClient, BlockChainInfo, BlockId,
|
||||||
CallAnalytics, ChainInfo, EngineInfo, ImportBlock, ImportSealedBlock, IoClient, LastHashes,
|
BlockInfo, BlockProducer, BlockStatus, BroadcastProposalBlock, Call, CallAnalytics, ChainInfo,
|
||||||
Mode, Nonce, PrepareOpenBlock, ProvingBlockChainClient, ReopenBlock, ScheduleInfo,
|
EngineInfo, ImportBlock, ImportSealedBlock, IoClient, LastHashes, Mode, Nonce,
|
||||||
SealedBlockImporter, StateClient, StateOrBlock, TraceFilter, TraceId, TransactionId,
|
PrepareOpenBlock, ProvingBlockChainClient, ReopenBlock, ScheduleInfo, SealedBlockImporter,
|
||||||
TransactionInfo, UncleId,
|
StateClient, StateOrBlock, TraceFilter, TraceId, TransactionId, TransactionInfo, UncleId,
|
||||||
};
|
};
|
||||||
use engines::EthEngine;
|
use engines::EthEngine;
|
||||||
use error::{Error, EthcoreResult};
|
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<SignedTransaction, transaction::Error> {
|
||||||
let transaction = TypedTransaction::Legacy(Transaction {
|
let transaction = TypedTransaction::Legacy(Transaction {
|
||||||
nonce: self.latest_nonce(&self.miner.authoring_params().author),
|
nonce: nonce
|
||||||
action: Action::Call(address),
|
.unwrap_or_else(|| self.latest_nonce(&self.miner.authoring_params().author)),
|
||||||
gas: self.spec.gas_limit,
|
action,
|
||||||
gas_price: U256::zero(),
|
gas: gas.unwrap_or(self.spec.gas_limit),
|
||||||
|
gas_price: gas_price.unwrap_or_else(U256::zero),
|
||||||
value: U256::default(),
|
value: U256::default(),
|
||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
@ -1054,7 +1064,11 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
.engine
|
.engine
|
||||||
.sign(transaction.signature_hash(chain_id))
|
.sign(transaction.signature_hash(chain_id))
|
||||||
.unwrap();
|
.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())
|
self.miner.import_own_transaction(self, signed.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ use types::{
|
|||||||
pruning_info::PruningInfo,
|
pruning_info::PruningInfo,
|
||||||
receipt::LocalizedReceipt,
|
receipt::LocalizedReceipt,
|
||||||
trace_filter::Filter as TraceFilter,
|
trace_filter::Filter as TraceFilter,
|
||||||
transaction::{self, LocalizedTransaction, SignedTransaction},
|
transaction::{self, Action, LocalizedTransaction, SignedTransaction},
|
||||||
BlockNumber,
|
BlockNumber,
|
||||||
};
|
};
|
||||||
use vm::LastHashes;
|
use vm::LastHashes;
|
||||||
@ -427,8 +427,15 @@ pub trait BlockChainClient:
|
|||||||
/// Returns information about pruning/data availability.
|
/// Returns information about pruning/data availability.
|
||||||
fn pruning_info(&self) -> PruningInfo;
|
fn pruning_info(&self) -> PruningInfo;
|
||||||
|
|
||||||
/// Schedule state-altering transaction to be executed on the next pending block.
|
/// Returns a transaction signed with the key configured in the engine signer.
|
||||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error>;
|
fn create_transaction(
|
||||||
|
&self,
|
||||||
|
tx_request: TransactionRequest,
|
||||||
|
) -> Result<SignedTransaction, transaction::Error>;
|
||||||
|
|
||||||
|
/// 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.
|
/// Get the address of the registry itself.
|
||||||
fn registrar_address(&self) -> Option<Address>;
|
fn registrar_address(&self) -> Option<Address>;
|
||||||
@ -437,6 +444,55 @@ pub trait BlockChainClient:
|
|||||||
fn is_processing_fork(&self) -> bool;
|
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<U256>,
|
||||||
|
/// Transaction gas price
|
||||||
|
pub gas_price: Option<U256>,
|
||||||
|
/// Transaction nonce
|
||||||
|
pub nonce: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<T: Into<Option<U256>>>(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
|
/// Provides `reopen_block` method
|
||||||
pub trait ReopenBlock {
|
pub trait ReopenBlock {
|
||||||
/// Reopens an OpenBlock and updates uncles.
|
/// Reopens an OpenBlock and updates uncles.
|
||||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! 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<U256> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ use std::collections::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use ethereum_types::{Address, H256};
|
use ethereum_types::{Address, H256};
|
||||||
|
use types::BlockNumber;
|
||||||
|
|
||||||
use engines::validator_set::SimpleList;
|
use engines::validator_set::SimpleList;
|
||||||
|
|
||||||
@ -32,20 +33,23 @@ pub struct UnknownValidator;
|
|||||||
/// Rolling finality checker for authority round consensus.
|
/// Rolling finality checker for authority round consensus.
|
||||||
/// Stores a chain of unfinalized hashes that can be pushed onto.
|
/// Stores a chain of unfinalized hashes that can be pushed onto.
|
||||||
pub struct RollingFinality {
|
pub struct RollingFinality {
|
||||||
headers: VecDeque<(H256, Vec<Address>)>,
|
headers: VecDeque<(H256, BlockNumber, Vec<Address>)>,
|
||||||
signers: SimpleList,
|
signers: SimpleList,
|
||||||
sign_count: HashMap<Address, usize>,
|
sign_count: HashMap<Address, usize>,
|
||||||
last_pushed: Option<H256>,
|
last_pushed: Option<H256>,
|
||||||
|
/// First block for which a 2/3 quorum (instead of 1/2) is required.
|
||||||
|
two_thirds_majority_transition: BlockNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RollingFinality {
|
impl RollingFinality {
|
||||||
/// Create a blank finality checker under the given validator set.
|
/// Create a blank finality checker under the given validator set.
|
||||||
pub fn blank(signers: Vec<Address>) -> Self {
|
pub fn blank(signers: Vec<Address>, two_thirds_majority_transition: BlockNumber) -> Self {
|
||||||
RollingFinality {
|
RollingFinality {
|
||||||
headers: VecDeque::new(),
|
headers: VecDeque::new(),
|
||||||
signers: SimpleList::new(signers),
|
signers: SimpleList::new(signers),
|
||||||
sign_count: HashMap::new(),
|
sign_count: HashMap::new(),
|
||||||
last_pushed: None,
|
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.
|
/// Fails if any provided signature isn't part of the signers set.
|
||||||
pub fn build_ancestry_subchain<I>(&mut self, iterable: I) -> Result<(), UnknownValidator>
|
pub fn build_ancestry_subchain<I>(&mut self, iterable: I) -> Result<(), UnknownValidator>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (H256, Vec<Address>)>,
|
I: IntoIterator<Item = (H256, BlockNumber, Vec<Address>)>,
|
||||||
{
|
{
|
||||||
self.clear();
|
self.clear();
|
||||||
for (hash, signers) in iterable {
|
for (hash, number, signers) in iterable {
|
||||||
if signers.iter().any(|s| !self.signers.contains(s)) {
|
if signers.iter().any(|s| !self.signers.contains(s)) {
|
||||||
return Err(UnknownValidator);
|
return Err(UnknownValidator);
|
||||||
}
|
}
|
||||||
if self.last_pushed.is_none() {
|
if self.last_pushed.is_none() {
|
||||||
self.last_pushed = Some(hash)
|
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.
|
// break when we've got our first finalized block.
|
||||||
{
|
if self.is_finalized() {
|
||||||
let current_signed = self.sign_count.len();
|
let (hash, _, signers) = self
|
||||||
|
.headers
|
||||||
let new_signers = signers
|
.pop_front()
|
||||||
.iter()
|
.expect("we just pushed a block; qed");
|
||||||
.filter(|s| !self.sign_count.contains_key(s))
|
self.remove_signers(&signers);
|
||||||
.count();
|
trace!(target: "finality", "Encountered already finalized block {}", hash);
|
||||||
let would_be_finalized = (current_signed + new_signers) * 2 > self.signers.len();
|
break;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.headers.push_front((hash, signers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(target: "finality", "Rolling finality state: {:?}", self.headers);
|
trace!(target: "finality", "Rolling finality state: {:?}", self.headers);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the finality status, but keeps the validator set.
|
/// Clears the finality status, but keeps the validator set.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.headers.clear();
|
self.headers.clear();
|
||||||
self.sign_count.clear();
|
self.sign_count.clear();
|
||||||
@ -108,7 +102,7 @@ impl RollingFinality {
|
|||||||
/// Get an iterator over stored hashes in order.
|
/// Get an iterator over stored hashes in order.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn unfinalized_hashes(&self) -> impl Iterator<Item = &H256> {
|
pub fn unfinalized_hashes(&self) -> impl Iterator<Item = &H256> {
|
||||||
self.headers.iter().map(|(h, _)| h)
|
self.headers.iter().map(|(h, _, _)| h)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the validator set.
|
/// Get the validator set.
|
||||||
@ -124,41 +118,26 @@ impl RollingFinality {
|
|||||||
pub fn push_hash(
|
pub fn push_hash(
|
||||||
&mut self,
|
&mut self,
|
||||||
head: H256,
|
head: H256,
|
||||||
|
number: BlockNumber,
|
||||||
signers: Vec<Address>,
|
signers: Vec<Address>,
|
||||||
) -> Result<Vec<H256>, UnknownValidator> {
|
) -> Result<Vec<H256>, UnknownValidator> {
|
||||||
if signers.iter().any(|s| !self.signers.contains(s)) {
|
if signers.iter().any(|s| !self.signers.contains(s)) {
|
||||||
return Err(UnknownValidator);
|
return Err(UnknownValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
for signer in signers.iter() {
|
self.add_signers(&signers);
|
||||||
*self.sign_count.entry(*signer).or_insert(0) += 1;
|
self.headers.push_back((head, number, signers));
|
||||||
}
|
|
||||||
|
|
||||||
self.headers.push_back((head, signers));
|
|
||||||
|
|
||||||
let mut newly_finalized = Vec::new();
|
let mut newly_finalized = Vec::new();
|
||||||
|
|
||||||
while self.sign_count.len() * 2 > self.signers.len() {
|
while self.is_finalized() {
|
||||||
let (hash, signers) = self
|
let (hash, _, signers) = self
|
||||||
.headers
|
.headers
|
||||||
.pop_front()
|
.pop_front()
|
||||||
.expect("headers length always greater than sign count length; qed");
|
.expect("headers length always greater than sign count length; qed");
|
||||||
|
self.remove_signers(&signers);
|
||||||
|
|
||||||
newly_finalized.push(hash);
|
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);
|
trace!(target: "finality", "Blocks finalized by {:?}: {:?}", head, newly_finalized);
|
||||||
@ -166,19 +145,62 @@ impl RollingFinality {
|
|||||||
self.last_pushed = Some(head);
|
self.last_pushed = Some(head);
|
||||||
Ok(newly_finalized)
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::RollingFinality;
|
use super::RollingFinality;
|
||||||
use ethereum_types::{Address, H256};
|
use ethereum_types::{Address, H256};
|
||||||
|
use types::BlockNumber;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rejects_unknown_signers() {
|
fn rejects_unknown_signers() {
|
||||||
let signers = (0..3).map(|_| Address::random()).collect::<Vec<_>>();
|
let signers = (0..3).map(|_| Address::random()).collect::<Vec<_>>();
|
||||||
let mut finality = RollingFinality::blank(signers.clone());
|
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
|
||||||
assert!(finality
|
assert!(finality
|
||||||
.push_hash(H256::random(), vec![signers[0], Address::random()])
|
.push_hash(H256::random(), 0, vec![signers[0], Address::random()])
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,19 +208,25 @@ mod tests {
|
|||||||
fn finalize_multiple() {
|
fn finalize_multiple() {
|
||||||
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
|
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();
|
let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect();
|
||||||
|
|
||||||
// 3 / 6 signers is < 51% so no finality.
|
// 3 / 6 signers is < 51% so no finality.
|
||||||
for (i, hash) in hashes.iter().take(6).cloned().enumerate() {
|
for (i, hash) in hashes.iter().take(6).cloned().enumerate() {
|
||||||
let i = i % 3;
|
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
|
// after pushing a block signed by a fourth validator, the first four
|
||||||
// blocks of the unverified chain become verified.
|
// blocks of the unverified chain become verified.
|
||||||
assert_eq!(
|
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]]
|
vec![hashes[0], hashes[1], hashes[2], hashes[3]]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -206,12 +234,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn finalize_multiple_signers() {
|
fn finalize_multiple_signers() {
|
||||||
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
|
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();
|
let hash = H256::random();
|
||||||
|
|
||||||
// after pushing a block signed by four validators, it becomes verified right away.
|
// after pushing a block signed by four validators, it becomes verified right away.
|
||||||
assert_eq!(
|
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]
|
vec![hash]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -220,10 +248,9 @@ mod tests {
|
|||||||
fn from_ancestry() {
|
fn from_ancestry() {
|
||||||
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
|
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
|
||||||
let hashes: Vec<_> = (0..12)
|
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();
|
.collect();
|
||||||
|
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
|
||||||
let mut finality = RollingFinality::blank(signers.clone());
|
|
||||||
finality
|
finality
|
||||||
.build_ancestry_subchain(hashes.iter().rev().cloned())
|
.build_ancestry_subchain(hashes.iter().rev().cloned())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -239,12 +266,13 @@ mod tests {
|
|||||||
.map(|i| {
|
.map(|i| {
|
||||||
(
|
(
|
||||||
H256::random(),
|
H256::random(),
|
||||||
|
i as u64,
|
||||||
vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]],
|
vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]],
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut finality = RollingFinality::blank(signers.clone());
|
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
|
||||||
finality
|
finality
|
||||||
.build_ancestry_subchain(hashes.iter().rev().cloned())
|
.build_ancestry_subchain(hashes.iter().rev().cloned())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -254,4 +282,91 @@ mod tests {
|
|||||||
assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0));
|
assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0));
|
||||||
assert_eq!(finality.subchain_head(), 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::<Vec<_>>();
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
257
crates/ethcore/src/engines/authority_round/randomness.rs
Normal file
257
crates/ethcore/src/engines/authority_round/randomness.rs
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! On-chain randomness generation for authority round
|
||||||
|
//!
|
||||||
|
//! This module contains the support code for the on-chain randomness generation used by AuRa. Its
|
||||||
|
//! core is the finite state machine `RandomnessPhase`, which can be loaded from the blockchain
|
||||||
|
//! state, then asked to perform potentially necessary transaction afterwards using the `advance()`
|
||||||
|
//! method.
|
||||||
|
//!
|
||||||
|
//! No additional state is kept inside the `RandomnessPhase`, it must be passed in each time.
|
||||||
|
//!
|
||||||
|
//! The process of generating random numbers is a simple finite state machine:
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! +
|
||||||
|
//! |
|
||||||
|
//! |
|
||||||
|
//! |
|
||||||
|
//! +--------------+ +-------v-------+
|
||||||
|
//! | | | |
|
||||||
|
//! | BeforeCommit <------------------------------+ Waiting |
|
||||||
|
//! | | enter commit phase | |
|
||||||
|
//! +------+-------+ +-------^-------+
|
||||||
|
//! | |
|
||||||
|
//! | call |
|
||||||
|
//! | `commitHash()` | call
|
||||||
|
//! | | `revealNumber()`
|
||||||
|
//! | |
|
||||||
|
//! +------v-------+ +-------+-------+
|
||||||
|
//! | | | |
|
||||||
|
//! | Committed +------------------------------> Reveal |
|
||||||
|
//! | | enter reveal phase | |
|
||||||
|
//! +--------------+ +---------------+
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Phase transitions are performed by the smart contract and simply queried by the engine.
|
||||||
|
//!
|
||||||
|
//! Randomness generation works as follows:
|
||||||
|
//! * During the commit phase, all validators locally generate a random number, and commit that number's hash to the
|
||||||
|
//! contract.
|
||||||
|
//! * During the reveal phase, all validators reveal their local random number to the contract. The contract should
|
||||||
|
//! verify that it matches the committed hash.
|
||||||
|
//! * Finally, the XOR of all revealed numbers is used as an on-chain random number.
|
||||||
|
//!
|
||||||
|
//! An adversary can only influence that number by either controlling _all_ validators who committed, or, to a lesser
|
||||||
|
//! extent, by not revealing committed numbers.
|
||||||
|
//! The length of the commit and reveal phases, as well as any penalties for failure to reveal, are defined by the
|
||||||
|
//! contract.
|
||||||
|
//!
|
||||||
|
//! A typical case of using `RandomnessPhase` is:
|
||||||
|
//!
|
||||||
|
//! 1. `RandomnessPhase::load()` the phase from the blockchain data.
|
||||||
|
//! 2. Call `RandomnessPhase::advance()`.
|
||||||
|
//!
|
||||||
|
//! A production implementation of a randomness contract can be found here:
|
||||||
|
//! https://github.com/poanetwork/posdao-contracts/blob/4fddb108993d4962951717b49222327f3d94275b/contracts/RandomAuRa.sol
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use crypto::publickey::{ecies, Error as CryptoError};
|
||||||
|
use derive_more::Display;
|
||||||
|
use engines::signer::EngineSigner;
|
||||||
|
use ethabi::Hash;
|
||||||
|
use ethabi_contract::use_contract;
|
||||||
|
use ethereum_types::{Address, H256, U256};
|
||||||
|
use hash::keccak;
|
||||||
|
use log::{debug, error};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use super::util::{BoundContract, CallError};
|
||||||
|
|
||||||
|
/// Random number type expected by the contract: This is generated locally, kept secret during the commit phase, and
|
||||||
|
/// published in the reveal phase.
|
||||||
|
pub type RandNumber = H256;
|
||||||
|
|
||||||
|
use_contract!(aura_random, "res/contracts/authority_round_random.json");
|
||||||
|
|
||||||
|
/// Validated randomness phase state.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RandomnessPhase {
|
||||||
|
// NOTE: Some states include information already gathered during `load` (e.g. `our_address`,
|
||||||
|
// `round`) for efficiency reasons.
|
||||||
|
/// Waiting for the next phase.
|
||||||
|
///
|
||||||
|
/// This state indicates either the successful revelation in this round or having missed the
|
||||||
|
/// window to make a commitment, i.e. having failed to commit during the commit phase.
|
||||||
|
Waiting,
|
||||||
|
/// Indicates a commitment is possible, but still missing.
|
||||||
|
BeforeCommit,
|
||||||
|
/// Indicates a successful commitment, waiting for the commit phase to end.
|
||||||
|
Committed,
|
||||||
|
/// Indicates revealing is expected as the next step.
|
||||||
|
Reveal { our_address: Address, round: U256 },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase loading error for randomness generation state machine.
|
||||||
|
///
|
||||||
|
/// This error usually indicates a bug in either the smart contract, the phase loading function or
|
||||||
|
/// some state being lost.
|
||||||
|
///
|
||||||
|
/// `BadRandNumber` will usually result in punishment by the contract or the other validators.
|
||||||
|
#[derive(Debug, Display)]
|
||||||
|
pub enum PhaseError {
|
||||||
|
/// The smart contract reported that we already revealed something while still being in the
|
||||||
|
/// commit phase.
|
||||||
|
#[display(fmt = "Revealed during commit phase")]
|
||||||
|
RevealedInCommit,
|
||||||
|
/// Failed to load contract information.
|
||||||
|
#[display(fmt = "Error loading randomness contract information: {:?}", _0)]
|
||||||
|
LoadFailed(CallError),
|
||||||
|
/// Failed to load the stored encrypted random number.
|
||||||
|
#[display(fmt = "Failed to load random number from the randomness contract")]
|
||||||
|
BadRandNumber,
|
||||||
|
/// Failed to encrypt random number.
|
||||||
|
#[display(fmt = "Failed to encrypt random number: {}", _0)]
|
||||||
|
Crypto(CryptoError),
|
||||||
|
/// Failed to get the engine signer's public key.
|
||||||
|
#[display(fmt = "Failed to get the engine signer's public key")]
|
||||||
|
MissingPublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CryptoError> for PhaseError {
|
||||||
|
fn from(err: CryptoError) -> PhaseError {
|
||||||
|
PhaseError::Crypto(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RandomnessPhase {
|
||||||
|
/// Determine randomness generation state from the contract.
|
||||||
|
///
|
||||||
|
/// Calls various constant contract functions to determine the precise state that needs to be
|
||||||
|
/// handled (that is, the phase and whether or not the current validator still needs to send
|
||||||
|
/// commitments or reveal random numbers).
|
||||||
|
pub fn load(
|
||||||
|
contract: &BoundContract,
|
||||||
|
our_address: Address,
|
||||||
|
) -> Result<RandomnessPhase, PhaseError> {
|
||||||
|
// Determine the current round and which phase we are in.
|
||||||
|
let round = contract
|
||||||
|
.call_const(aura_random::functions::current_collect_round::call())
|
||||||
|
.map_err(PhaseError::LoadFailed)?;
|
||||||
|
let is_commit_phase = contract
|
||||||
|
.call_const(aura_random::functions::is_commit_phase::call())
|
||||||
|
.map_err(PhaseError::LoadFailed)?;
|
||||||
|
|
||||||
|
// Ensure we are not committing or revealing twice.
|
||||||
|
let committed = contract
|
||||||
|
.call_const(aura_random::functions::is_committed::call(
|
||||||
|
round,
|
||||||
|
our_address,
|
||||||
|
))
|
||||||
|
.map_err(PhaseError::LoadFailed)?;
|
||||||
|
let revealed: bool = contract
|
||||||
|
.call_const(aura_random::functions::sent_reveal::call(
|
||||||
|
round,
|
||||||
|
our_address,
|
||||||
|
))
|
||||||
|
.map_err(PhaseError::LoadFailed)?;
|
||||||
|
|
||||||
|
// With all the information known, we can determine the actual state we are in.
|
||||||
|
if is_commit_phase {
|
||||||
|
if revealed {
|
||||||
|
return Err(PhaseError::RevealedInCommit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !committed {
|
||||||
|
Ok(RandomnessPhase::BeforeCommit)
|
||||||
|
} else {
|
||||||
|
Ok(RandomnessPhase::Committed)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !committed {
|
||||||
|
// We apparently entered too late to make a commitment, wait until we get a chance again.
|
||||||
|
return Ok(RandomnessPhase::Waiting);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !revealed {
|
||||||
|
Ok(RandomnessPhase::Reveal { our_address, round })
|
||||||
|
} else {
|
||||||
|
Ok(RandomnessPhase::Waiting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Advance the random seed construction process as far as possible.
|
||||||
|
///
|
||||||
|
/// Returns the encoded contract call necessary to advance the randomness contract's state.
|
||||||
|
///
|
||||||
|
/// **Warning**: After calling the `advance()` function, wait until the returned transaction has been included in
|
||||||
|
/// a block before calling it again; otherwise spurious transactions resulting in punishments might be executed.
|
||||||
|
pub fn advance<R: Rng>(
|
||||||
|
self,
|
||||||
|
contract: &BoundContract,
|
||||||
|
rng: &mut R,
|
||||||
|
signer: &dyn EngineSigner,
|
||||||
|
) -> Result<Option<Bytes>, PhaseError> {
|
||||||
|
match self {
|
||||||
|
RandomnessPhase::Waiting | RandomnessPhase::Committed => Ok(None),
|
||||||
|
RandomnessPhase::BeforeCommit => {
|
||||||
|
// Generate a new random number, but don't reveal it yet. Instead, we publish its hash to the
|
||||||
|
// randomness contract, together with the number encrypted to ourselves. That way we will later be
|
||||||
|
// able to decrypt and reveal it, and other parties are able to verify it against the hash.
|
||||||
|
let number: RandNumber = rng.gen();
|
||||||
|
let number_hash: Hash = keccak(number.0);
|
||||||
|
let public = signer.public().ok_or(PhaseError::MissingPublicKey)?;
|
||||||
|
let cipher = ecies::encrypt(&public, number_hash.as_bytes(), number.as_bytes())?;
|
||||||
|
|
||||||
|
debug!(target: "engine", "Randomness contract: committing {}.", number_hash);
|
||||||
|
// Return the call data for the transaction that commits the hash and the encrypted number.
|
||||||
|
let (data, _decoder) =
|
||||||
|
aura_random::functions::commit_hash::call(number_hash, cipher);
|
||||||
|
Ok(Some(data))
|
||||||
|
}
|
||||||
|
RandomnessPhase::Reveal { round, our_address } => {
|
||||||
|
// Load the hash and encrypted number that we stored in the commit phase.
|
||||||
|
let call = aura_random::functions::get_commit_and_cipher::call(round, our_address);
|
||||||
|
let (committed_hash, cipher) =
|
||||||
|
contract.call_const(call).map_err(PhaseError::LoadFailed)?;
|
||||||
|
|
||||||
|
// Decrypt the number and check against the hash.
|
||||||
|
let number_bytes = signer.decrypt(&committed_hash.0, &cipher)?;
|
||||||
|
let number = if number_bytes.len() == 32 {
|
||||||
|
RandNumber::from_slice(&number_bytes)
|
||||||
|
} else {
|
||||||
|
// This can only happen if there is a bug in the smart contract,
|
||||||
|
// or if the entire network goes awry.
|
||||||
|
error!(target: "engine", "Decrypted random number has the wrong length.");
|
||||||
|
return Err(PhaseError::BadRandNumber);
|
||||||
|
};
|
||||||
|
let number_hash: Hash = keccak(number.0);
|
||||||
|
if number_hash != committed_hash {
|
||||||
|
error!(target: "engine", "Decrypted random number doesn't agree with the hash.");
|
||||||
|
return Err(PhaseError::BadRandNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(target: "engine", "Randomness contract: scheduling tx to reveal our random number {} (round={}, our_address={}).", number_hash, round, our_address);
|
||||||
|
// We are now sure that we have the correct secret and can reveal it. So we return the call data for the
|
||||||
|
// transaction that stores the revealed random bytes on the contract.
|
||||||
|
let (data, _decoder) = aura_random::functions::reveal_number::call(number.0);
|
||||||
|
Ok(Some(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
crates/ethcore/src/engines/authority_round/util.rs
Normal file
119
crates/ethcore/src/engines/authority_round/util.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Utility functions.
|
||||||
|
//!
|
||||||
|
//! Contains small functions used by the AuRa engine that are not strictly limited to that scope.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use client::{traits::EngineClient, BlockChainClient};
|
||||||
|
use ethabi::{self, FunctionOutputDecoder};
|
||||||
|
use ethabi_contract::use_contract;
|
||||||
|
use ethereum_types::{Address, U256};
|
||||||
|
use log::{debug, error};
|
||||||
|
use types::{header::Header, ids::BlockId};
|
||||||
|
|
||||||
|
/// A contract bound to a client and block number.
|
||||||
|
///
|
||||||
|
/// A bound contract is a combination of a `Client` reference, a `BlockId` and a contract `Address`.
|
||||||
|
/// These three parts are enough to call a contract's function; return values are automatically
|
||||||
|
/// decoded.
|
||||||
|
pub struct BoundContract<'a> {
|
||||||
|
client: &'a dyn EngineClient,
|
||||||
|
block_id: BlockId,
|
||||||
|
contract_addr: Address,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contract call failed error.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CallError {
|
||||||
|
/// The call itself failed.
|
||||||
|
CallFailed(String),
|
||||||
|
/// Decoding the return value failed or the decoded value was a failure.
|
||||||
|
DecodeFailed(ethabi::Error),
|
||||||
|
/// The passed in client reference could not be upgraded to a `BlockchainClient`.
|
||||||
|
NotFullClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for BoundContract<'a> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.debug_struct("BoundContract")
|
||||||
|
.field("client", &(self.client as *const dyn EngineClient))
|
||||||
|
.field("block_id", &self.block_id)
|
||||||
|
.field("contract_addr", &self.contract_addr)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BoundContract<'a> {
|
||||||
|
/// Create a new `BoundContract`.
|
||||||
|
pub fn new(
|
||||||
|
client: &dyn EngineClient,
|
||||||
|
block_id: BlockId,
|
||||||
|
contract_addr: Address,
|
||||||
|
) -> BoundContract {
|
||||||
|
BoundContract {
|
||||||
|
client,
|
||||||
|
block_id,
|
||||||
|
contract_addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a function call to an Ethereum machine that doesn't create a transaction or change the state.
|
||||||
|
///
|
||||||
|
/// Runs a constant function call on `client`. The `call` value can be serialized by calling any
|
||||||
|
/// api function generated by the `use_contract!` macro. This does not create any transactions, it only produces a
|
||||||
|
/// result based on the state at the current block: It is constant in the sense that it does not alter the EVM
|
||||||
|
/// state.
|
||||||
|
pub fn call_const<D>(&self, call: (ethabi::Bytes, D)) -> Result<D::Output, CallError>
|
||||||
|
where
|
||||||
|
D: ethabi::FunctionOutputDecoder,
|
||||||
|
{
|
||||||
|
let (data, output_decoder) = call;
|
||||||
|
|
||||||
|
let call_return = self
|
||||||
|
.client
|
||||||
|
.as_full_client()
|
||||||
|
.ok_or(CallError::NotFullClient)?
|
||||||
|
.call_contract(self.block_id, self.contract_addr, data)
|
||||||
|
.map_err(CallError::CallFailed)?;
|
||||||
|
|
||||||
|
// Decode the result and return it.
|
||||||
|
output_decoder
|
||||||
|
.decode(call_return.as_slice())
|
||||||
|
.map_err(CallError::DecodeFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use_contract!(contract, "res/contracts/block_gas_limit.json");
|
||||||
|
|
||||||
|
pub fn block_gas_limit(
|
||||||
|
full_client: &dyn BlockChainClient,
|
||||||
|
header: &Header,
|
||||||
|
address: Address,
|
||||||
|
) -> Option<U256> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ use super::validator_set::{new_validator_set, SimpleList, ValidatorSet};
|
|||||||
use block::*;
|
use block::*;
|
||||||
use client::EngineClient;
|
use client::EngineClient;
|
||||||
use crypto::publickey::{self, Signature};
|
use crypto::publickey::{self, Signature};
|
||||||
use engines::{signer::EngineSigner, ConstructedVerifier, Engine, EngineError, Seal};
|
use engines::{signer::EngineSigner, ConstructedVerifier, Engine, EngineError, Seal, SealingState};
|
||||||
use error::{BlockError, Error};
|
use error::{BlockError, Error};
|
||||||
use ethereum_types::{H256, H520};
|
use ethereum_types::{H256, H520};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
@ -104,8 +104,12 @@ impl Engine<EthereumMachine> for BasicAuthority {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seals_internally(&self) -> Option<bool> {
|
fn sealing_state(&self) -> SealingState {
|
||||||
Some(self.signer.read().is_some())
|
if self.signer.read().is_some() {
|
||||||
|
SealingState::Ready
|
||||||
|
} else {
|
||||||
|
SealingState::NotReady
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to seal the block internally.
|
/// Attempt to seal the block internally.
|
||||||
@ -197,8 +201,8 @@ impl Engine<EthereumMachine> for BasicAuthority {
|
|||||||
self.validators.register_client(client);
|
self.validators.register_client(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_signer(&self, signer: Box<dyn EngineSigner>) {
|
fn set_signer(&self, signer: Option<Box<dyn EngineSigner>>) {
|
||||||
*self.signer.write() = Some(signer);
|
*self.signer.write() = signer;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, hash: H256) -> Result<Signature, Error> {
|
fn sign(&self, hash: H256) -> Result<Signature, Error> {
|
||||||
@ -223,7 +227,7 @@ impl Engine<EthereumMachine> for BasicAuthority {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use accounts::AccountProvider;
|
use accounts::AccountProvider;
|
||||||
use block::*;
|
use block::*;
|
||||||
use engines::Seal;
|
use engines::{Seal, SealingState};
|
||||||
use ethereum_types::H520;
|
use ethereum_types::H520;
|
||||||
use hash::keccak;
|
use hash::keccak;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
@ -269,7 +273,7 @@ mod tests {
|
|||||||
|
|
||||||
let spec = new_test_authority();
|
let spec = new_test_authority();
|
||||||
let engine = &*spec.engine;
|
let engine = &*spec.engine;
|
||||||
engine.set_signer(Box::new((Arc::new(tap), addr, "".into())));
|
engine.set_signer(Some(Box::new((Arc::new(tap), addr, "".into()))));
|
||||||
let genesis_header = spec.genesis_header();
|
let genesis_header = spec.genesis_header();
|
||||||
let db = spec
|
let db = spec
|
||||||
.ensure_db_good(get_temp_state_db(), &Default::default())
|
.ensure_db_good(get_temp_state_db(), &Default::default())
|
||||||
@ -296,13 +300,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn seals_internally() {
|
fn sealing_state() {
|
||||||
let tap = AccountProvider::transient_provider();
|
let tap = AccountProvider::transient_provider();
|
||||||
let authority = tap.insert_account(keccak("").into(), &"".into()).unwrap();
|
let authority = tap.insert_account(keccak("").into(), &"".into()).unwrap();
|
||||||
|
|
||||||
let engine = new_test_authority().engine;
|
let engine = new_test_authority().engine;
|
||||||
assert!(!engine.seals_internally().unwrap());
|
assert_eq!(SealingState::NotReady, engine.sealing_state());
|
||||||
engine.set_signer(Box::new((Arc::new(tap), authority, "".into())));
|
engine.set_signer(Some(Box::new((Arc::new(tap), authority, "".into()))));
|
||||||
assert!(engine.seals_internally().unwrap());
|
assert_eq!(SealingState::Ready, engine.sealing_state());
|
||||||
|
engine.set_signer(None);
|
||||||
|
assert_eq!(SealingState::NotReady, engine.sealing_state());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,8 @@
|
|||||||
///
|
///
|
||||||
/// 1. Set a signer using `Engine::set_signer()`. If a miner account was set up through
|
/// 1. Set a signer using `Engine::set_signer()`. If a miner account was set up through
|
||||||
/// a config file or CLI flag `MinerService::set_author()` will eventually set the signer
|
/// a config file or CLI flag `MinerService::set_author()` will eventually set the signer
|
||||||
/// 2. We check that the engine seals internally through `Clique::seals_internally()`
|
/// 2. We check that the engine is ready for sealing through `Clique::sealing_state()`
|
||||||
/// Note: This is always true for Clique
|
/// Note: This is always `SealingState::Ready` for Clique
|
||||||
/// 3. Calling `Clique::new()` will spawn a `StepService` thread. This thread will call `Engine::step()`
|
/// 3. Calling `Clique::new()` will spawn a `StepService` thread. This thread will call `Engine::step()`
|
||||||
/// periodically. Internally, the Clique `step()` function calls `Client::update_sealing()`, which is
|
/// periodically. Internally, the Clique `step()` function calls `Client::update_sealing()`, which is
|
||||||
/// what makes and seals a block.
|
/// what makes and seals a block.
|
||||||
@ -71,7 +71,7 @@ use client::{traits::ForceUpdateSealing, BlockId, EngineClient};
|
|||||||
use crypto::publickey::Signature;
|
use crypto::publickey::Signature;
|
||||||
use engines::{
|
use engines::{
|
||||||
clique::util::{extract_signers, recover_creator},
|
clique::util::{extract_signers, recover_creator},
|
||||||
Engine, EngineError, Seal,
|
Engine, EngineError, Seal, SealingState,
|
||||||
};
|
};
|
||||||
use error::{BlockError, Error};
|
use error::{BlockError, Error};
|
||||||
use ethereum_types::{Address, H160, H256, H64, U256};
|
use ethereum_types::{Address, H160, H256, H64, U256};
|
||||||
@ -476,8 +476,8 @@ impl Engine<EthereumMachine> for Clique {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Clique doesn't require external work to seal, so we always return true here.
|
/// Clique doesn't require external work to seal, so we always return true here.
|
||||||
fn seals_internally(&self) -> Option<bool> {
|
fn sealing_state(&self) -> SealingState {
|
||||||
Some(true)
|
SealingState::Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if we are ready to seal, the real sealing (signing extra_data) is actually done in `on_seal_block()`.
|
/// Returns if we are ready to seal, the real sealing (signing extra_data) is actually done in `on_seal_block()`.
|
||||||
@ -766,9 +766,14 @@ impl Engine<EthereumMachine> for Clique {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_signer(&self, signer: Box<dyn EngineSigner>) {
|
fn set_signer(&self, signer: Option<Box<dyn EngineSigner>>) {
|
||||||
trace!(target: "engine", "set_signer: {}", signer.address());
|
let mut current_signer = self.signer.write();
|
||||||
*self.signer.write() = Some(signer);
|
if let Some(signer) = signer.as_ref() {
|
||||||
|
trace!(target: "engine", "set_signer: {:?}", signer.address());
|
||||||
|
} else if let Some(signer) = &*current_signer {
|
||||||
|
trace!(target: "engine", "set_signer: cleared; previous signer: {:?})", signer.address());
|
||||||
|
}
|
||||||
|
*current_signer = signer;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_client(&self, client: Weak<dyn EngineClient>) {
|
fn register_client(&self, client: Weak<dyn EngineClient>) {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use block::ExecutedBlock;
|
use block::ExecutedBlock;
|
||||||
use engines::{Engine, Seal};
|
use engines::{Engine, Seal, SealingState};
|
||||||
use machine::Machine;
|
use machine::Machine;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
use types::header::{ExtendedHeader, Header};
|
use types::header::{ExtendedHeader, Header};
|
||||||
@ -63,8 +63,8 @@ impl<M: Machine> Engine<M> for InstantSeal<M> {
|
|||||||
&self.machine
|
&self.machine
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seals_internally(&self) -> Option<bool> {
|
fn sealing_state(&self) -> SealingState {
|
||||||
Some(true)
|
SealingState::Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_reseal_on_update(&self) -> bool {
|
fn should_reseal_on_update(&self) -> bool {
|
||||||
|
@ -89,6 +89,10 @@ pub enum EngineError {
|
|||||||
InsufficientProof(String),
|
InsufficientProof(String),
|
||||||
/// Failed system call.
|
/// Failed system call.
|
||||||
FailedSystemCall(String),
|
FailedSystemCall(String),
|
||||||
|
/// Failed to decode the result of a system call.
|
||||||
|
SystemCallResultDecoding(String),
|
||||||
|
/// The result of a system call is invalid.
|
||||||
|
SystemCallResultInvalid(String),
|
||||||
/// Malformed consensus message.
|
/// Malformed consensus message.
|
||||||
MalformedMessage(String),
|
MalformedMessage(String),
|
||||||
/// Requires client ref, but none registered.
|
/// Requires client ref, but none registered.
|
||||||
@ -153,6 +157,12 @@ impl fmt::Display for EngineError {
|
|||||||
BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob),
|
BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob),
|
||||||
InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg),
|
InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg),
|
||||||
FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg),
|
FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg),
|
||||||
|
SystemCallResultDecoding(ref msg) => {
|
||||||
|
format!("Failed to decode the result of a system call: {}", msg)
|
||||||
|
}
|
||||||
|
SystemCallResultInvalid(ref msg) => {
|
||||||
|
format!("The result of a system call is invalid: {}", msg)
|
||||||
|
}
|
||||||
MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg),
|
MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg),
|
||||||
RequiresClient => format!("Call requires client but none registered"),
|
RequiresClient => format!("Call requires client but none registered"),
|
||||||
RequiresSigner => format!("Call requires signer but none registered"),
|
RequiresSigner => format!("Call requires signer but none registered"),
|
||||||
@ -180,6 +190,17 @@ pub enum Seal {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of sealing the engine is currently able to perform.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum SealingState {
|
||||||
|
/// The engine is ready to seal a block.
|
||||||
|
Ready,
|
||||||
|
/// The engine can't seal at the moment, and no block should be prepared and queued.
|
||||||
|
NotReady,
|
||||||
|
/// The engine does not seal internally.
|
||||||
|
External,
|
||||||
|
}
|
||||||
|
|
||||||
/// A system-calling closure. Enacts calls on a block's state from the system address.
|
/// A system-calling closure. Enacts calls on a block's state from the system address.
|
||||||
pub type SystemCall<'a> = dyn FnMut(Address, Vec<u8>) -> Result<Vec<u8>, String> + 'a;
|
pub type SystemCall<'a> = dyn FnMut(Address, Vec<u8>) -> Result<Vec<u8>, String> + 'a;
|
||||||
|
|
||||||
@ -329,11 +350,9 @@ pub trait Engine<M: Machine>: Sync + Send {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// None means that it requires external input (e.g. PoW) to seal a block.
|
/// Returns the engine's current sealing state.
|
||||||
/// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator).
|
fn sealing_state(&self) -> SealingState {
|
||||||
/// Some(false) means that the node might seal internally but is not qualified now.
|
SealingState::External
|
||||||
fn seals_internally(&self) -> Option<bool> {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called in `miner.chain_new_blocks` if the engine wishes to `update_sealing`
|
/// Called in `miner.chain_new_blocks` if the engine wishes to `update_sealing`
|
||||||
@ -449,7 +468,7 @@ pub trait Engine<M: Machine>: Sync + Send {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Register a component which signs consensus messages.
|
/// Register a component which signs consensus messages.
|
||||||
fn set_signer(&self, _signer: Box<dyn EngineSigner>) {}
|
fn set_signer(&self, _signer: Option<Box<dyn EngineSigner>>) {}
|
||||||
|
|
||||||
/// Sign using the EngineSigner, to be used for consensus tx signing.
|
/// Sign using the EngineSigner, to be used for consensus tx signing.
|
||||||
fn sign(&self, _hash: H256) -> Result<Signature, M::Error> {
|
fn sign(&self, _hash: H256) -> Result<Signature, M::Error> {
|
||||||
@ -505,6 +524,24 @@ pub trait Engine<M: Machine>: Sync + Send {
|
|||||||
fn executive_author(&self, header: &Header) -> Result<Address, Error> {
|
fn executive_author(&self, header: &Header) -> Result<Address, Error> {
|
||||||
Ok(*header.author())
|
Ok(*header.author())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of transactions for a new block if we are the author.
|
||||||
|
///
|
||||||
|
/// This is called when the miner prepares a new block that this node will author and seal. It returns a list of
|
||||||
|
/// transactions that will be added to the block before any other transactions from the queue.
|
||||||
|
/// Added for AuRa needs.
|
||||||
|
fn generate_engine_transactions(
|
||||||
|
&self,
|
||||||
|
_block: &ExecutedBlock,
|
||||||
|
) -> Result<Vec<SignedTransaction>, Error> {
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides the block gas limit. Whenever this returns `Some` for a header, the next block's gas limit must be
|
||||||
|
/// exactly that value. used by AuRa engine.
|
||||||
|
fn gas_limit_override(&self, _header: &Header) -> Option<U256> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// t_nb 9.3 Check whether a given block is the best block based on the default total difficulty rule.
|
/// t_nb 9.3 Check whether a given block is the best block based on the default total difficulty rule.
|
||||||
@ -610,6 +647,11 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
|
|||||||
let schedule = self.schedule(best_block_number);
|
let schedule = self.schedule(best_block_number);
|
||||||
self.machine().decode_transaction(transaction, &schedule)
|
self.machine().decode_transaction(transaction, &schedule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The configured minimum gas limit. Used by AuRa Engine.
|
||||||
|
fn min_gas_limit(&self) -> U256 {
|
||||||
|
self.params().min_gas_limit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convenience wrappers for existing functions.
|
// convenience wrappers for existing functions.
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
|
|
||||||
//! A signer used by Engines which need to sign messages.
|
//! A signer used by Engines which need to sign messages.
|
||||||
|
|
||||||
use crypto::publickey::{self, Signature};
|
use crypto::publickey::{self, ecies, Error, Public, Signature};
|
||||||
use ethereum_types::{Address, H256};
|
use ethereum_types::{Address, H256};
|
||||||
|
//TODO dr
|
||||||
|
|
||||||
/// Everything that an Engine needs to sign messages.
|
/// Everything that an Engine needs to sign messages.
|
||||||
pub trait EngineSigner: Send + Sync {
|
pub trait EngineSigner: Send + Sync {
|
||||||
@ -26,6 +27,12 @@ pub trait EngineSigner: Send + Sync {
|
|||||||
|
|
||||||
/// Signing address
|
/// Signing address
|
||||||
fn address(&self) -> Address;
|
fn address(&self) -> Address;
|
||||||
|
|
||||||
|
/// Decrypt a message that was encrypted to this signer's key.
|
||||||
|
fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result<Vec<u8>, Error>;
|
||||||
|
|
||||||
|
/// The signer's public key, if available.
|
||||||
|
fn public(&self) -> Option<Public>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `EngineSigner` from given key pair.
|
/// Creates a new `EngineSigner` from given key pair.
|
||||||
@ -43,6 +50,14 @@ impl EngineSigner for Signer {
|
|||||||
fn address(&self) -> Address {
|
fn address(&self) -> Address {
|
||||||
self.0.address()
|
self.0.address()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
ecies::decrypt(self.0.secret(), auth_data, cipher).map_err(From::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public(&self) -> Option<Public> {
|
||||||
|
Some(*self.0.public())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -78,5 +93,18 @@ mod test_signer {
|
|||||||
fn address(&self) -> Address {
|
fn address(&self) -> Address {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
self.0
|
||||||
|
.decrypt(self.1, None, auth_data, cipher)
|
||||||
|
.map_err(|e| {
|
||||||
|
warn!("Unable to decrypt message: {:?}", e);
|
||||||
|
Error::InvalidMessage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public(&self) -> Option<Public> {
|
||||||
|
self.0.account_public(self.1, &self.2).ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,14 @@
|
|||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use ethereum_types::{Address, H256};
|
use ethereum_types::{Address, H256, U256};
|
||||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use types::{header::Header, BlockNumber};
|
use types::{header::Header, ids::BlockId, transaction, BlockNumber};
|
||||||
|
|
||||||
use client::EngineClient;
|
use client::{traits::TransactionRequest, EngineClient};
|
||||||
|
|
||||||
|
use error::Error as EthcoreError;
|
||||||
|
|
||||||
use super::{safe_contract::ValidatorSafeContract, SimpleList, SystemCall, ValidatorSet};
|
use super::{safe_contract::ValidatorSafeContract, SimpleList, SystemCall, ValidatorSet};
|
||||||
|
|
||||||
@ -35,34 +37,87 @@ pub struct ValidatorContract {
|
|||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
validators: ValidatorSafeContract,
|
validators: ValidatorSafeContract,
|
||||||
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
|
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
|
||||||
|
posdao_transition: Option<BlockNumber>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidatorContract {
|
impl ValidatorContract {
|
||||||
pub fn new(contract_address: Address) -> Self {
|
pub fn new(contract_address: Address, posdao_transition: Option<BlockNumber>) -> Self {
|
||||||
ValidatorContract {
|
ValidatorContract {
|
||||||
contract_address,
|
contract_address,
|
||||||
validators: ValidatorSafeContract::new(contract_address),
|
validators: ValidatorSafeContract::new(contract_address, posdao_transition),
|
||||||
client: RwLock::new(None),
|
client: RwLock::new(None),
|
||||||
|
posdao_transition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ValidatorContract {
|
fn transact(
|
||||||
fn transact(&self, data: Bytes) -> Result<(), String> {
|
&self,
|
||||||
|
data: Bytes,
|
||||||
|
gas_price: Option<U256>,
|
||||||
|
client: &dyn EngineClient,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let full_client = client.as_full_client().ok_or("No full client!")?;
|
||||||
|
let tx_request = TransactionRequest::call(self.contract_address, data).gas_price(gas_price);
|
||||||
|
match full_client.transact(tx_request) {
|
||||||
|
Ok(()) | Err(transaction::Error::AlreadyImported) => Ok(()),
|
||||||
|
Err(e) => Err(e.to_string())?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_report_malicious(
|
||||||
|
&self,
|
||||||
|
address: &Address,
|
||||||
|
block: BlockNumber,
|
||||||
|
proof: Bytes,
|
||||||
|
) -> Result<(), EthcoreError> {
|
||||||
let client = self
|
let client = self
|
||||||
.client
|
.client
|
||||||
.read()
|
.read()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(Weak::upgrade)
|
.and_then(Weak::upgrade)
|
||||||
.ok_or_else(|| "No client!")?;
|
.ok_or("No client!")?;
|
||||||
|
let latest = client
|
||||||
|
.block_header(BlockId::Latest)
|
||||||
|
.ok_or("No latest block!")?;
|
||||||
|
if !self.contains(&latest.parent_hash(), address) {
|
||||||
|
warn!(target: "engine", "Not reporting {} on block {}: Not a validator", address, block);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let data =
|
||||||
|
validator_report::functions::report_malicious::encode_input(*address, block, proof);
|
||||||
|
self.validators
|
||||||
|
.enqueue_report(*address, block, data.clone());
|
||||||
|
let gas_price = self.report_gas_price(latest.number());
|
||||||
|
self.transact(data, gas_price, &*client)?;
|
||||||
|
warn!(target: "engine", "Reported malicious validator {} at block {}", address, block);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
match client.as_full_client() {
|
fn do_report_benign(&self, address: &Address, block: BlockNumber) -> Result<(), EthcoreError> {
|
||||||
Some(c) => {
|
let client = self
|
||||||
c.transact_contract(self.contract_address, data)
|
.client
|
||||||
.map_err(|e| format!("Transaction import error: {}", e))?;
|
.read()
|
||||||
Ok(())
|
.as_ref()
|
||||||
}
|
.and_then(Weak::upgrade)
|
||||||
None => Err("No full client!".into()),
|
.ok_or("No client!")?;
|
||||||
|
let latest = client
|
||||||
|
.block_header(BlockId::Latest)
|
||||||
|
.ok_or("No latest block!")?;
|
||||||
|
let data = validator_report::functions::report_benign::encode_input(*address, block);
|
||||||
|
let gas_price = self.report_gas_price(latest.number());
|
||||||
|
self.transact(data, gas_price, &*client)?;
|
||||||
|
warn!(target: "engine", "Benign report for validator {} at block {}", address, block);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the gas price for report transactions.
|
||||||
|
///
|
||||||
|
/// After `posdaoTransition`, this is zero. Otherwise it is the default (`None`).
|
||||||
|
fn report_gas_price(&self, block: BlockNumber) -> Option<U256> {
|
||||||
|
if self.posdao_transition? <= block {
|
||||||
|
Some(0.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,6 +127,20 @@ impl ValidatorSet for ValidatorContract {
|
|||||||
self.validators.default_caller(id)
|
self.validators.default_caller(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_engine_transactions(
|
||||||
|
&self,
|
||||||
|
first: bool,
|
||||||
|
header: &Header,
|
||||||
|
call: &mut SystemCall,
|
||||||
|
) -> Result<Vec<(Address, Bytes)>, EthcoreError> {
|
||||||
|
self.validators
|
||||||
|
.generate_engine_transactions(first, header, call)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_close_block(&self, header: &Header, address: &Address) -> Result<(), EthcoreError> {
|
||||||
|
self.validators.on_close_block(header, address)
|
||||||
|
}
|
||||||
|
|
||||||
fn on_epoch_begin(
|
fn on_epoch_begin(
|
||||||
&self,
|
&self,
|
||||||
first: bool,
|
first: bool,
|
||||||
@ -127,19 +196,14 @@ impl ValidatorSet for ValidatorContract {
|
|||||||
block: BlockNumber,
|
block: BlockNumber,
|
||||||
proof: Bytes,
|
proof: Bytes,
|
||||||
) {
|
) {
|
||||||
let data =
|
if let Err(s) = self.do_report_malicious(address, block, proof) {
|
||||||
validator_report::functions::report_malicious::encode_input(*address, block, proof);
|
warn!(target: "engine", "Validator {} could not be reported ({}) on block {}", address, s, block);
|
||||||
match self.transact(data) {
|
|
||||||
Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address),
|
|
||||||
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) {
|
fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) {
|
||||||
let data = validator_report::functions::report_benign::encode_input(*address, block);
|
if let Err(s) = self.do_report_benign(address, block) {
|
||||||
match self.transact(data) {
|
warn!(target: "engine", "Validator {} could not be reported ({}) on block {}", address, s, block);
|
||||||
Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address),
|
|
||||||
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +219,8 @@ mod tests {
|
|||||||
use accounts::AccountProvider;
|
use accounts::AccountProvider;
|
||||||
use bytes::ToPretty;
|
use bytes::ToPretty;
|
||||||
use call_contract::CallContract;
|
use call_contract::CallContract;
|
||||||
use client::{BlockChainClient, BlockInfo, ChainInfo};
|
use client::{traits::TransactionRequest, BlockChainClient, BlockInfo, ChainInfo};
|
||||||
|
use ethabi::FunctionOutputDecoder;
|
||||||
use ethereum_types::{Address, H520};
|
use ethereum_types::{Address, H520};
|
||||||
use hash::keccak;
|
use hash::keccak;
|
||||||
use miner::{self, MinerService};
|
use miner::{self, MinerService};
|
||||||
@ -169,11 +234,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fetches_validators() {
|
fn fetches_validators() {
|
||||||
let client = generate_dummy_client_with_spec(Spec::new_validator_contract);
|
let client = generate_dummy_client_with_spec(Spec::new_validator_contract);
|
||||||
let vc = Arc::new(ValidatorContract::new(
|
let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
|
||||||
"0000000000000000000000000000000000000005"
|
let vc = Arc::new(ValidatorContract::new(addr, None));
|
||||||
.parse::<Address>()
|
|
||||||
.unwrap(),
|
|
||||||
));
|
|
||||||
vc.register_client(Arc::downgrade(&client) as _);
|
vc.register_client(Arc::downgrade(&client) as _);
|
||||||
let last_hash = client.best_block_header().hash();
|
let last_hash = client.best_block_header().hash();
|
||||||
assert!(vc.contains(
|
assert!(vc.contains(
|
||||||
@ -219,6 +281,8 @@ mod tests {
|
|||||||
assert!(client.engine().verify_block_external(&header).is_err());
|
assert!(client.engine().verify_block_external(&header).is_err());
|
||||||
client.engine().step();
|
client.engine().step();
|
||||||
assert_eq!(client.chain_info().best_block_number, 0);
|
assert_eq!(client.chain_info().best_block_number, 0);
|
||||||
|
// `reportBenign` when the designated proposer releases block from the future (bad clock).
|
||||||
|
assert!(client.engine().verify_block_basic(&header).is_err());
|
||||||
|
|
||||||
// Now create one that is more in future. That one should be rejected and validator should be reported.
|
// Now create one that is more in future. That one should be rejected and validator should be reported.
|
||||||
let mut header = Header::default();
|
let mut header = Header::default();
|
||||||
@ -232,7 +296,7 @@ mod tests {
|
|||||||
// Seal a block.
|
// Seal a block.
|
||||||
client.engine().step();
|
client.engine().step();
|
||||||
assert_eq!(client.chain_info().best_block_number, 1);
|
assert_eq!(client.chain_info().best_block_number, 1);
|
||||||
// Check if the unresponsive validator is `disliked`.
|
// Check if the unresponsive validator is `disliked`. "d8f2e0bf" accesses the field `disliked`..
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client
|
client
|
||||||
.call_contract(
|
.call_contract(
|
||||||
@ -254,10 +318,23 @@ mod tests {
|
|||||||
client.engine().step();
|
client.engine().step();
|
||||||
client.engine().step();
|
client.engine().step();
|
||||||
assert_eq!(client.chain_info().best_block_number, 2);
|
assert_eq!(client.chain_info().best_block_number, 2);
|
||||||
|
let (data, decoder) =
|
||||||
|
super::validator_report::functions::malice_reported_for_block::call(v1, 1);
|
||||||
|
let reported_enc = client
|
||||||
|
.call_contract(BlockId::Latest, validator_contract, data)
|
||||||
|
.expect("call failed");
|
||||||
|
assert_ne!(
|
||||||
|
Vec::<Address>::new(),
|
||||||
|
decoder.decode(&reported_enc).expect("decoding failed")
|
||||||
|
);
|
||||||
|
|
||||||
// Check if misbehaving validator was removed.
|
// Check if misbehaving validator was removed.
|
||||||
|
|
||||||
client
|
client
|
||||||
.transact_contract(Default::default(), Default::default())
|
.transact(TransactionRequest::call(
|
||||||
|
Default::default(),
|
||||||
|
Default::default(),
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
client.engine().step();
|
client.engine().step();
|
||||||
client.engine().step();
|
client.engine().step();
|
||||||
|
@ -33,31 +33,49 @@ use types::{header::Header, ids::BlockId, BlockNumber};
|
|||||||
|
|
||||||
use client::EngineClient;
|
use client::EngineClient;
|
||||||
|
|
||||||
|
use error::Error as EthcoreError;
|
||||||
|
|
||||||
pub use self::simple_list::SimpleList;
|
pub use self::simple_list::SimpleList;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use self::test::TestSet;
|
pub use self::test::TestSet;
|
||||||
use self::{contract::ValidatorContract, multi::Multi, safe_contract::ValidatorSafeContract};
|
use self::{contract::ValidatorContract, multi::Multi, safe_contract::ValidatorSafeContract};
|
||||||
use super::SystemCall;
|
use super::SystemCall;
|
||||||
|
|
||||||
/// Creates a validator set from spec.
|
/// Creates a validator set from the given spec and initializes a transition to POSDAO AuRa consensus.
|
||||||
pub fn new_validator_set(spec: ValidatorSpec) -> Box<dyn ValidatorSet> {
|
pub fn new_validator_set_posdao(
|
||||||
|
spec: ValidatorSpec,
|
||||||
|
posdao_transition: Option<BlockNumber>,
|
||||||
|
) -> Box<dyn ValidatorSet> {
|
||||||
match spec {
|
match spec {
|
||||||
ValidatorSpec::List(list) => {
|
ValidatorSpec::List(list) => {
|
||||||
Box::new(SimpleList::new(list.into_iter().map(Into::into).collect()))
|
Box::new(SimpleList::new(list.into_iter().map(Into::into).collect()))
|
||||||
}
|
}
|
||||||
ValidatorSpec::SafeContract(address) => {
|
ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(
|
||||||
Box::new(ValidatorSafeContract::new(address.into()))
|
address.into(),
|
||||||
|
posdao_transition,
|
||||||
|
)),
|
||||||
|
ValidatorSpec::Contract(address) => {
|
||||||
|
Box::new(ValidatorContract::new(address.into(), posdao_transition))
|
||||||
}
|
}
|
||||||
ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())),
|
|
||||||
ValidatorSpec::Multi(sequence) => Box::new(Multi::new(
|
ValidatorSpec::Multi(sequence) => Box::new(Multi::new(
|
||||||
sequence
|
sequence
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(block, set)| (block.into(), new_validator_set(set)))
|
.map(|(block, set)| {
|
||||||
|
(
|
||||||
|
block.into(),
|
||||||
|
new_validator_set_posdao(set, posdao_transition),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a validator set from the given spec.
|
||||||
|
pub fn new_validator_set(spec: ValidatorSpec) -> Box<dyn ValidatorSet> {
|
||||||
|
new_validator_set_posdao(spec, None)
|
||||||
|
}
|
||||||
|
|
||||||
/// A validator set.
|
/// A validator set.
|
||||||
pub trait ValidatorSet: Send + Sync + 'static {
|
pub trait ValidatorSet: Send + Sync + 'static {
|
||||||
/// Get the default "Call" helper, for use in general operation.
|
/// Get the default "Call" helper, for use in general operation.
|
||||||
@ -65,6 +83,21 @@ pub trait ValidatorSet: Send + Sync + 'static {
|
|||||||
// a strict dependency on state always being available.
|
// a strict dependency on state always being available.
|
||||||
fn default_caller(&self, block_id: BlockId) -> Box<Call>;
|
fn default_caller(&self, block_id: BlockId) -> Box<Call>;
|
||||||
|
|
||||||
|
/// Called for each new block this node is creating. If this block is
|
||||||
|
/// the first block of an epoch, this is called *after* `on_epoch_begin()`,
|
||||||
|
/// but with the same parameters.
|
||||||
|
///
|
||||||
|
/// Returns a list of contract calls to be pushed onto the new block.
|
||||||
|
fn generate_engine_transactions(
|
||||||
|
&self,
|
||||||
|
_first: bool,
|
||||||
|
_header: &Header,
|
||||||
|
_call: &mut SystemCall,
|
||||||
|
) -> Result<Vec<(Address, Bytes)>, EthcoreError>;
|
||||||
|
|
||||||
|
/// Called on the close of every block.
|
||||||
|
fn on_close_block(&self, _header: &Header, _address: &Address) -> Result<(), EthcoreError>;
|
||||||
|
|
||||||
/// Checks if a given address is a validator,
|
/// Checks if a given address is a validator,
|
||||||
/// using underlying, default call mechanism.
|
/// using underlying, default call mechanism.
|
||||||
fn contains(&self, parent: &H256, address: &Address) -> bool {
|
fn contains(&self, parent: &H256, address: &Address) -> bool {
|
||||||
|
@ -25,6 +25,7 @@ use types::{header::Header, ids::BlockId, BlockNumber};
|
|||||||
|
|
||||||
use super::{SystemCall, ValidatorSet};
|
use super::{SystemCall, ValidatorSet};
|
||||||
use client::EngineClient;
|
use client::EngineClient;
|
||||||
|
use error::Error as EthcoreError;
|
||||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||||
|
|
||||||
type BlockNumberLookup =
|
type BlockNumberLookup =
|
||||||
@ -47,6 +48,15 @@ impl Multi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_children<T, F>(&self, header: &Header, mut func: F) -> Result<T, EthcoreError>
|
||||||
|
where
|
||||||
|
F: FnMut(&dyn ValidatorSet, bool) -> Result<T, EthcoreError>,
|
||||||
|
{
|
||||||
|
let (set_block, set) = self.correct_set_by_number(header.number());
|
||||||
|
let first = set_block == header.number();
|
||||||
|
func(set, first)
|
||||||
|
}
|
||||||
|
|
||||||
fn correct_set(&self, id: BlockId) -> Option<&dyn ValidatorSet> {
|
fn correct_set(&self, id: BlockId) -> Option<&dyn ValidatorSet> {
|
||||||
match self.block_number.read()(id)
|
match self.block_number.read()(id)
|
||||||
.map(|parent_block| self.correct_set_by_number(parent_block))
|
.map(|parent_block| self.correct_set_by_number(parent_block))
|
||||||
@ -81,16 +91,31 @@ impl ValidatorSet for Multi {
|
|||||||
.unwrap_or_else(|| Box::new(|_, _| Err("No validator set for given ID.".into())))
|
.unwrap_or_else(|| Box::new(|_, _| Err("No validator set for given ID.".into())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_engine_transactions(
|
||||||
|
&self,
|
||||||
|
_first: bool,
|
||||||
|
header: &Header,
|
||||||
|
call: &mut SystemCall,
|
||||||
|
) -> Result<Vec<(Address, Bytes)>, EthcoreError> {
|
||||||
|
self.map_children(header, &mut |set: &dyn ValidatorSet, first| {
|
||||||
|
set.generate_engine_transactions(first, header, call)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn on_close_block(&self, header: &Header, address: &Address) -> Result<(), EthcoreError> {
|
||||||
|
self.map_children(header, &mut |set: &dyn ValidatorSet, _first| {
|
||||||
|
set.on_close_block(header, address)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn on_epoch_begin(
|
fn on_epoch_begin(
|
||||||
&self,
|
&self,
|
||||||
_first: bool,
|
_first: bool,
|
||||||
header: &Header,
|
header: &Header,
|
||||||
call: &mut SystemCall,
|
call: &mut SystemCall,
|
||||||
) -> Result<(), ::error::Error> {
|
) -> Result<(), EthcoreError> {
|
||||||
let (set_block, set) = self.correct_set_by_number(header.number());
|
self.map_children(header, &mut |set: &dyn ValidatorSet, first| {
|
||||||
let first = set_block == header.number();
|
set.on_epoch_begin(first, header, call)
|
||||||
|
})
|
||||||
set.on_epoch_begin(first, header, call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
|
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
|
||||||
@ -182,7 +207,10 @@ impl ValidatorSet for Multi {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use accounts::AccountProvider;
|
use accounts::AccountProvider;
|
||||||
use client::{traits::ForceUpdateSealing, BlockChainClient, BlockInfo, ChainInfo, ImportBlock};
|
use client::{
|
||||||
|
traits::{ForceUpdateSealing, TransactionRequest},
|
||||||
|
BlockChainClient, BlockInfo, ChainInfo, ImportBlock,
|
||||||
|
};
|
||||||
use crypto::publickey::Secret;
|
use crypto::publickey::Secret;
|
||||||
use engines::{validator_set::ValidatorSet, EpochChange};
|
use engines::{validator_set::ValidatorSet, EpochChange};
|
||||||
use ethereum_types::Address;
|
use ethereum_types::Address;
|
||||||
@ -190,7 +218,7 @@ mod tests {
|
|||||||
use miner::{self, MinerService};
|
use miner::{self, MinerService};
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data};
|
use test_helpers::generate_dummy_client_with_spec;
|
||||||
use types::{header::Header, ids::BlockId};
|
use types::{header::Header, ids::BlockId};
|
||||||
use verification::queue::kind::blocks::Unverified;
|
use verification::queue::kind::blocks::Unverified;
|
||||||
|
|
||||||
@ -216,7 +244,10 @@ mod tests {
|
|||||||
let signer = Box::new((tap.clone(), v1, "".into()));
|
let signer = Box::new((tap.clone(), v1, "".into()));
|
||||||
client.miner().set_author(miner::Author::Sealer(signer));
|
client.miner().set_author(miner::Author::Sealer(signer));
|
||||||
client
|
client
|
||||||
.transact_contract(Default::default(), Default::default())
|
.transact(TransactionRequest::call(
|
||||||
|
Default::default(),
|
||||||
|
Default::default(),
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
|
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
|
||||||
assert_eq!(client.chain_info().best_block_number, 0);
|
assert_eq!(client.chain_info().best_block_number, 0);
|
||||||
@ -227,7 +258,10 @@ mod tests {
|
|||||||
assert_eq!(client.chain_info().best_block_number, 1);
|
assert_eq!(client.chain_info().best_block_number, 1);
|
||||||
// This time v0 is wrong.
|
// This time v0 is wrong.
|
||||||
client
|
client
|
||||||
.transact_contract(Default::default(), Default::default())
|
.transact(TransactionRequest::call(
|
||||||
|
Default::default(),
|
||||||
|
Default::default(),
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
|
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
|
||||||
assert_eq!(client.chain_info().best_block_number, 1);
|
assert_eq!(client.chain_info().best_block_number, 1);
|
||||||
@ -237,14 +271,16 @@ mod tests {
|
|||||||
assert_eq!(client.chain_info().best_block_number, 2);
|
assert_eq!(client.chain_info().best_block_number, 2);
|
||||||
// v1 is still good.
|
// v1 is still good.
|
||||||
client
|
client
|
||||||
.transact_contract(Default::default(), Default::default())
|
.transact(TransactionRequest::call(
|
||||||
|
Default::default(),
|
||||||
|
Default::default(),
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
|
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
|
||||||
assert_eq!(client.chain_info().best_block_number, 3);
|
assert_eq!(client.chain_info().best_block_number, 3);
|
||||||
|
|
||||||
// Check syncing.
|
// Check syncing.
|
||||||
let sync_client =
|
let sync_client = generate_dummy_client_with_spec(Spec::new_validator_multi);
|
||||||
generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]);
|
|
||||||
sync_client
|
sync_client
|
||||||
.engine()
|
.engine()
|
||||||
.register_client(Arc::downgrade(&sync_client) as _);
|
.register_client(Arc::downgrade(&sync_client) as _);
|
||||||
|
@ -15,25 +15,39 @@
|
|||||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/// Validator set maintained in a contract, updated using `getValidators` method.
|
/// Validator set maintained in a contract, updated using `getValidators` method.
|
||||||
use std::sync::{Arc, Weak};
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use error::{Error as EthcoreError, ErrorKind as EthcoreErrorKind};
|
||||||
use ethabi::FunctionOutputDecoder;
|
use ethabi::FunctionOutputDecoder;
|
||||||
use ethereum_types::{Address, Bloom, H256, U256};
|
use ethereum_types::{Address, Bloom, H256, U256};
|
||||||
use hash::keccak;
|
use hash::keccak;
|
||||||
use kvdb::DBValue;
|
use kvdb::DBValue;
|
||||||
use memory_cache::MemoryLruCache;
|
use memory_cache::MemoryLruCache;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rlp::{Rlp, RlpStream};
|
use rlp::{Rlp, RlpStream};
|
||||||
use types::{header::Header, ids::BlockId, log_entry::LogEntry, receipt::TypedReceipt};
|
use types::{
|
||||||
|
header::Header, ids::BlockId, log_entry::LogEntry, receipt::TypedReceipt, transaction,
|
||||||
|
BlockNumber,
|
||||||
|
};
|
||||||
use unexpected::Mismatch;
|
use unexpected::Mismatch;
|
||||||
|
|
||||||
use super::{simple_list::SimpleList, SystemCall, ValidatorSet};
|
use super::{simple_list::SimpleList, SystemCall, ValidatorSet};
|
||||||
use client::EngineClient;
|
use client::{traits::TransactionRequest, BlockChainClient, EngineClient};
|
||||||
use machine::{AuxiliaryData, AuxiliaryRequest, Call, EthereumMachine};
|
use machine::{AuxiliaryData, AuxiliaryRequest, Call, EthereumMachine};
|
||||||
|
|
||||||
use_contract!(validator_set, "res/contracts/validator_set.json");
|
use_contract!(validator_set, "res/contracts/validator_set.json");
|
||||||
|
|
||||||
|
/// The maximum number of reports to keep queued.
|
||||||
|
const MAX_QUEUED_REPORTS: usize = 10;
|
||||||
|
/// The maximum number of malice reports to include when creating a new block.
|
||||||
|
const MAX_REPORTS_PER_BLOCK: usize = 10;
|
||||||
|
/// Don't re-send malice reports every block. Skip this many before retrying.
|
||||||
|
const REPORTS_SKIP_BLOCKS: u64 = 1;
|
||||||
|
|
||||||
const MEMOIZE_CAPACITY: usize = 500;
|
const MEMOIZE_CAPACITY: usize = 500;
|
||||||
|
|
||||||
// TODO: ethabi should be able to generate this.
|
// TODO: ethabi should be able to generate this.
|
||||||
@ -71,6 +85,12 @@ pub struct ValidatorSafeContract {
|
|||||||
contract_address: Address,
|
contract_address: Address,
|
||||||
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
|
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
|
||||||
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
|
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
|
||||||
|
report_queue: Mutex<ReportQueue>,
|
||||||
|
/// The block number where we resent the queued reports last time.
|
||||||
|
resent_reports_in_block: Mutex<BlockNumber>,
|
||||||
|
/// If set, this is the block number at which the consensus engine switches from AuRa to AuRa
|
||||||
|
/// with POSDAO modifications.
|
||||||
|
posdao_transition: Option<BlockNumber>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// first proof is just a state proof call of `getValidators` at header's state.
|
// first proof is just a state proof call of `getValidators` at header's state.
|
||||||
@ -203,14 +223,54 @@ fn prove_initial(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ValidatorSafeContract {
|
impl ValidatorSafeContract {
|
||||||
pub fn new(contract_address: Address) -> Self {
|
pub fn new(contract_address: Address, posdao_transition: Option<BlockNumber>) -> Self {
|
||||||
ValidatorSafeContract {
|
ValidatorSafeContract {
|
||||||
contract_address,
|
contract_address,
|
||||||
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
|
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
|
||||||
client: RwLock::new(None),
|
client: RwLock::new(None),
|
||||||
|
report_queue: Mutex::new(ReportQueue::default()),
|
||||||
|
resent_reports_in_block: Mutex::new(0),
|
||||||
|
posdao_transition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transact(&self, data: Bytes, nonce: U256) -> Result<(), EthcoreError> {
|
||||||
|
let client = self
|
||||||
|
.client
|
||||||
|
.read()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(Weak::upgrade)
|
||||||
|
.ok_or("No client!")?;
|
||||||
|
let full_client = client.as_full_client().ok_or("No full client!")?;
|
||||||
|
|
||||||
|
let tx_request = TransactionRequest::call(self.contract_address, data)
|
||||||
|
.gas_price(U256::zero())
|
||||||
|
.nonce(nonce);
|
||||||
|
match full_client.transact(tx_request) {
|
||||||
|
Ok(()) | Err(transaction::Error::AlreadyImported) => Ok(()),
|
||||||
|
Err(e) => Err(e)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts a malice report into the queue for later resending.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `addr` - The address of the misbehaving validator.
|
||||||
|
/// * `block` - The block number at which the misbehavior occurred.
|
||||||
|
/// * `data` - The call data for the `reportMalicious` contract call.
|
||||||
|
pub(crate) fn enqueue_report(&self, addr: Address, block: BlockNumber, data: Vec<u8>) {
|
||||||
|
// Skip the rest of the function unless there has been a transition to POSDAO AuRa.
|
||||||
|
if self
|
||||||
|
.posdao_transition
|
||||||
|
.map_or(true, |block_num| block < block_num)
|
||||||
|
{
|
||||||
|
trace!(target: "engine", "Skipping queueing a malicious behavior report");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.report_queue.lock().push(addr, block, data)
|
||||||
|
}
|
||||||
|
|
||||||
/// Queries the state and gets the set of validators.
|
/// Queries the state and gets the set of validators.
|
||||||
fn get_list(&self, caller: &Call) -> Option<SimpleList> {
|
fn get_list(&self, caller: &Call) -> Option<SimpleList> {
|
||||||
let contract_address = self.contract_address;
|
let contract_address = self.contract_address;
|
||||||
@ -314,6 +374,110 @@ impl ValidatorSet for ValidatorSafeContract {
|
|||||||
}) // generate no proofs in general
|
}) // generate no proofs in general
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_engine_transactions(
|
||||||
|
&self,
|
||||||
|
_first: bool,
|
||||||
|
header: &Header,
|
||||||
|
caller: &mut SystemCall,
|
||||||
|
) -> Result<Vec<(Address, Bytes)>, EthcoreError> {
|
||||||
|
// Skip the rest of the function unless there has been a transition to POSDAO AuRa.
|
||||||
|
if self
|
||||||
|
.posdao_transition
|
||||||
|
.map_or(true, |block_num| header.number() < block_num)
|
||||||
|
{
|
||||||
|
trace!(target: "engine", "Skipping a call to emitInitiateChange");
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
let mut transactions = Vec::new();
|
||||||
|
|
||||||
|
// Create the `InitiateChange` event if necessary.
|
||||||
|
let (data, decoder) = validator_set::functions::emit_initiate_change_callable::call();
|
||||||
|
let emit_initiate_change_callable = caller(self.contract_address, data)
|
||||||
|
.and_then(|x| {
|
||||||
|
decoder
|
||||||
|
.decode(&x)
|
||||||
|
.map_err(|x| format!("chain spec bug: could not decode: {:?}", x))
|
||||||
|
})
|
||||||
|
.map_err(::engines::EngineError::FailedSystemCall)?;
|
||||||
|
if !emit_initiate_change_callable {
|
||||||
|
trace!(target: "engine", "New block #{} issued ― no need to call emitInitiateChange()", header.number());
|
||||||
|
} else {
|
||||||
|
trace!(target: "engine", "New block issued #{} ― calling emitInitiateChange()", header.number());
|
||||||
|
let (data, _decoder) = validator_set::functions::emit_initiate_change::call();
|
||||||
|
transactions.push((self.contract_address, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = self
|
||||||
|
.client
|
||||||
|
.read()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(Weak::upgrade)
|
||||||
|
.ok_or("No client!")?;
|
||||||
|
let client = client.as_full_client().ok_or("No full client!")?;
|
||||||
|
|
||||||
|
// Retry all pending reports.
|
||||||
|
let mut report_queue = self.report_queue.lock();
|
||||||
|
report_queue.filter(client, header.author(), self.contract_address);
|
||||||
|
for (_address, _block, data) in report_queue.iter().take(MAX_REPORTS_PER_BLOCK) {
|
||||||
|
transactions.push((self.contract_address, data.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(transactions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_close_block(&self, header: &Header, our_address: &Address) -> Result<(), EthcoreError> {
|
||||||
|
// Skip the rest of the function unless there has been a transition to POSDAO AuRa.
|
||||||
|
if self
|
||||||
|
.posdao_transition
|
||||||
|
.map_or(true, |block_num| header.number() < block_num)
|
||||||
|
{
|
||||||
|
trace!(target: "engine", "Skipping resending of queued malicious behavior reports");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = self
|
||||||
|
.client
|
||||||
|
.read()
|
||||||
|
.as_ref()
|
||||||
|
.and_then(Weak::upgrade)
|
||||||
|
.ok_or("No client!")?;
|
||||||
|
let client = client.as_full_client().ok_or("No full client!")?;
|
||||||
|
|
||||||
|
let mut report_queue = self.report_queue.lock();
|
||||||
|
report_queue.filter(client, our_address, self.contract_address);
|
||||||
|
report_queue.truncate();
|
||||||
|
|
||||||
|
let mut resent_reports_in_block = self.resent_reports_in_block.lock();
|
||||||
|
|
||||||
|
// Skip at least one block after sending malicious reports last time.
|
||||||
|
if header.number() > *resent_reports_in_block + REPORTS_SKIP_BLOCKS {
|
||||||
|
*resent_reports_in_block = header.number();
|
||||||
|
let mut nonce = client.latest_nonce(our_address);
|
||||||
|
for (address, block, data) in report_queue.iter() {
|
||||||
|
debug!(target: "engine", "Retrying to report validator {} for misbehavior on block {} with nonce {}.",
|
||||||
|
address, block, nonce);
|
||||||
|
while match self.transact(data.clone(), nonce) {
|
||||||
|
Ok(()) => false,
|
||||||
|
Err(EthcoreError(
|
||||||
|
EthcoreErrorKind::Transaction(transaction::Error::Old),
|
||||||
|
_,
|
||||||
|
)) => true,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(target: "engine", "Cannot report validator {} for misbehavior on block {}: {}",
|
||||||
|
address, block, err);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
warn!(target: "engine", "Nonce {} already used. Incrementing.", nonce);
|
||||||
|
nonce += U256::from(1);
|
||||||
|
}
|
||||||
|
nonce += U256::from(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn on_epoch_begin(
|
fn on_epoch_begin(
|
||||||
&self,
|
&self,
|
||||||
_first: bool,
|
_first: bool,
|
||||||
@ -473,6 +637,68 @@ impl ValidatorSet for ValidatorSafeContract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A queue containing pending reports of malicious validators.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct ReportQueue(VecDeque<(Address, BlockNumber, Vec<u8>)>);
|
||||||
|
|
||||||
|
impl ReportQueue {
|
||||||
|
/// Pushes a report to the end of the queue.
|
||||||
|
fn push(&mut self, addr: Address, block: BlockNumber, data: Vec<u8>) {
|
||||||
|
self.0.push_back((addr, block, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filters reports of validators that have already been reported or are banned.
|
||||||
|
fn filter(
|
||||||
|
&mut self,
|
||||||
|
client: &dyn BlockChainClient,
|
||||||
|
our_address: &Address,
|
||||||
|
contract_address: Address,
|
||||||
|
) {
|
||||||
|
self.0.retain(|&(malicious_validator_address, block, ref _data)| {
|
||||||
|
trace!(
|
||||||
|
target: "engine",
|
||||||
|
"Checking if report of malicious validator {} at block {} should be removed from cache",
|
||||||
|
malicious_validator_address,
|
||||||
|
block
|
||||||
|
);
|
||||||
|
// Check if the validator should be reported.
|
||||||
|
let (data, decoder) = validator_set::functions::should_validator_report::call(
|
||||||
|
*our_address, malicious_validator_address, block
|
||||||
|
);
|
||||||
|
match client.call_contract(BlockId::Latest, contract_address, data)
|
||||||
|
.and_then(|result| decoder.decode(&result[..]).map_err(|e| e.to_string()))
|
||||||
|
{
|
||||||
|
Ok(false) => {
|
||||||
|
trace!(target: "engine", "Successfully removed report from report cache");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Ok(true) => true,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(target: "engine", "Failed to query report status {:?}, dropping pending report.", err);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over all transactions in the queue.
|
||||||
|
fn iter(&self) -> impl Iterator<Item = &(Address, BlockNumber, Vec<u8>)> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes reports from the queue if it contains more than `MAX_QUEUED_REPORTS` entries.
|
||||||
|
fn truncate(&mut self) {
|
||||||
|
if self.0.len() > MAX_QUEUED_REPORTS {
|
||||||
|
warn!(
|
||||||
|
target: "engine",
|
||||||
|
"Removing {} reports from report cache, even though it has not been finalized",
|
||||||
|
self.0.len() - MAX_QUEUED_REPORTS
|
||||||
|
);
|
||||||
|
self.0.truncate(MAX_QUEUED_REPORTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{super::ValidatorSet, ValidatorSafeContract, EVENT_NAME_HASH};
|
use super::{super::ValidatorSet, ValidatorSafeContract, EVENT_NAME_HASH};
|
||||||
@ -488,7 +714,7 @@ mod tests {
|
|||||||
use rustc_hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data};
|
use test_helpers::generate_dummy_client_with_spec;
|
||||||
use types::{
|
use types::{
|
||||||
ids::BlockId,
|
ids::BlockId,
|
||||||
transaction::{Action, Transaction, TypedTransaction},
|
transaction::{Action, Transaction, TypedTransaction},
|
||||||
@ -498,11 +724,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fetches_validators() {
|
fn fetches_validators() {
|
||||||
let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract);
|
let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract);
|
||||||
let vc = Arc::new(ValidatorSafeContract::new(
|
let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
|
||||||
"0000000000000000000000000000000000000005"
|
let vc = Arc::new(ValidatorSafeContract::new(addr, None));
|
||||||
.parse::<Address>()
|
|
||||||
.unwrap(),
|
|
||||||
));
|
|
||||||
vc.register_client(Arc::downgrade(&client) as _);
|
vc.register_client(Arc::downgrade(&client) as _);
|
||||||
let last_hash = client.best_block_header().hash();
|
let last_hash = client.best_block_header().hash();
|
||||||
assert!(vc.contains(
|
assert!(vc.contains(
|
||||||
@ -600,8 +823,7 @@ mod tests {
|
|||||||
assert_eq!(client.chain_info().best_block_number, 3);
|
assert_eq!(client.chain_info().best_block_number, 3);
|
||||||
|
|
||||||
// Check syncing.
|
// Check syncing.
|
||||||
let sync_client =
|
let sync_client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract);
|
||||||
generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]);
|
|
||||||
sync_client
|
sync_client
|
||||||
.engine()
|
.engine()
|
||||||
.register_client(Arc::downgrade(&sync_client) as _);
|
.register_client(Arc::downgrade(&sync_client) as _);
|
||||||
|
@ -18,7 +18,9 @@ use ethereum_types::{Address, H256};
|
|||||||
/// Preconfigured validator list.
|
/// Preconfigured validator list.
|
||||||
use parity_util_mem::MallocSizeOf;
|
use parity_util_mem::MallocSizeOf;
|
||||||
|
|
||||||
use super::ValidatorSet;
|
use super::{SystemCall, ValidatorSet};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use error::Error as EthcoreError;
|
||||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||||
use types::{header::Header, BlockNumber};
|
use types::{header::Header, BlockNumber};
|
||||||
|
|
||||||
@ -63,6 +65,19 @@ impl ValidatorSet for SimpleList {
|
|||||||
Box::new(|_, _| Err("Simple list doesn't require calls.".into()))
|
Box::new(|_, _| Err("Simple list doesn't require calls.".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_engine_transactions(
|
||||||
|
&self,
|
||||||
|
_first: bool,
|
||||||
|
_header: &Header,
|
||||||
|
_call: &mut SystemCall,
|
||||||
|
) -> Result<Vec<(Address, Bytes)>, EthcoreError> {
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_close_block(&self, _header: &Header, _address: &Address) -> Result<(), EthcoreError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_epoch_end(&self, first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
|
fn is_epoch_end(&self, first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
|
||||||
match first {
|
match first {
|
||||||
true => Some(Vec::new()), // allow transition to fixed list, and instantly
|
true => Some(Vec::new()), // allow transition to fixed list, and instantly
|
||||||
|
@ -26,11 +26,12 @@ use ethereum_types::{Address, H256};
|
|||||||
use parity_util_mem::MallocSizeOf;
|
use parity_util_mem::MallocSizeOf;
|
||||||
use types::{header::Header, BlockNumber};
|
use types::{header::Header, BlockNumber};
|
||||||
|
|
||||||
use super::{SimpleList, ValidatorSet};
|
use super::{SimpleList, SystemCall, ValidatorSet};
|
||||||
|
use error::Error as EthcoreError;
|
||||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||||
|
|
||||||
/// Set used for testing with a single validator.
|
/// Set used for testing with a single validator.
|
||||||
#[derive(MallocSizeOf)]
|
#[derive(Clone, MallocSizeOf)]
|
||||||
pub struct TestSet {
|
pub struct TestSet {
|
||||||
validator: SimpleList,
|
validator: SimpleList,
|
||||||
last_malicious: Arc<AtomicUsize>,
|
last_malicious: Arc<AtomicUsize>,
|
||||||
@ -54,6 +55,21 @@ impl TestSet {
|
|||||||
last_benign,
|
last_benign,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_validators(validators: Vec<Address>) -> Self {
|
||||||
|
let mut ts = TestSet::new(Default::default(), Default::default());
|
||||||
|
ts.validator = SimpleList::new(validators);
|
||||||
|
ts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_malicious(&self) -> usize {
|
||||||
|
self.last_malicious.load(AtomicOrdering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn last_benign(&self) -> usize {
|
||||||
|
self.last_benign.load(AtomicOrdering::SeqCst)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidatorSet for TestSet {
|
impl ValidatorSet for TestSet {
|
||||||
@ -61,6 +77,19 @@ impl ValidatorSet for TestSet {
|
|||||||
Box::new(|_, _| Err("Test set doesn't require calls.".into()))
|
Box::new(|_, _| Err("Test set doesn't require calls.".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_engine_transactions(
|
||||||
|
&self,
|
||||||
|
_first: bool,
|
||||||
|
_header: &Header,
|
||||||
|
_call: &mut SystemCall,
|
||||||
|
) -> Result<Vec<(Address, Bytes)>, EthcoreError> {
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_close_block(&self, _header: &Header, _address: &Address) -> Result<(), EthcoreError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
|
fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option<Vec<u8>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
extern crate ansi_term;
|
extern crate ansi_term;
|
||||||
extern crate common_types as types;
|
extern crate common_types as types;
|
||||||
extern crate crossbeam_utils;
|
extern crate crossbeam_utils;
|
||||||
|
extern crate derive_more;
|
||||||
extern crate ethabi;
|
extern crate ethabi;
|
||||||
extern crate ethash;
|
extern crate ethash;
|
||||||
extern crate ethcore_blockchain as blockchain;
|
extern crate ethcore_blockchain as blockchain;
|
||||||
|
@ -60,7 +60,7 @@ use client::{
|
|||||||
BlockChain, BlockId, BlockProducer, ChainInfo, ClientIoMessage, Nonce, SealedBlockImporter,
|
BlockChain, BlockId, BlockProducer, ChainInfo, ClientIoMessage, Nonce, SealedBlockImporter,
|
||||||
TransactionId, TransactionInfo,
|
TransactionId, TransactionInfo,
|
||||||
};
|
};
|
||||||
use engines::{EngineSigner, EthEngine, Seal};
|
use engines::{EngineSigner, EthEngine, Seal, SealingState};
|
||||||
use error::{Error, ErrorKind};
|
use error::{Error, ErrorKind};
|
||||||
use executed::ExecutionError;
|
use executed::ExecutionError;
|
||||||
use executive::contract_address;
|
use executive::contract_address;
|
||||||
@ -288,7 +288,8 @@ impl Miner {
|
|||||||
Miner {
|
Miner {
|
||||||
sealing: Mutex::new(SealingWork {
|
sealing: Mutex::new(SealingWork {
|
||||||
queue: UsingQueue::new(options.work_queue_size),
|
queue: UsingQueue::new(options.work_queue_size),
|
||||||
enabled: options.force_sealing || spec.engine.seals_internally().is_some(),
|
enabled: options.force_sealing
|
||||||
|
|| spec.engine.sealing_state() != SealingState::External,
|
||||||
next_allowed_reseal: Instant::now(),
|
next_allowed_reseal: Instant::now(),
|
||||||
next_mandatory_reseal: Instant::now() + options.reseal_max_period,
|
next_mandatory_reseal: Instant::now() + options.reseal_max_period,
|
||||||
last_request: None,
|
last_request: None,
|
||||||
@ -319,6 +320,17 @@ impl Miner {
|
|||||||
///
|
///
|
||||||
/// NOTE This should be only used for tests.
|
/// NOTE This should be only used for tests.
|
||||||
pub fn new_for_tests(spec: &Spec, accounts: Option<HashSet<Address>>) -> Miner {
|
pub fn new_for_tests(spec: &Spec, accounts: Option<HashSet<Address>>) -> Miner {
|
||||||
|
Miner::new_for_tests_force_sealing(spec, accounts, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new instance of miner with given spec and accounts.
|
||||||
|
///
|
||||||
|
/// NOTE This should be only used for tests.
|
||||||
|
pub fn new_for_tests_force_sealing(
|
||||||
|
spec: &Spec,
|
||||||
|
accounts: Option<HashSet<Address>>,
|
||||||
|
force_sealing: bool,
|
||||||
|
) -> Miner {
|
||||||
let minimal_gas_price = 0.into();
|
let minimal_gas_price = 0.into();
|
||||||
Miner::new(
|
Miner::new(
|
||||||
MinerOptions {
|
MinerOptions {
|
||||||
@ -329,6 +341,7 @@ impl Miner {
|
|||||||
no_early_reject: false,
|
no_early_reject: false,
|
||||||
},
|
},
|
||||||
reseal_min_period: Duration::from_secs(0),
|
reseal_min_period: Duration::from_secs(0),
|
||||||
|
force_sealing,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
GasPricer::new_fixed(minimal_gas_price),
|
GasPricer::new_fixed(minimal_gas_price),
|
||||||
@ -419,8 +432,8 @@ impl Miner {
|
|||||||
trace_time!("prepare_block");
|
trace_time!("prepare_block");
|
||||||
let chain_info = chain.chain_info();
|
let chain_info = chain.chain_info();
|
||||||
|
|
||||||
// Open block
|
// Some engines add transactions to the block for their own purposes, e.g. AuthorityRound RANDAO.
|
||||||
let (mut open_block, original_work_hash) = {
|
let (mut open_block, original_work_hash, engine_txs) = {
|
||||||
let mut sealing = self.sealing.lock();
|
let mut sealing = self.sealing.lock();
|
||||||
let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash());
|
let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash());
|
||||||
let best_hash = chain_info.best_block_hash;
|
let best_hash = chain_info.best_block_hash;
|
||||||
@ -431,41 +444,50 @@ impl Miner {
|
|||||||
// if at least one was pushed successfully, close and enqueue new ClosedBlock;
|
// if at least one was pushed successfully, close and enqueue new ClosedBlock;
|
||||||
// otherwise, leave everything alone.
|
// otherwise, leave everything alone.
|
||||||
// otherwise, author a fresh block.
|
// otherwise, author a fresh block.
|
||||||
let mut open_block = match sealing
|
match sealing
|
||||||
.queue
|
.queue
|
||||||
.get_pending_if(|b| b.header.parent_hash() == &best_hash)
|
.get_pending_if(|b| b.header.parent_hash() == &best_hash)
|
||||||
{
|
{
|
||||||
Some(old_block) => {
|
Some(old_block) => {
|
||||||
trace!(target: "miner", "prepare_block: Already have previous work; updating and returning");
|
trace!(target: "miner", "prepare_block: Already have previous work; updating and returning");
|
||||||
// add transactions to old_block
|
// add transactions to old_block
|
||||||
chain.reopen_block(old_block)
|
(chain.reopen_block(old_block), last_work_hash, Vec::new())
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// block not found - create it.
|
// block not found - create it.
|
||||||
trace!(target: "miner", "prepare_block: No existing work - making new block");
|
trace!(target: "miner", "prepare_block: No existing work - making new block");
|
||||||
let params = self.params.read().clone();
|
let params = self.params.read().clone();
|
||||||
|
|
||||||
match chain.prepare_open_block(
|
let block = match chain.prepare_open_block(
|
||||||
params.author,
|
params.author,
|
||||||
params.gas_range_target,
|
params.gas_range_target,
|
||||||
params.extra_data,
|
params.extra_data,
|
||||||
) {
|
) {
|
||||||
Ok(block) => block,
|
Ok(block) => block,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in chain specificiations or on-chain consensus smart contracts.", err);
|
warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in \
|
||||||
|
chain specification or on-chain consensus smart contracts.", err);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Before adding from the queue to the new block, give the engine a chance to add transactions.
|
||||||
|
match self.engine.generate_engine_transactions(&block) {
|
||||||
|
Ok(transactions) => (block, last_work_hash, transactions),
|
||||||
|
Err(err) => {
|
||||||
|
error!(target: "miner", "Failed to prepare engine transactions for new block: {:?}. \
|
||||||
|
This is likely an error in chain specification or on-chain consensus smart \
|
||||||
|
contracts.", err);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if self.options.infinite_pending_block {
|
|
||||||
open_block.remove_gas_limit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(open_block, last_work_hash)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if self.options.infinite_pending_block {
|
||||||
|
open_block.remove_gas_limit();
|
||||||
|
}
|
||||||
|
|
||||||
let mut invalid_transactions = HashSet::new();
|
let mut invalid_transactions = HashSet::new();
|
||||||
let mut not_allowed_transactions = HashSet::new();
|
let mut not_allowed_transactions = HashSet::new();
|
||||||
let mut senders_to_penalize = HashSet::new();
|
let mut senders_to_penalize = HashSet::new();
|
||||||
@ -501,13 +523,13 @@ impl Miner {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let pending: Vec<Arc<_>> = self.transaction_queue.pending(
|
let queue_txs: Vec<Arc<_>> = self.transaction_queue.pending(
|
||||||
client.clone(),
|
client.clone(),
|
||||||
pool::PendingSettings {
|
pool::PendingSettings {
|
||||||
block_number: chain_info.best_block_number,
|
block_number: chain_info.best_block_number,
|
||||||
current_timestamp: chain_info.best_block_timestamp,
|
current_timestamp: chain_info.best_block_timestamp,
|
||||||
nonce_cap,
|
nonce_cap,
|
||||||
max_len: max_transactions,
|
max_len: max_transactions.saturating_sub(engine_txs.len()),
|
||||||
ordering: miner::PendingOrdering::Priority,
|
ordering: miner::PendingOrdering::Priority,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -517,12 +539,14 @@ impl Miner {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let block_start = Instant::now();
|
let block_start = Instant::now();
|
||||||
debug!(target: "miner", "Attempting to push {} transactions.", pending.len());
|
debug!(target: "miner", "Attempting to push {} transactions.", engine_txs.len() + queue_txs.len());
|
||||||
|
|
||||||
for tx in pending {
|
for transaction in engine_txs
|
||||||
|
.into_iter()
|
||||||
|
.chain(queue_txs.into_iter().map(|tx| tx.signed().clone()))
|
||||||
|
{
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let transaction = tx.signed().clone();
|
|
||||||
let hash = transaction.hash();
|
let hash = transaction.hash();
|
||||||
let sender = transaction.sender();
|
let sender = transaction.sender();
|
||||||
|
|
||||||
@ -670,7 +694,7 @@ impl Miner {
|
|||||||
// keep sealing enabled if any of the conditions is met
|
// keep sealing enabled if any of the conditions is met
|
||||||
let sealing_enabled = self.forced_sealing()
|
let sealing_enabled = self.forced_sealing()
|
||||||
|| self.transaction_queue.has_local_pending_transactions()
|
|| self.transaction_queue.has_local_pending_transactions()
|
||||||
|| self.engine.seals_internally() == Some(true)
|
|| self.engine.sealing_state() == SealingState::Ready
|
||||||
|| had_requests;
|
|| had_requests;
|
||||||
|
|
||||||
let should_disable_sealing = !sealing_enabled;
|
let should_disable_sealing = !sealing_enabled;
|
||||||
@ -679,7 +703,7 @@ impl Miner {
|
|||||||
should_disable_sealing,
|
should_disable_sealing,
|
||||||
self.forced_sealing(),
|
self.forced_sealing(),
|
||||||
self.transaction_queue.has_local_pending_transactions(),
|
self.transaction_queue.has_local_pending_transactions(),
|
||||||
self.engine.seals_internally(),
|
self.engine.sealing_state(),
|
||||||
had_requests,
|
had_requests,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -862,6 +886,11 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if self.engine.sealing_state() != SealingState::External {
|
||||||
|
trace!(target: "miner", "prepare_pending_block: engine not sealing externally; not preparing");
|
||||||
|
return BlockPreparationStatus::NotPrepared;
|
||||||
|
}
|
||||||
|
|
||||||
let preparation_status = if prepare_new {
|
let preparation_status = if prepare_new {
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// | NOTE Code below requires sealing locks. |
|
// | NOTE Code below requires sealing locks. |
|
||||||
@ -896,7 +925,9 @@ impl Miner {
|
|||||||
fn prepare_and_update_sealing<C: miner::BlockChainClient>(&self, chain: &C) {
|
fn prepare_and_update_sealing<C: miner::BlockChainClient>(&self, chain: &C) {
|
||||||
// Make sure to do it after transaction is imported and lock is dropped.
|
// Make sure to do it after transaction is imported and lock is dropped.
|
||||||
// We need to create pending block and enable sealing.
|
// We need to create pending block and enable sealing.
|
||||||
if self.engine.seals_internally().unwrap_or(false)
|
let sealing_state = self.engine.sealing_state();
|
||||||
|
|
||||||
|
if sealing_state == SealingState::Ready
|
||||||
|| self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared
|
|| self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared
|
||||||
{
|
{
|
||||||
// If new block has not been prepared (means we already had one)
|
// If new block has not been prepared (means we already had one)
|
||||||
@ -924,21 +955,38 @@ impl miner::MinerService for Miner {
|
|||||||
self.params.write().extra_data = extra_data;
|
self.params.write().extra_data = extra_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_author(&self, author: Author) {
|
fn set_author<T: Into<Option<Author>>>(&self, author: T) {
|
||||||
self.params.write().author = author.address();
|
let author_opt = author.into();
|
||||||
|
self.params.write().author = author_opt.as_ref().map(Author::address).unwrap_or_default();
|
||||||
|
|
||||||
if let Author::Sealer(signer) = author {
|
match author_opt {
|
||||||
if self.engine.seals_internally().is_some() {
|
Some(Author::Sealer(signer)) => {
|
||||||
// Enable sealing
|
if self.engine.sealing_state() != SealingState::External {
|
||||||
self.sealing.lock().enabled = true;
|
// Enable sealing
|
||||||
// --------------------------------------------------------------------------
|
self.sealing.lock().enabled = true;
|
||||||
// | NOTE Code below may require author and sealing locks |
|
// --------------------------------------------------------------------------
|
||||||
// | (some `Engine`s call `EngineClient.update_sealing()`) |
|
// | NOTE Code below may require author and sealing locks |
|
||||||
// | Make sure to release the locks before calling that method. |
|
// | (some `Engine`s call `EngineClient.update_sealing()`) |
|
||||||
// --------------------------------------------------------------------------
|
// | Make sure to release the locks before calling that method. |
|
||||||
self.engine.set_signer(signer);
|
// --------------------------------------------------------------------------
|
||||||
} else {
|
self.engine.set_signer(Some(signer));
|
||||||
warn!("Setting an EngineSigner while Engine does not require one.");
|
} else {
|
||||||
|
warn!("Setting an EngineSigner while Engine does not require one.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Author::External(_address)) => (),
|
||||||
|
None => {
|
||||||
|
// Clear the author.
|
||||||
|
if self.engine.sealing_state() != SealingState::External {
|
||||||
|
// Disable sealing.
|
||||||
|
self.sealing.lock().enabled = false;
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// | NOTE Code below may require author and sealing locks |
|
||||||
|
// | (some `Engine`s call `EngineClient.update_sealing()`) |
|
||||||
|
// | Make sure to release the locks before calling that method. |
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
self.engine.set_signer(None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1257,6 +1305,11 @@ impl miner::MinerService for Miner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sealing_state = self.engine.sealing_state();
|
||||||
|
if sealing_state == SealingState::NotReady {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// | NOTE Code below requires sealing locks. |
|
// | NOTE Code below requires sealing locks. |
|
||||||
// | Make sure to release the locks before calling that method. |
|
// | Make sure to release the locks before calling that method. |
|
||||||
@ -1277,20 +1330,23 @@ impl miner::MinerService for Miner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.engine.seals_internally() {
|
match sealing_state {
|
||||||
Some(true) => {
|
SealingState::Ready => {
|
||||||
trace!(target: "miner", "update_sealing: engine indicates internal sealing");
|
trace!(target: "miner", "update_sealing: engine indicates internal sealing");
|
||||||
if self.seal_and_import_block_internally(chain, block) {
|
if self.seal_and_import_block_internally(chain, block) {
|
||||||
trace!(target: "miner", "update_sealing: imported internally sealed block");
|
trace!(target: "miner", "update_sealing: imported internally sealed block");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(false) => {
|
SealingState::NotReady => {
|
||||||
trace!(target: "miner", "update_sealing: engine is not keen to seal internally right now");
|
unreachable!("We returned right after sealing_state was computed. qed.")
|
||||||
// anyway, save the block for later use
|
|
||||||
self.sealing.lock().queue.set_pending(block);
|
|
||||||
}
|
}
|
||||||
None => {
|
// => {
|
||||||
|
// trace!(target: "miner", "update_sealing: engine is not keen to seal internally right now");
|
||||||
|
// // anyway, save the block for later use
|
||||||
|
// self.sealing.lock().queue.set_pending(block);
|
||||||
|
// }
|
||||||
|
SealingState::External => {
|
||||||
trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work");
|
trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work");
|
||||||
self.prepare_work(block, original_work_hash);
|
self.prepare_work(block, original_work_hash);
|
||||||
}
|
}
|
||||||
@ -1305,7 +1361,7 @@ impl miner::MinerService for Miner {
|
|||||||
where
|
where
|
||||||
C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync,
|
C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync,
|
||||||
{
|
{
|
||||||
if self.engine.seals_internally().is_some() {
|
if self.engine.sealing_state() != SealingState::External {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ pub trait MinerService: Send + Sync {
|
|||||||
/// Set info necessary to sign consensus messages and block authoring.
|
/// Set info necessary to sign consensus messages and block authoring.
|
||||||
///
|
///
|
||||||
/// On chains where sealing is done externally (e.g. PoW) we provide only reward beneficiary.
|
/// On chains where sealing is done externally (e.g. PoW) we provide only reward beneficiary.
|
||||||
fn set_author(&self, author: Author);
|
fn set_author<T: Into<Option<Author>>>(&self, author: T);
|
||||||
|
|
||||||
// Transaction Pool
|
// Transaction Pool
|
||||||
|
|
||||||
|
@ -1012,7 +1012,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sends_async_messages() {
|
fn sends_async_messages() {
|
||||||
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
||||||
let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices);
|
let client =
|
||||||
|
generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices, false);
|
||||||
let service = IoService::<ClientIoMessage>::start("Test").unwrap();
|
let service = IoService::<ClientIoMessage>::start("Test").unwrap();
|
||||||
let spec = Spec::new_test();
|
let spec = Spec::new_test();
|
||||||
|
|
||||||
|
@ -46,8 +46,13 @@ fn restored_is_equivalent() {
|
|||||||
const TX_PER: usize = 5;
|
const TX_PER: usize = 5;
|
||||||
|
|
||||||
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
||||||
let client =
|
let client = generate_dummy_client_with_spec_and_data(
|
||||||
generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, TX_PER, &gas_prices);
|
Spec::new_null,
|
||||||
|
NUM_BLOCKS,
|
||||||
|
TX_PER,
|
||||||
|
&gas_prices,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
let tempdir = TempDir::new("").unwrap();
|
let tempdir = TempDir::new("").unwrap();
|
||||||
let client_db = tempdir.path().join("client_db");
|
let client_db = tempdir.path().join("client_db");
|
||||||
@ -112,7 +117,8 @@ fn restored_is_equivalent() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn guards_delete_folders() {
|
fn guards_delete_folders() {
|
||||||
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
||||||
let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices);
|
let client =
|
||||||
|
generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices, false);
|
||||||
|
|
||||||
let spec = Spec::new_null();
|
let spec = Spec::new_null();
|
||||||
let tempdir = TempDir::new("").unwrap();
|
let tempdir = TempDir::new("").unwrap();
|
||||||
@ -178,7 +184,7 @@ fn keep_ancient_blocks() {
|
|||||||
let spec_f = Spec::new_null;
|
let spec_f = Spec::new_null;
|
||||||
let spec = spec_f();
|
let spec = spec_f();
|
||||||
let client =
|
let client =
|
||||||
generate_dummy_client_with_spec_and_data(spec_f, NUM_BLOCKS as u32, 5, &gas_prices);
|
generate_dummy_client_with_spec_and_data(spec_f, NUM_BLOCKS as u32, 5, &gas_prices, false);
|
||||||
|
|
||||||
let bc = client.chain();
|
let bc = client.chain();
|
||||||
|
|
||||||
@ -298,7 +304,7 @@ fn recover_aborted_recovery() {
|
|||||||
const NUM_BLOCKS: u32 = 400;
|
const NUM_BLOCKS: u32 = 400;
|
||||||
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
||||||
let client =
|
let client =
|
||||||
generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, 5, &gas_prices);
|
generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, 5, &gas_prices, false);
|
||||||
|
|
||||||
let spec = Spec::new_null();
|
let spec = Spec::new_null();
|
||||||
let tempdir = TempDir::new("oe_snapshot").unwrap();
|
let tempdir = TempDir::new("oe_snapshot").unwrap();
|
||||||
|
@ -1063,6 +1063,12 @@ impl Spec {
|
|||||||
load_bundled!("test/constructor")
|
load_bundled!("test/constructor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new Spec with Autority Round randomness contract
|
||||||
|
#[cfg(any(test, feature = "test-helpers"))]
|
||||||
|
pub fn new_test_round_randomness_contract() -> Spec {
|
||||||
|
load_bundled!("test/authority_round_randomness_contract")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec with AuthorityRound consensus which does internal sealing (not
|
/// Create a new Spec with AuthorityRound consensus which does internal sealing (not
|
||||||
/// requiring work).
|
/// requiring work).
|
||||||
/// Accounts with secrets keccak("0") and keccak("1") are the validators.
|
/// Accounts with secrets keccak("0") and keccak("1") are the validators.
|
||||||
|
@ -112,7 +112,7 @@ pub fn create_test_block_with_data(
|
|||||||
|
|
||||||
/// Generates dummy client (not test client) with corresponding amount of blocks
|
/// Generates dummy client (not test client) with corresponding amount of blocks
|
||||||
pub fn generate_dummy_client(block_number: u32) -> Arc<Client> {
|
pub fn generate_dummy_client(block_number: u32) -> Arc<Client> {
|
||||||
generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &[])
|
generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &[], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates dummy client (not test client) with corresponding amount of blocks and txs per every block
|
/// Generates dummy client (not test client) with corresponding amount of blocks and txs per every block
|
||||||
@ -126,6 +126,7 @@ pub fn generate_dummy_client_with_data(
|
|||||||
block_number,
|
block_number,
|
||||||
txs_per_block,
|
txs_per_block,
|
||||||
tx_gas_prices,
|
tx_gas_prices,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ pub fn generate_dummy_client_with_spec<F>(test_spec: F) -> Arc<Client>
|
|||||||
where
|
where
|
||||||
F: Fn() -> Spec,
|
F: Fn() -> Spec,
|
||||||
{
|
{
|
||||||
generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[])
|
generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates dummy client (not test client) with corresponding amount of blocks, txs per block and spec
|
/// Generates dummy client (not test client) with corresponding amount of blocks, txs per block and spec
|
||||||
@ -143,6 +144,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(
|
|||||||
block_number: u32,
|
block_number: u32,
|
||||||
txs_per_block: usize,
|
txs_per_block: usize,
|
||||||
tx_gas_prices: &[U256],
|
tx_gas_prices: &[U256],
|
||||||
|
force_sealing: bool,
|
||||||
) -> Arc<Client>
|
) -> Arc<Client>
|
||||||
where
|
where
|
||||||
F: Fn() -> Spec,
|
F: Fn() -> Spec,
|
||||||
@ -150,11 +152,13 @@ where
|
|||||||
let test_spec = test_spec();
|
let test_spec = test_spec();
|
||||||
let client_db = new_db();
|
let client_db = new_db();
|
||||||
|
|
||||||
|
let miner = Miner::new_for_tests_force_sealing(&test_spec, None, force_sealing);
|
||||||
|
|
||||||
let client = Client::new(
|
let client = Client::new(
|
||||||
ClientConfig::default(),
|
ClientConfig::default(),
|
||||||
&test_spec,
|
&test_spec,
|
||||||
client_db,
|
client_db,
|
||||||
Arc::new(Miner::new_for_tests(&test_spec, None)),
|
Arc::new(miner),
|
||||||
IoChannel::disconnected(),
|
IoChannel::disconnected(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -51,7 +51,7 @@ impl CreateResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Description of a _call_ action, either a `CALL` operation or a message transction.
|
/// Description of a _call_ action, either a `CALL` operation or a message transaction.
|
||||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
/// The sending account.
|
/// The sending account.
|
||||||
@ -102,7 +102,7 @@ impl Call {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Description of a _create_ action, either a `CREATE` operation or a create transction.
|
/// Description of a _create_ action, either a `CREATE` operation or a create transaction.
|
||||||
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
||||||
pub struct Create {
|
pub struct Create {
|
||||||
/// The address of the creator.
|
/// The address of the creator.
|
||||||
|
@ -35,6 +35,10 @@ use_contract!(
|
|||||||
"res/contracts/tx_acl_deprecated.json"
|
"res/contracts/tx_acl_deprecated.json"
|
||||||
);
|
);
|
||||||
use_contract!(transact_acl, "res/contracts/tx_acl.json");
|
use_contract!(transact_acl, "res/contracts/tx_acl.json");
|
||||||
|
use_contract!(
|
||||||
|
transact_acl_gas_price,
|
||||||
|
"res/contracts/tx_acl_gas_price.json"
|
||||||
|
);
|
||||||
|
|
||||||
const MAX_CACHE_SIZE: usize = 4096;
|
const MAX_CACHE_SIZE: usize = 4096;
|
||||||
|
|
||||||
@ -99,6 +103,7 @@ impl TransactionFilter {
|
|||||||
|
|
||||||
let sender = transaction.sender();
|
let sender = transaction.sender();
|
||||||
let value = transaction.tx().value;
|
let value = transaction.tx().value;
|
||||||
|
let gas_price = transaction.tx().gas_price;
|
||||||
let key = (*parent_hash, sender);
|
let key = (*parent_hash, sender);
|
||||||
|
|
||||||
if let Some(permissions) = permission_cache.get_mut(&key) {
|
if let Some(permissions) = permission_cache.get_mut(&key) {
|
||||||
@ -138,6 +143,24 @@ impl TransactionFilter {
|
|||||||
(tx_permissions::NONE, true)
|
(tx_permissions::NONE, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
3 => {
|
||||||
|
trace!(target: "tx_filter", "Using filter with gas price and data");
|
||||||
|
let (data, decoder) =
|
||||||
|
transact_acl_gas_price::functions::allowed_tx_types::call(
|
||||||
|
sender,
|
||||||
|
to,
|
||||||
|
value,
|
||||||
|
gas_price,
|
||||||
|
transaction.tx().data.clone(),
|
||||||
|
);
|
||||||
|
client.call_contract(BlockId::Hash(*parent_hash), contract_address, data)
|
||||||
|
.and_then(|value| decoder.decode(&value).map_err(|e| e.to_string()))
|
||||||
|
.map(|(p, f)| (p.low_u32(), f))
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
error!(target: "tx_filter", "Error calling tx permissions contract: {:?}", e);
|
||||||
|
(tx_permissions::NONE, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!(target: "tx_filter", "Unknown version of tx permissions contract is used");
|
error!(target: "tx_filter", "Unknown version of tx permissions contract is used");
|
||||||
(tx_permissions::NONE, true)
|
(tx_permissions::NONE, true)
|
||||||
@ -161,10 +184,7 @@ impl TransactionFilter {
|
|||||||
if filter_only_sender {
|
if filter_only_sender {
|
||||||
permission_cache.insert((*parent_hash, sender), permissions);
|
permission_cache.insert((*parent_hash, sender), permissions);
|
||||||
}
|
}
|
||||||
trace!(target: "tx_filter",
|
trace!(target: "tx_filter", "Given transaction data: sender: {:?} to: {:?} value: {}, gas_price: {}. Permissions required: {:X}, got: {:X}", sender, to, value, gas_price, tx_type, permissions);
|
||||||
"Given transaction data: sender: {:?} to: {:?} value: {}. Permissions required: {:X}, got: {:X}",
|
|
||||||
sender, to, value, tx_type, permissions
|
|
||||||
);
|
|
||||||
permissions & tx_type != 0
|
permissions & tx_type != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,7 +205,7 @@ mod test {
|
|||||||
|
|
||||||
/// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
|
/// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
|
||||||
#[test]
|
#[test]
|
||||||
fn transaction_filter() {
|
fn transaction_filter_ver_2() {
|
||||||
let spec_data = include_str!("../res/chainspec/test/contract_ver_2_genesis.json");
|
let spec_data = include_str!("../res/chainspec/test/contract_ver_2_genesis.json");
|
||||||
|
|
||||||
let db = test_helpers::new_db();
|
let db = test_helpers::new_db();
|
||||||
@ -418,6 +438,74 @@ mod test {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contract code: res/tx_permission_tests/contract_ver_3.sol
|
||||||
|
#[test]
|
||||||
|
fn transaction_filter_ver_3() {
|
||||||
|
let spec_data = include_str!("../res/chainspec/test/contract_ver_3_genesis.json");
|
||||||
|
|
||||||
|
let db = test_helpers::new_db();
|
||||||
|
let tempdir = TempDir::new("").unwrap();
|
||||||
|
let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
let client = Client::new(
|
||||||
|
ClientConfig::default(),
|
||||||
|
&spec,
|
||||||
|
db,
|
||||||
|
Arc::new(Miner::new_for_tests(&spec, None)),
|
||||||
|
IoChannel::disconnected(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let key1 = KeyPair::from_secret(
|
||||||
|
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// The only difference to version 2 is that the contract now knows the transaction's gas price and data.
|
||||||
|
// So we only test those: The contract allows only transactions with either nonzero gas price or short data.
|
||||||
|
|
||||||
|
let filter = TransactionFilter::from_params(spec.params()).unwrap();
|
||||||
|
let mut tx = TypedTransaction::Legacy(Transaction::default());
|
||||||
|
tx.tx_mut().action =
|
||||||
|
Action::Call(Address::from_str("0000000000000000000000000000000000000042").unwrap());
|
||||||
|
tx.tx_mut().data = b"01234567".to_vec();
|
||||||
|
tx.tx_mut().gas_price = 0.into();
|
||||||
|
|
||||||
|
let genesis = client.block_hash(BlockId::Latest).unwrap();
|
||||||
|
let block_number = 1;
|
||||||
|
|
||||||
|
// Data too long and gas price zero. This transaction is not allowed.
|
||||||
|
assert!(!filter.transaction_allowed(
|
||||||
|
&genesis,
|
||||||
|
block_number,
|
||||||
|
&tx.clone().sign(key1.secret(), None),
|
||||||
|
&*client
|
||||||
|
));
|
||||||
|
|
||||||
|
// But if we either set a nonzero gas price or short data or both, it is allowed.
|
||||||
|
tx.tx_mut().gas_price = 1.into();
|
||||||
|
assert!(filter.transaction_allowed(
|
||||||
|
&genesis,
|
||||||
|
block_number,
|
||||||
|
&tx.clone().sign(key1.secret(), None),
|
||||||
|
&*client
|
||||||
|
));
|
||||||
|
tx.tx_mut().data = b"01".to_vec();
|
||||||
|
assert!(filter.transaction_allowed(
|
||||||
|
&genesis,
|
||||||
|
block_number,
|
||||||
|
&tx.clone().sign(key1.secret(), None),
|
||||||
|
&*client
|
||||||
|
));
|
||||||
|
tx.tx_mut().gas_price = 0.into();
|
||||||
|
assert!(filter.transaction_allowed(
|
||||||
|
&genesis,
|
||||||
|
block_number,
|
||||||
|
&tx.clone().sign(key1.secret(), None),
|
||||||
|
&*client
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/// Contract code: https://gist.github.com/arkpar/38a87cb50165b7e683585eec71acb05a
|
/// Contract code: https://gist.github.com/arkpar/38a87cb50165b7e683585eec71acb05a
|
||||||
#[test]
|
#[test]
|
||||||
fn transaction_filter_deprecated() {
|
fn transaction_filter_deprecated() {
|
||||||
|
@ -83,7 +83,16 @@ pub fn verify_block_basic(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// t_nb 4.6 call engine.gas_limit_override (Used only by Aura) TODO added in new version
|
// t_nb 4.6 call engine.gas_limit_override (Used only by Aura)
|
||||||
|
if let Some(gas_limit) = engine.gas_limit_override(&block.header) {
|
||||||
|
if *block.header.gas_limit() != gas_limit {
|
||||||
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
|
||||||
|
min: Some(gas_limit),
|
||||||
|
max: Some(gas_limit),
|
||||||
|
found: *block.header.gas_limit(),
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// t_nb 4.7 for every transaction call engine.verify_transaction_basic
|
// t_nb 4.7 for every transaction call engine.verify_transaction_basic
|
||||||
for t in &block.transactions {
|
for t in &block.transactions {
|
||||||
@ -358,25 +367,26 @@ pub fn verify_header_params(
|
|||||||
found: *header.gas_used(),
|
found: *header.gas_used(),
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
let min_gas_limit = engine.params().min_gas_limit;
|
if engine.gas_limit_override(header).is_none() {
|
||||||
if header.gas_limit() < &min_gas_limit {
|
let min_gas_limit = engine.min_gas_limit();
|
||||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
|
if header.gas_limit() < &min_gas_limit {
|
||||||
min: Some(min_gas_limit),
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
|
||||||
max: None,
|
min: Some(min_gas_limit),
|
||||||
found: *header.gas_limit(),
|
max: None,
|
||||||
})));
|
found: *header.gas_limit(),
|
||||||
}
|
})));
|
||||||
if let Some(limit) = engine.maximum_gas_limit() {
|
}
|
||||||
if header.gas_limit() > &limit {
|
if let Some(limit) = engine.maximum_gas_limit() {
|
||||||
return Err(From::from(::error::BlockError::InvalidGasLimit(
|
if header.gas_limit() > &limit {
|
||||||
OutOfBounds {
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
|
||||||
min: None,
|
min: None,
|
||||||
max: Some(limit),
|
max: Some(limit),
|
||||||
found: *header.gas_limit(),
|
found: *header.gas_limit(),
|
||||||
},
|
})));
|
||||||
)));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let maximum_extra_data_size = engine.maximum_extra_data_size();
|
let maximum_extra_data_size = engine.maximum_extra_data_size();
|
||||||
if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size {
|
if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size {
|
||||||
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds {
|
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds {
|
||||||
@ -435,8 +445,6 @@ fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Re
|
|||||||
"Parent hash should already have been verified; qed"
|
"Parent hash should already have been verified; qed"
|
||||||
);
|
);
|
||||||
|
|
||||||
let gas_limit_divisor = engine.params().gas_limit_bound_divisor;
|
|
||||||
|
|
||||||
if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) {
|
if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) {
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
let min = CheckedSystemTime::checked_add(
|
let min = CheckedSystemTime::checked_add(
|
||||||
@ -468,15 +476,18 @@ fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Re
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let parent_gas_limit = *parent.gas_limit();
|
if engine.gas_limit_override(header).is_none() {
|
||||||
let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor;
|
let gas_limit_divisor = engine.params().gas_limit_bound_divisor;
|
||||||
let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor;
|
let parent_gas_limit = *parent.gas_limit();
|
||||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor;
|
||||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
|
let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor;
|
||||||
min: Some(min_gas),
|
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||||
max: Some(max_gas),
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
|
||||||
found: *header.gas_limit(),
|
min: Some(min_gas),
|
||||||
})));
|
max: Some(max_gas),
|
||||||
|
found: *header.gas_limit(),
|
||||||
|
})));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -740,7 +751,7 @@ mod tests {
|
|||||||
// that's an invalid transaction list rlp
|
// that's an invalid transaction list rlp
|
||||||
let invalid_transactions = vec![vec![0u8]];
|
let invalid_transactions = vec![vec![0u8]];
|
||||||
header.set_transactions_root(ordered_trie_root(&invalid_transactions));
|
header.set_transactions_root(ordered_trie_root(&invalid_transactions));
|
||||||
header.set_gas_limit(engine.params().min_gas_limit);
|
header.set_gas_limit(engine.min_gas_limit());
|
||||||
rlp.append(&header);
|
rlp.append(&header);
|
||||||
rlp.append_list::<Vec<u8>, _>(&invalid_transactions);
|
rlp.append_list::<Vec<u8>, _>(&invalid_transactions);
|
||||||
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
|
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
|
||||||
@ -759,7 +770,7 @@ mod tests {
|
|||||||
let spec = Spec::new_test();
|
let spec = Spec::new_test();
|
||||||
let engine = &*spec.engine;
|
let engine = &*spec.engine;
|
||||||
|
|
||||||
let min_gas_limit = engine.params().min_gas_limit;
|
let min_gas_limit = engine.min_gas_limit();
|
||||||
good.set_gas_limit(min_gas_limit);
|
good.set_gas_limit(min_gas_limit);
|
||||||
good.set_timestamp(40);
|
good.set_timestamp(40);
|
||||||
good.set_number(10);
|
good.set_number(10);
|
||||||
|
@ -131,7 +131,7 @@ impl fmt::Display for Error {
|
|||||||
InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(),
|
InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(),
|
||||||
InvalidSignature(ref err) => format!("Transaction has invalid signature: {}.", err),
|
InvalidSignature(ref err) => format!("Transaction has invalid signature: {}.", err),
|
||||||
NotAllowed => {
|
NotAllowed => {
|
||||||
"Sender does not have permissions to execute this type of transction".into()
|
"Sender does not have permissions to execute this type of transaction".into()
|
||||||
}
|
}
|
||||||
TooBig => "Transaction too big".into(),
|
TooBig => "Transaction too big".into(),
|
||||||
InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err),
|
InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err),
|
||||||
|
@ -14,10 +14,33 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Authority params deserialization.
|
//! Authority Round parameter deserialization.
|
||||||
|
//!
|
||||||
|
//! Here is an example of input parameters where the step duration is constant at 5 seconds, the set
|
||||||
|
//! of validators is decided by the contract at address `0x10..01` starting from block 0, and where
|
||||||
|
//! the address of the contract that computes block rewards is set to `0x20..02` for blocks 0
|
||||||
|
//! through 41 and to `0x30.03` for all blocks starting from block 42.
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! "params": {
|
||||||
|
//! "stepDuration": "5",
|
||||||
|
//! "validators": {
|
||||||
|
//! "multi": {
|
||||||
|
//! "0": {
|
||||||
|
//! "contract": "0x1000000000000000000000000000000000000001"
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! },
|
||||||
|
//! "blockRewardContractTransitions": {
|
||||||
|
//! "0": "0x2000000000000000000000000000000000000002",
|
||||||
|
//! "42": "0x3000000000000000000000000000000000000003"
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use super::{BlockReward, ValidatorSet};
|
use super::{BlockReward, StepDuration, ValidatorSet};
|
||||||
use crate::{bytes::Bytes, hash::Address, uint::Uint};
|
use crate::{bytes::Bytes, hash::Address, uint::Uint};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Authority params deserialization.
|
/// Authority params deserialization.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
@ -25,7 +48,7 @@ use crate::{bytes::Bytes, hash::Address, uint::Uint};
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AuthorityRoundParams {
|
pub struct AuthorityRoundParams {
|
||||||
/// Block duration, in seconds.
|
/// Block duration, in seconds.
|
||||||
pub step_duration: Uint,
|
pub step_duration: StepDuration,
|
||||||
/// Valid authorities
|
/// Valid authorities
|
||||||
pub validators: ValidatorSet,
|
pub validators: ValidatorSet,
|
||||||
/// Starting step. Determined automatically if not specified.
|
/// Starting step. Determined automatically if not specified.
|
||||||
@ -39,11 +62,24 @@ pub struct AuthorityRoundParams {
|
|||||||
pub immediate_transitions: Option<bool>,
|
pub immediate_transitions: Option<bool>,
|
||||||
/// Reward per block in wei.
|
/// Reward per block in wei.
|
||||||
pub block_reward: Option<BlockReward>,
|
pub block_reward: Option<BlockReward>,
|
||||||
/// Block at which the block reward contract should start being used.
|
/// Block at which the block reward contract should start being used. This option allows one to
|
||||||
|
/// add a single block reward contract transition and is compatible with the multiple address
|
||||||
|
/// option `block_reward_contract_transitions` below.
|
||||||
pub block_reward_contract_transition: Option<Uint>,
|
pub block_reward_contract_transition: Option<Uint>,
|
||||||
/// Block reward contract address (setting the block reward contract
|
/// Block reward contract address which overrides the `block_reward` setting. This option allows
|
||||||
/// overrides the static block reward definition).
|
/// one to add a single block reward contract address and is compatible with the multiple
|
||||||
|
/// address option `block_reward_contract_transitions` below.
|
||||||
pub block_reward_contract_address: Option<Address>,
|
pub block_reward_contract_address: Option<Address>,
|
||||||
|
/// Block reward contract addresses with their associated starting block numbers.
|
||||||
|
///
|
||||||
|
/// Setting the block reward contract overrides `block_reward`. If the single block reward
|
||||||
|
/// contract address is also present then it is added into the map at the block number stored in
|
||||||
|
/// `block_reward_contract_transition` or 0 if that block number is not provided. Therefore both
|
||||||
|
/// a single block reward contract transition and a map of reward contract transitions can be
|
||||||
|
/// used simulataneously in the same configuration. In such a case the code requires that the
|
||||||
|
/// block number of the single transition is strictly less than any of the block numbers in the
|
||||||
|
/// map.
|
||||||
|
pub block_reward_contract_transitions: Option<BTreeMap<Uint, Address>>,
|
||||||
/// Block reward code. This overrides the block reward contract address.
|
/// Block reward code. This overrides the block reward contract address.
|
||||||
pub block_reward_contract_code: Option<Bytes>,
|
pub block_reward_contract_code: Option<Bytes>,
|
||||||
/// Block at which maximum uncle count should be considered.
|
/// Block at which maximum uncle count should be considered.
|
||||||
@ -56,13 +92,23 @@ pub struct AuthorityRoundParams {
|
|||||||
pub maximum_empty_steps: Option<Uint>,
|
pub maximum_empty_steps: Option<Uint>,
|
||||||
/// Strict validation of empty steps transition block.
|
/// Strict validation of empty steps transition block.
|
||||||
pub strict_empty_steps_transition: Option<Uint>,
|
pub strict_empty_steps_transition: Option<Uint>,
|
||||||
|
/// First block for which a 2/3 quorum (instead of 1/2) is required.
|
||||||
|
pub two_thirds_majority_transition: Option<Uint>,
|
||||||
|
/// The random number contract's address, or a map of contract transitions.
|
||||||
|
pub randomness_contract_address: Option<BTreeMap<Uint, Address>>,
|
||||||
|
/// The addresses of contracts that determine the block gas limit starting from the block number
|
||||||
|
/// associated with each of those contracts.
|
||||||
|
pub block_gas_limit_contract_transitions: Option<BTreeMap<Uint, Address>>,
|
||||||
|
/// The block number at which the consensus engine switches from AuRa to AuRa with POSDAO
|
||||||
|
/// modifications.
|
||||||
|
pub posdao_transition: Option<Uint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authority engine deserialization.
|
/// Authority engine deserialization.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct AuthorityRound {
|
pub struct AuthorityRound {
|
||||||
/// Ethash params.
|
/// Authority Round parameters.
|
||||||
pub params: AuthorityRoundParams,
|
pub params: AuthorityRoundParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +119,10 @@ mod tests {
|
|||||||
use super::BlockReward;
|
use super::BlockReward;
|
||||||
use crate::{
|
use crate::{
|
||||||
hash::Address,
|
hash::Address,
|
||||||
spec::{authority_round::AuthorityRound, validator_set::ValidatorSet},
|
spec::{
|
||||||
|
authority_round::AuthorityRound, step_duration::StepDuration,
|
||||||
|
validator_set::ValidatorSet,
|
||||||
|
},
|
||||||
uint::Uint,
|
uint::Uint,
|
||||||
};
|
};
|
||||||
use ethereum_types::{H160, U256};
|
use ethereum_types::{H160, U256};
|
||||||
@ -92,12 +141,23 @@ mod tests {
|
|||||||
"validateStepTransition": 150,
|
"validateStepTransition": 150,
|
||||||
"blockReward": 5000000,
|
"blockReward": 5000000,
|
||||||
"maximumUncleCountTransition": 10000000,
|
"maximumUncleCountTransition": 10000000,
|
||||||
"maximumUncleCount": 5
|
"maximumUncleCount": 5,
|
||||||
|
"randomnessContractAddress": {
|
||||||
|
"10": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"20": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||||
|
},
|
||||||
|
"blockGasLimitContractTransitions": {
|
||||||
|
"10": "0x1000000000000000000000000000000000000001",
|
||||||
|
"20": "0x2000000000000000000000000000000000000002"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
let deserialized: AuthorityRound = serde_json::from_str(s).unwrap();
|
let deserialized: AuthorityRound = serde_json::from_str(s).unwrap();
|
||||||
assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02)));
|
assert_eq!(
|
||||||
|
deserialized.params.step_duration,
|
||||||
|
StepDuration::Single(Uint(U256::from(2)))
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
deserialized.params.validators,
|
deserialized.params.validators,
|
||||||
ValidatorSet::List(vec![Address(
|
ValidatorSet::List(vec![Address(
|
||||||
@ -115,9 +175,34 @@ mod tests {
|
|||||||
Some(Uint(5.into()))
|
Some(Uint(5.into()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
deserialized.params.block_reward,
|
deserialized.params.randomness_contract_address.unwrap(),
|
||||||
Some(BlockReward::Single(Uint(5000000.into())))
|
vec![
|
||||||
)
|
(
|
||||||
|
Uint(10.into()),
|
||||||
|
Address(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap())
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Uint(20.into()),
|
||||||
|
Address(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap())
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect()
|
||||||
|
);
|
||||||
|
let expected_bglc = [
|
||||||
|
(
|
||||||
|
Uint(10.into()),
|
||||||
|
Address(H160::from_str("1000000000000000000000000000000000000001").unwrap()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Uint(20.into()),
|
||||||
|
Address(H160::from_str("2000000000000000000000000000000000000002").unwrap()),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
assert_eq!(
|
||||||
|
deserialized.params.block_gas_limit_contract_transitions,
|
||||||
|
Some(expected_bglc.to_vec().into_iter().collect())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -136,7 +221,10 @@ mod tests {
|
|||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
let deserialized: AuthorityRound = serde_json::from_str(s).unwrap();
|
let deserialized: AuthorityRound = serde_json::from_str(s).unwrap();
|
||||||
assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02)));
|
assert_eq!(
|
||||||
|
deserialized.params.step_duration,
|
||||||
|
StepDuration::Single(Uint(U256::from(0x02)))
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
deserialized.params.validators,
|
deserialized.params.validators,
|
||||||
ValidatorSet::Contract(Address(
|
ValidatorSet::Contract(Address(
|
||||||
|
@ -30,6 +30,7 @@ pub mod params;
|
|||||||
pub mod seal;
|
pub mod seal;
|
||||||
pub mod spec;
|
pub mod spec;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
pub mod step_duration;
|
||||||
pub mod validator_set;
|
pub mod validator_set;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@ -47,5 +48,6 @@ pub use self::{
|
|||||||
seal::{AuthorityRoundSeal, Ethereum, Seal, TendermintSeal},
|
seal::{AuthorityRoundSeal, Ethereum, Seal, TendermintSeal},
|
||||||
spec::{ForkSpec, Spec},
|
spec::{ForkSpec, Spec},
|
||||||
state::State,
|
state::State,
|
||||||
|
step_duration::StepDuration,
|
||||||
validator_set::ValidatorSet,
|
validator_set::ValidatorSet,
|
||||||
};
|
};
|
||||||
|
36
crates/ethjson/src/spec/step_duration.rs
Normal file
36
crates/ethjson/src/spec/step_duration.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Step duration configuration parameter
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::uint::Uint;
|
||||||
|
|
||||||
|
/// Step duration can be specified either as a `Uint` (in seconds), in which case it will be
|
||||||
|
/// constant, or as a list of pairs consisting of a timestamp of type `Uint` and a duration, in
|
||||||
|
/// which case the duration of a step will be determined by a mapping arising from that list.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum StepDuration {
|
||||||
|
/// Duration of all steps.
|
||||||
|
Single(Uint),
|
||||||
|
/// Step duration transitions: a mapping of timestamp to step durations.
|
||||||
|
Transitions(BTreeMap<Uint, Uint>),
|
||||||
|
}
|
@ -17,7 +17,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use accounts::AccountProvider;
|
use accounts::AccountProvider;
|
||||||
use crypto::publickey::{self, Address};
|
use crypto::publickey::{self, Address, Error, Public};
|
||||||
use ethkey::Password;
|
use ethkey::Password;
|
||||||
|
|
||||||
/// An implementation of EngineSigner using internal account management.
|
/// An implementation of EngineSigner using internal account management.
|
||||||
@ -49,7 +49,22 @@ impl ethcore::engines::EngineSigner for EngineSigner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
self.accounts
|
||||||
|
.decrypt(self.address, None, auth_data, cipher)
|
||||||
|
.map_err(|e| {
|
||||||
|
warn!("Unable to decrypt message: {:?}", e);
|
||||||
|
Error::InvalidMessage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn address(&self) -> Address {
|
fn address(&self) -> Address {
|
||||||
self.address
|
self.address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn public(&self) -> Option<Public> {
|
||||||
|
self.accounts
|
||||||
|
.account_public(self.address, &self.password)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,6 +157,11 @@ where
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_engine_signer(&self) -> Result<bool> {
|
||||||
|
self.miner.set_author(None);
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
fn add_reserved_peer(&self, peer: String) -> Result<bool> {
|
fn add_reserved_peer(&self, peer: String) -> Result<bool> {
|
||||||
match self.net.add_reserved_peer(peer) {
|
match self.net.add_reserved_peer(peer) {
|
||||||
Ok(()) => Ok(true),
|
Ok(()) => Ok(true),
|
||||||
|
@ -134,10 +134,16 @@ impl MinerService for TestMinerService {
|
|||||||
self.authoring_params.read().clone()
|
self.authoring_params.read().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_author(&self, author: miner::Author) {
|
fn set_author<T: Into<Option<miner::Author>>>(&self, author: T) {
|
||||||
self.authoring_params.write().author = author.address();
|
let author_opt = author.into();
|
||||||
if let miner::Author::Sealer(signer) = author {
|
self.authoring_params.write().author = author_opt
|
||||||
*self.signer.write() = Some(signer);
|
.as_ref()
|
||||||
|
.map(miner::Author::address)
|
||||||
|
.unwrap_or_default();
|
||||||
|
match author_opt {
|
||||||
|
Some(miner::Author::Sealer(signer)) => *self.signer.write() = Some(signer),
|
||||||
|
Some(miner::Author::External(_addr)) => (),
|
||||||
|
None => *self.signer.write() = None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,10 @@ pub trait ParitySet {
|
|||||||
#[rpc(name = "parity_setEngineSignerSecret")]
|
#[rpc(name = "parity_setEngineSignerSecret")]
|
||||||
fn set_engine_signer_secret(&self, _: H256) -> Result<bool>;
|
fn set_engine_signer_secret(&self, _: H256) -> Result<bool>;
|
||||||
|
|
||||||
|
/// Unsets the engine signer account address.
|
||||||
|
#[rpc(name = "parity_clearEngineSigner")]
|
||||||
|
fn clear_engine_signer(&self) -> Result<bool>;
|
||||||
|
|
||||||
/// Sets the limits for transaction queue.
|
/// Sets the limits for transaction queue.
|
||||||
#[rpc(name = "parity_setTransactionsLimit")]
|
#[rpc(name = "parity_setTransactionsLimit")]
|
||||||
fn set_transactions_limit(&self, _: usize) -> Result<bool>;
|
fn set_transactions_limit(&self, _: usize) -> Result<bool>;
|
||||||
|
@ -28,6 +28,7 @@ extern crate parking_lot;
|
|||||||
extern crate rustc_hex;
|
extern crate rustc_hex;
|
||||||
extern crate vm;
|
extern crate vm;
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
use criterion::{black_box, Bencher, Criterion};
|
use criterion::{black_box, Bencher, Criterion};
|
||||||
use ethereum_types::{Address, U256};
|
use ethereum_types::{Address, U256};
|
||||||
use evm::Factory;
|
use evm::Factory;
|
||||||
@ -37,6 +38,28 @@ use vm::{tests::FakeExt, ActionParams, Ext, GasLeft, Result};
|
|||||||
|
|
||||||
criterion_group!(
|
criterion_group!(
|
||||||
basic,
|
basic,
|
||||||
|
mul500,
|
||||||
|
mul1000,
|
||||||
|
div500,
|
||||||
|
div1000,
|
||||||
|
sdiv500,
|
||||||
|
sdiv1000,
|
||||||
|
mod500,
|
||||||
|
mod1000,
|
||||||
|
smod500,
|
||||||
|
smod1000,
|
||||||
|
addmod500,
|
||||||
|
addmod1000,
|
||||||
|
mulmod500,
|
||||||
|
mulmod1000,
|
||||||
|
mulmod1_500,
|
||||||
|
mulmod1_1000,
|
||||||
|
mulmod5_500,
|
||||||
|
mulmod5_1000,
|
||||||
|
mulmod11_500,
|
||||||
|
mulmod11_1000,
|
||||||
|
mulmod_big_500,
|
||||||
|
mulmod_big_1000,
|
||||||
simple_loop_log0_usize,
|
simple_loop_log0_usize,
|
||||||
simple_loop_log0_u256,
|
simple_loop_log0_u256,
|
||||||
mem_gas_calculation_same_usize,
|
mem_gas_calculation_same_usize,
|
||||||
@ -206,3 +229,188 @@ fn result(r: Result<evm::GasLeft>) -> U256 {
|
|||||||
_ => U256::zero(),
|
_ => U256::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs a given EVM bytecode.
|
||||||
|
fn run_code(b: &mut Bencher, code: Bytes) {
|
||||||
|
let factory = Factory::default();
|
||||||
|
let mut ext = FakeExt::new();
|
||||||
|
b.iter(|| {
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
params.gas = U256::MAX;
|
||||||
|
params.code = Some(Arc::new(black_box(code.clone())));
|
||||||
|
let vm = factory.create(params, ext.schedule(), 0);
|
||||||
|
result(vm.exec(&mut ext).ok().unwrap())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 1) 500 times.
|
||||||
|
fn mulmod1_500(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo 1, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 1) 1000 times.
|
||||||
|
fn mulmod1_1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo 1, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 5) 500 times.
|
||||||
|
fn mulmod5_500(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo 5, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 5) 1000 times.
|
||||||
|
fn mulmod5_1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo 5, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 11) 500 times.
|
||||||
|
fn mulmod11_500(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo 11, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 11) 1000 times.
|
||||||
|
fn mulmod11_1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo 11, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 0x58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd591) 500 times.
|
||||||
|
fn mulmod_big_500(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo random 256-bit number, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(U256::MAX, U256::MAX, 0x58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd591) 1000 times.
|
||||||
|
fn mulmod_big_1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod modulo random 256-bit number, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(a, b, c) for random 256-bit a, b and c. Iterate 500 times.
|
||||||
|
///
|
||||||
|
/// Source:
|
||||||
|
/// ```
|
||||||
|
/// PUSH2 0x01F4
|
||||||
|
/// JUMPDEST
|
||||||
|
/// PUSH1 0x01
|
||||||
|
/// SWAP1
|
||||||
|
/// SUB
|
||||||
|
/// PUSH32 0x5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a937
|
||||||
|
/// PUSH32 0xb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db
|
||||||
|
/// PUSH32 0xcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca
|
||||||
|
/// MULMOD
|
||||||
|
/// POP
|
||||||
|
/// DUP1
|
||||||
|
/// PUSH1 0x03
|
||||||
|
/// JUMPI
|
||||||
|
/// ```
|
||||||
|
fn mulmod500(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod randomly generated ints, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mulmod(a, b, c) for random 256-bit a, b and c. Iterate 1000 times.
|
||||||
|
fn mulmod1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("mulmod randomly generated ints, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute addmod(a, b, c) for random 256-bit a, b and c. Iterate 500 times.
|
||||||
|
fn addmod500(b: &mut Criterion) {
|
||||||
|
b.bench_function("addmod randomly generated ints, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute addmod(a, b, c) for random 256-bit a, b and c. Iterate 1000 times.
|
||||||
|
fn addmod1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("addmod randomly generated ints, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mul(a, b) for random 256-bit a and b. Iterate 500 times.
|
||||||
|
fn mul500(b: &mut Criterion) {
|
||||||
|
b.bench_function("mul randomly generated ints, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mul(a, b) for random 256-bit a and b. Iterate 1000 times.
|
||||||
|
fn mul1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("mul randomly generated ints, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute div(a, b) for random 256-bit a and b. Iterate 500 times.
|
||||||
|
fn div500(b: &mut Criterion) {
|
||||||
|
b.bench_function("div randomly generated ints, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute div(a, b) for random 256-bit a and b. Iterate 1000 times.
|
||||||
|
fn div1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("div randomly generated ints, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute sdiv(a, b) for random 256-bit a and b. Iterate 500 times.
|
||||||
|
fn sdiv500(b: &mut Criterion) {
|
||||||
|
b.bench_function("sdiv randomly generated ints, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute sdiv(a, b) for random 256-bit a and b. Iterate 1000 times.
|
||||||
|
fn sdiv1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("sdiv randomly generated ints, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mod(a, b) for random 256-bit a and b. Iterate 500 times.
|
||||||
|
fn mod500(b: &mut Criterion) {
|
||||||
|
b.bench_function("mod randomly generated ints, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute mod(a, b) for random 256-bit a and b. Iterate 1000 times.
|
||||||
|
fn mod1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("mod randomly generated ints, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute smod(a, b) for random 256-bit a and b. Iterate 500 times.
|
||||||
|
fn smod500(b: &mut Criterion) {
|
||||||
|
b.bench_function("smod randomly generated ints, 500 times", |b| {
|
||||||
|
run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute smod(a, b) for random 256-bit a and b. Iterate 1000 times.
|
||||||
|
fn smod1000(b: &mut Criterion) {
|
||||||
|
b.bench_function("smod randomly generated ints, 1000 times", |b| {
|
||||||
|
run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357".from_hex().unwrap());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user