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:
rakita 2021-03-25 14:37:01 +01:00 committed by GitHub
parent 561ed8df3c
commit 85391f99ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 4278 additions and 575 deletions

1
Cargo.lock generated
View File

@ -952,6 +952,7 @@ dependencies = [
"common-types",
"criterion 0.2.11",
"crossbeam-utils 0.6.6",
"derive_more",
"eip-152",
"env_logger",
"error-chain",

View 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)
}
}

View File

@ -76,6 +76,7 @@ using_queue = { path = "../concensus/miner/using-queue" }
vm = { path = "../vm/vm" }
walkdir = "2.3"
wasm = { path = "../vm/wasm" }
derive_more = "0.99"
scopeguard = "1.1.0"
[dev-dependencies]

View File

@ -88,9 +88,13 @@
"builtin": {
"name": "ecrecover",
"pricing": {
"linear": {
"base": 3000,
"word": 0
"0": {
"price": {
"linear": {
"base": 3000,
"word": 0
}
}
}
}
}
@ -100,9 +104,13 @@
"builtin": {
"name": "sha256",
"pricing": {
"linear": {
"base": 60,
"word": 12
"0": {
"price": {
"linear": {
"base": 60,
"word": 12
}
}
}
}
}
@ -112,9 +120,13 @@
"builtin": {
"name": "ripemd160",
"pricing": {
"linear": {
"base": 600,
"word": 120
"0": {
"price": {
"linear": {
"base": 600,
"word": 120
}
}
}
}
}
@ -124,9 +136,13 @@
"builtin": {
"name": "identity",
"pricing": {
"linear": {
"base": 15,
"word": 3
"0": {
"price": {
"linear": {
"base": 15,
"word": 3
}
}
}
}
}
@ -134,10 +150,13 @@
"0x0000000000000000000000000000000000000005": {
"builtin": {
"name": "modexp",
"activate_at": "0x4d50f8",
"pricing": {
"modexp": {
"divisor": 20
"0x4d50f8": {
"price": {
"modexp": {
"divisor": 20
}
}
}
}
}
@ -147,11 +166,19 @@
"name": "alt_bn128_add",
"pricing": {
"0x4d50f8": {
"price": { "alt_bn128_const_operations": { "price": 500 }}
"price": {
"alt_bn128_const_operations": {
"price": 500
}
}
},
"0xd751a5": {
"info": "EIP 1108 transition at block 14_111_141 (0xd751a5)",
"price": { "alt_bn128_const_operations": { "price": 150 }}
"price": {
"alt_bn128_const_operations": {
"price": 150
}
}
}
}
}
@ -161,11 +188,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0x4d50f8": {
"price": { "alt_bn128_const_operations": { "price": 40000 }}
"price": {
"alt_bn128_const_operations": {
"price": 40000
}
}
},
"0xd751a5": {
"info": "EIP 1108 transition at block 14_111_141 (0xd751a5)",
"price": { "alt_bn128_const_operations": { "price": 6000 }}
"price": {
"alt_bn128_const_operations": {
"price": 6000
}
}
}
}
}
@ -175,11 +210,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0x4d50f8": {
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
"price": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
},
"0xd751a5": {
"info": "EIP 1108 transition at block 14_111_141 (0xd751a5)",
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
"price": {
"alt_bn128_pairing": {
"base": 45000,
"pair": 34000
}
}
}
}
}
@ -187,10 +232,13 @@
"0x0000000000000000000000000000000000000009": {
"builtin": {
"name": "blake2_f",
"activate_at": "0xd751a5",
"pricing": {
"blake2_f": {
"gas_per_round": 1
"0xd751a5": {
"price": {
"blake2_f": {
"gas_per_round": 1
}
}
}
}
}
@ -200,13 +248,9 @@
}
},
"nodes": [
"enode://f6e37b943bad3a78cb8589b1798d30d210ffd39cfcd2c8f2de4f098467fd49c667980100d919da7ca46cd50505d30989abda87f0b9339377de13d6592c22caf8@34.198.49.72:30303",
"enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303",
"enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@3.217.96.11:30303",
"enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303",
"enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303",
"enode://38e6e7fd416293ed120d567a2675fe078c0205ab0671abf16982ce969823bd1f3443d590c18b321dfae7dcbe1f6ba98ef8702f255c3c9822a188abb82c53adca@51.77.66.187:30303",
"enode://6f289111f7c77c68651b0f4803c3a47bcec801f9c618bb41231a1a24a6dbb9c76f2fdb63ba7a21357c41ebb7f6922c17397c1b5c8f71f7d3ef7965505d4945de@144.217.72.209:30303",
"enode://b6340eb94c3db1362ee517801389fe21cce6354275376b1006f8ce84f8a5cfa2b836268b3727be9db7cd3e581f356f39da39418c4ec1d63d959abc235d99cd86@145.239.7.213:30303"
"enode://740e1c8ea64e71762c71a463a04e2046070a0c9394fcab5891d41301dc473c0cff00ebab5a9bc87fbcb610ab98ac18225ff897bc8b7b38def5975d5ceb0a7d7c@108.61.170.124:30303",
"enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@157.230.31.163:30303"
]
}

View File

@ -22,7 +22,10 @@
}
},
"blockRewardContractAddress": "0x4d0153D434384128D17243409e02fca1B3EE21D6",
"blockRewardContractTransition": 5761140
"blockRewardContractTransition": 5761140,
"randomnessContractAddress": {
"14350721": "0x67e90a54AeEA85f21949c645082FE95d77BC1E70"
}
}
}
},
@ -55,12 +58,12 @@
"gasLimit": "0x663BE0"
},
"nodes": [
"enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@178.128.175.244:30303",
"enode://24f3bf1307b8e9cd53abaaa339db5653aa3db64d579f7f37788b8a9b938420cc10004dc9af4edbb0f0611a57b741a4912dd081917b3d373e4409d788c422e7e6@134.209.22.144:30303",
"enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@142.93.3.70:30303",
"enode://6e3d1b39cbd2a9c4f053a27e68fd90d0bac83691dfdc4a13c59f2555078a71e63c5daaee5a82aa6db500512760a5456f86076bf8bbe8011c27c82ed7d6f5fb26@45.77.140.210:30303",
"enode://31dffed97f8fed1f34fe66453280a89cbeeda60cf28f6fbb212ebbefd7c7566a02c1c7d5c00bbbb49b9fa8a49f157e0f786f379ca9bcbf2fea24de70d70a22b6@206.156.242.61:30303",
"enode://6bdc7553ab2e4914cb47774c1e6d8c8f47ac7c3981891f85f65d06f208ea1bc4d3bf982b330950e0a0cd127efd7145c4df7113159a1d4a06ed722e6c16d0ac6c@45.32.215.190:30303",
"enode://872d82a24144bc007658fb6fac0dcdfb9b63aeb05ef563a06d0186f2d1e5ffbfc5c4f1244891a8a86ef70682b9d24382e654b305224883698862e2df647a4d23@45.76.236.247:30303",
"enode://b11fbc6cde81c80be69508aca8ffea8460680a25a9c151b683293f8617282062b8e8e139bf91e88cedf60068a3cf927b0d48832fda5169b58a8f7ce442de6fb4@206.189.76.132:30303",
"enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@35.175.15.164:30303"
"enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@178.128.175.244:30303"
],
"accounts": {
"0x0000000000000000000000000000000000000005": {
@ -82,11 +85,19 @@
"name": "alt_bn128_add",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 500 }}
"price": {
"alt_bn128_const_operations": {
"price": 500
}
}
},
"12598600": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 150 }}
"price": {
"alt_bn128_const_operations": {
"price": 150
}
}
}
}
}
@ -96,11 +107,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 40000 }}
"price": {
"alt_bn128_const_operations": {
"price": 40000
}
}
},
"12598600": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 6000 }}
"price": {
"alt_bn128_const_operations": {
"price": 6000
}
}
}
}
}
@ -110,11 +129,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0": {
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
"price": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
},
"12598600": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
"price": {
"alt_bn128_pairing": {
"base": 45000,
"pair": 34000
}
}
}
}
}

View File

@ -25,7 +25,10 @@
}
},
"blockRewardContractAddress": "0x3145197AD50D7083D0222DE4fCCf67d9BD05C30D",
"blockRewardContractTransition": 4639000
"blockRewardContractTransition": 4639000,
"randomnessContractAddress": {
"13391641": "0x8f2b78169B0970F11a762e56659Db52B59CBCf1B"
}
}
}
},
@ -60,14 +63,10 @@
"gasLimit": "0x663BE0"
},
"nodes": [
"enode://bdcd6f875583df2bd8094f08ae58c7c2db6ed67795ca8c0e6415a30721d3657291aec9b933d15e17e0b36ad7a76424a1447ddbfc75809a04f7a0ffef5617dd56@3.91.206.172:30303",
"enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303",
"enode://f11a0f80939b49a28bf99581da9b351a592ec1504b9d32a7dfda79b36510a891e96631239c4166e5c73368c21e9bb3241e7fd6929b899772e5a8fe9a7b7c3af6@45.77.52.149:30303",
"enode://e08adce358fc26dfbe1f24ee578dceaa29575ca44a39d9041203131db5135aceba6241840a9b57b1540eeaf7b4eff1aead28a74641be43342c35af454abb31b3@199.247.18.10:30313",
"enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303",
"enode://0d1e0372f63a3f0b82d66635ea101ecc0f6797788a078805cc933dd93e6a22f7c9fa51ab4e2d21da02d04480ef19f3bbb9a2b41dd1c262085d295a354bb8b0f9@18.217.47.209:30303",
"enode://875e1bd1b98019a5d6d588c23f68534b75462dd6ecbb3dd058221dbf7aa923f0ab782ab93bb82d42edc9996f7f0816a318bdc761e55c02b95e1169cef66f7edc@159.203.24.35:30303",
"enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303",
"enode://182ee200ca134dc4d6390f3d5aadbcd80df0f7f24335830335d142573eacce4eeb919d30e82c5df588034e167e6ba6dd11187502ac9264a71005127f6b146a99@159.203.95.241:30303",
"enode://b022ff70b5fcaf9596ae5efed99a8198b4ae0578ee9d17b733609d803a75cef95d3a2a18e50dca9a7c3b26139f158c59eaf8b5fb8d1d331c9a46934a78acabe8@206.189.76.128:30303"
"enode://f11a0f80939b49a28bf99581da9b351a592ec1504b9d32a7dfda79b36510a891e96631239c4166e5c73368c21e9bb3241e7fd6929b899772e5a8fe9a7b7c3af6@45.77.52.149:30303"
],
"accounts": {
"0000000000000000000000000000000000000005": {
@ -89,11 +88,19 @@
"name": "alt_bn128_add",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 500 }}
"price": {
"alt_bn128_const_operations": {
"price": 500
}
}
},
"12095200": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 150 }}
"price": {
"alt_bn128_const_operations": {
"price": 150
}
}
}
}
}
@ -103,11 +110,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 40000 }}
"price": {
"alt_bn128_const_operations": {
"price": 40000
}
}
},
"12095200": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 6000 }}
"price": {
"alt_bn128_const_operations": {
"price": 6000
}
}
}
}
}
@ -117,11 +132,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0": {
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
"price": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
},
"12095200": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
"price": {
"alt_bn128_pairing": {
"base": 45000,
"pair": 34000
}
}
}
}
}

View File

@ -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"
}
}
}

View 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);
}
}

View File

@ -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

View 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];
}
}

View File

@ -11,15 +11,27 @@
"validators": {
"multi": {
"0": {
"list": ["0xcace5b3c29211740e595850e80478416ee77ca21"]
"list": [
"0xcace5b3c29211740e595850e80478416ee77ca21"
]
},
"1300": {
"safeContract": "0x22e1229a2c5b95a60983b5577f745a603284f535"
},
"9186425": {
"contract": "0xB87BE9f7196F2AE084Ca1DE6af5264292976e013"
}
}
},
"blockRewardContractAddress": "0x867305d19606aadba405ce534e303d0e225f9556",
"blockRewardContractTransition": 1310
"blockRewardContractTransition": 1310,
"blockRewardContractTransitions": {
"9186425": "0x481c034c6d9441db23Ea48De68BCAe812C5d39bA"
},
"randomnessContractAddress": {
"9186425": "0x5870b0527DeDB1cFBD9534343Feda1a41Ce47766"
},
"posdaoTransition": 9186425
}
}
},
@ -42,7 +54,9 @@
"eip1706Transition": 7298030,
"eip1884Transition": 7298030,
"eip2028Transition": 7298030,
"registrar": "0x1ec97dc137f5168af053c24460a1200502e1a9d2"
"registrar": "0x6B53721D4f2Fb9514B85f5C49b197D857e36Cf03",
"transactionPermissionContract": "0x7Dd7032AA75A37ea0b150f57F899119C7379A78b",
"transactionPermissionContractTransition": 9186425
},
"genesis": {
"seal": {
@ -54,6 +68,25 @@
"difficulty": "0x20000",
"gasLimit": "0x989680"
},
"nodes": [
"enode://4716883567b5317aad93ea28e707fad0631fb4aa5ac7c5fbd485380b01d8801c21a8cbf4d6ee3a2c9b2b070a270a49d4a2a0da9e1d47a1f433dafbaf7b2edd06@157.245.92.222:30303",
"enode://ab7f6c633ba2dc54795dfd2c739ba7d964f499541c0b8d8ba9d275bd3df1b789470a21a921a469fa515a3dfccc96a434a3fd016a169d88d0043fc6744f34288e@67.205.180.17:30303",
"enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@67.205.145.143:30303",
"enode://bd75111424c42c349fc255db017ac0be370b37b558627e3bbc41319071ef7642c04cdbd2b674193a99aa35d67a83016ab293b8ab87ed4a4606e69f114ac95535@157.230.185.80:30303",
"enode://bd75111424c42c349fc255db017ac0be370b37b558627e3bbc41319071ef7642c04cdbd2b674193a99aa35d67a83016ab293b8ab87ed4a4606e69f114ac95535@161.35.51.60:30303",
"enode://ef94ffb10c440dd990c5c4be1c85046f5f7329ba60d23db7a68c8b91b6a721081f8190369f3a32f3c02d213127b2066eb42ee0444998d354ba0923378522acb3@161.35.62.72:30303",
"enode://4a0eadf22d6a37c5596fd2df2a53a26a5b59dd863e67246ab94e6a81b31765e08d9f70a4dd9683221e63cc2120c8a808a6a457455bd658bdf49c688c62db2011@51.81.244.170:30303",
"enode://e75a1e9f080bd6012b39321c0f2d984567172625280b3e7362e962a42578c5e79c847b3eb83aa7e2a4cdeefbfadf0c36ed2719cad1d5e6377ccd6ebe314cc6bc@64.227.97.130:30303",
"enode://6674773f7aac78d5527fa90c847dcbca198de4081306406a8fec5c15f7a2e141362344041291dd10d0aafa7706a3d8f21a08b6f6834a5b1aab9cccd8ca35ccee@143.110.226.15:30303",
"enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@134.122.24.231:30303",
"enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@67.205.145.143:30303",
"enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@162.243.164.98:30303",
"enode://0caa2d84aef00d0bc5de6cf9db3e736da245d882ec8f91e201b3e1635960e62cbb2f8bfc57e679ff3e1d53da2773e31df624a56b2f457ecb51d09fdf9970c86b@167.99.4.175:30303",
"enode://da2449aaba873c40c6daf764de55f4b9eae24c4738daec893ef95b6ada96463c6b9624f8e376e1073d21dd820c5bb361e14575121b09bbd7735b6b556ee1b768@67.205.176.117:30303",
"enode://e8c7a0db430429bb374c981438c0dbd95e565088a483388aa46d8377a3bd62f02cd83d7e2c7e5fc77606141bfef29d23d4285a7c1d9b7e743cf3029314506df7@80.240.16.221:30303",
"enode://2cde5ae04ed57bba5bac8311a97be056838d5304bc3bcee698066e5fc532e846f785198f87e8b84b57b03622a22aac5dd2853c5203d1ece2c9f25b48487d145b@149.28.57.8:30303",
"enode://90b0a0e74a9a1ad258531b4ceec25587d8b52ff2cfb36206a34bf6ba1a8d21b2abd20da13260102508a2ac67afbeb2d2ab7a5e9d6bea3bce845cd81e655585cc@45.77.110.159:30303"
],
"accounts": {
"0x0000000000000000000000000000000000000005": {
"builtin": {
@ -74,11 +107,19 @@
"name": "alt_bn128_add",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 500 }}
"price": {
"alt_bn128_const_operations": {
"price": 500
}
}
},
"7298030": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 150 }}
"price": {
"alt_bn128_const_operations": {
"price": 150
}
}
}
}
}
@ -88,11 +129,19 @@
"name": "alt_bn128_mul",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 40000 }}
"price": {
"alt_bn128_const_operations": {
"price": 40000
}
}
},
"7298030": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 6000 }}
"price": {
"alt_bn128_const_operations": {
"price": 6000
}
}
}
}
}
@ -102,11 +151,21 @@
"name": "alt_bn128_pairing",
"pricing": {
"0": {
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
"price": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
},
"7298030": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
"price": {
"alt_bn128_pairing": {
"base": 45000,
"pair": 34000
}
}
}
}
}
@ -190,10 +249,6 @@
}
}
}
},
"nodes": [
"enode://1c19ba0a77dd663b843c33beb9020e7eb41fc34b47b98424dbc427f74692115d74c7c27b6c0aa2b59bb1d8f710650cae1090153d10b6909ca7bdcdfb183b1c59@54.39.190.172:30303",
"enode://c1c3a604950119f82d78189792b73f5a82a239017c77465e3c32fc51c1d758a9a772ffddd58436d465342f2cfa6d4a442a49e526743f4d8354d7c5ce794c3ee5@95.179.222.48:30303"
]
}
}

View 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"
}
]

View File

@ -0,0 +1,16 @@
[
{
"constant": true,
"inputs": [],
"name": "blockGasLimit",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

View 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"
}
]

View 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;
}
}

View 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"
}
]

View File

@ -1,4 +1,5 @@
[
{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"},{"name":"proof","type":"bytes"}],"name":"reportMalicious","outputs":[],"payable":false,"type":"function"},
{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"}
{"constant":false,"inputs":[{"name":"validator","type":"address"},{"name":"blockNumber","type":"uint256"}],"name":"reportBenign","outputs":[],"payable":false,"type":"function"},
{"constant": true, "inputs": [ { "name": "validator", "type": "address" }, { "name": "blockNum", "type": "uint256" } ], "name": "maliceReportedForBlock", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }
]

View File

@ -1,5 +1,55 @@
[
{"constant":false,"inputs":[],"name":"finalizeChange","outputs":[],"payable":false,"type":"function"},
{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"validators","type":"address[]"}],"payable":false,"type":"function"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"InitiateChange","type":"event"}
]
{"anonymous":false,"inputs":[{"indexed":true,"name":"_parent_hash","type":"bytes32"},{"indexed":false,"name":"_new_set","type":"address[]"}],"name":"InitiateChange","type":"event"},
{
"constant": true,
"inputs": [],
"name": "emitInitiateChangeCallable",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "emitInitiateChange",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_reportingValidator",
"type": "address"
},
{
"name": "_maliciousValidator",
"type": "address"
},
{
"name": "_blockNumber",
"type": "uint256"
}
],
"name": "shouldValidatorReport",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

View File

@ -64,17 +64,20 @@ use ansi_term::Colour;
use block::{enact_verified, ClosedBlock, Drain, LockedBlock, OpenBlock, SealedBlock};
use call_contract::RegistryInfo;
use client::{
ancient_import::AncientVerifier, bad_blocks, traits::ForceUpdateSealing, AccountData,
BadBlocks, Balance, BlockChain as BlockChainTrait, BlockChainClient, BlockChainReset, BlockId,
BlockInfo, BlockProducer, BroadcastProposalBlock, Call, CallAnalytics, ChainInfo,
ChainMessageType, ChainNotify, ChainRoute, ClientConfig, ClientIoMessage, EngineInfo,
ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient, Mode, NewBlocks, Nonce,
PrepareOpenBlock, ProvingBlockChainClient, PruningInfo, ReopenBlock, ScheduleInfo,
SealedBlockImporter, StateClient, StateInfo, StateOrBlock, TraceFilter, TraceId, TransactionId,
TransactionInfo, UncleId,
ancient_import::AncientVerifier,
bad_blocks,
traits::{ForceUpdateSealing, TransactionRequest},
AccountData, BadBlocks, Balance, BlockChain as BlockChainTrait, BlockChainClient,
BlockChainReset, BlockId, BlockInfo, BlockProducer, BroadcastProposalBlock, Call,
CallAnalytics, ChainInfo, ChainMessageType, ChainNotify, ChainRoute, ClientConfig,
ClientIoMessage, EngineInfo, ImportBlock, ImportExportBlocks, ImportSealedBlock, IoClient,
Mode, NewBlocks, Nonce, PrepareOpenBlock, ProvingBlockChainClient, PruningInfo, ReopenBlock,
ScheduleInfo, SealedBlockImporter, StateClient, StateInfo, StateOrBlock, TraceFilter, TraceId,
TransactionId, TransactionInfo, UncleId,
};
use engines::{
epoch::PendingTransition, EngineError, EpochTransition, EthEngine, ForkChoice, MAX_UNCLE_AGE,
epoch::PendingTransition, EngineError, EpochTransition, EthEngine, ForkChoice, SealingState,
MAX_UNCLE_AGE,
};
use error::{
BlockError, CallError, Error as EthcoreError, ErrorKind as EthcoreErrorKind, EthcoreResult,
@ -2613,31 +2616,46 @@ impl BlockChainClient for Client {
}
}
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
fn create_transaction(
&self,
TransactionRequest {
action,
data,
gas,
gas_price,
nonce,
}: TransactionRequest,
) -> Result<SignedTransaction, transaction::Error> {
let authoring_params = self.importer.miner.authoring_params();
let service_transaction_checker = self.importer.miner.service_transaction_checker();
let gas_price = if let Some(checker) = service_transaction_checker {
match checker.check_address(self, authoring_params.author) {
Ok(true) => U256::zero(),
_ => self.importer.miner.sensible_gas_price(),
_ => gas_price.unwrap_or_else(|| self.importer.miner.sensible_gas_price()),
}
} else {
self.importer.miner.sensible_gas_price()
};
let transaction = TypedTransaction::Legacy(transaction::Transaction {
nonce: self.latest_nonce(&authoring_params.author),
action: Action::Call(address),
gas: self.importer.miner.sensible_gas_limit(),
nonce: nonce.unwrap_or_else(|| self.latest_nonce(&authoring_params.author)),
action,
gas: gas.unwrap_or_else(|| self.importer.miner.sensible_gas_limit()),
gas_price,
value: U256::zero(),
data: data,
data,
});
let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
let signature = self
.engine
.sign(transaction.signature_hash(chain_id))
.map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?;
let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
Ok(SignedTransaction::new(
transaction.with_signature(signature, chain_id),
)?)
}
fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> {
let signed = self.create_transaction(tx_request)?;
self.importer
.miner
.import_own_transaction(self, signed.into())
@ -2906,7 +2924,7 @@ impl ImportSealedBlock for Client {
&[],
route.enacted(),
route.retracted(),
self.engine.seals_internally().is_some(),
self.engine.sealing_state() != SealingState::External,
);
self.notify(|notify| {
notify.new_blocks(NewBlocks::new(
@ -3599,8 +3617,13 @@ mod tests {
#[test]
fn should_mark_finalization_correctly_for_parent() {
let client =
generate_dummy_client_with_spec_and_data(Spec::new_test_with_finality, 2, 0, &[]);
let client = generate_dummy_client_with_spec_and_data(
Spec::new_test_with_finality,
2,
0,
&[],
false,
);
let chain = client.chain();
let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h));

View File

@ -59,12 +59,12 @@ use vm::Schedule;
use block::{ClosedBlock, OpenBlock, SealedBlock};
use call_contract::{CallContract, RegistryInfo};
use client::{
traits::ForceUpdateSealing, AccountData, BadBlocks, Balance, BlockChain, BlockChainClient,
BlockChainInfo, BlockId, BlockInfo, BlockProducer, BlockStatus, BroadcastProposalBlock, Call,
CallAnalytics, ChainInfo, EngineInfo, ImportBlock, ImportSealedBlock, IoClient, LastHashes,
Mode, Nonce, PrepareOpenBlock, ProvingBlockChainClient, ReopenBlock, ScheduleInfo,
SealedBlockImporter, StateClient, StateOrBlock, TraceFilter, TraceId, TransactionId,
TransactionInfo, UncleId,
traits::{ForceUpdateSealing, TransactionRequest},
AccountData, BadBlocks, Balance, BlockChain, BlockChainClient, BlockChainInfo, BlockId,
BlockInfo, BlockProducer, BlockStatus, BroadcastProposalBlock, Call, CallAnalytics, ChainInfo,
EngineInfo, ImportBlock, ImportSealedBlock, IoClient, LastHashes, Mode, Nonce,
PrepareOpenBlock, ProvingBlockChainClient, ReopenBlock, ScheduleInfo, SealedBlockImporter,
StateClient, StateOrBlock, TraceFilter, TraceId, TransactionId, TransactionInfo, UncleId,
};
use engines::EthEngine;
use error::{Error, EthcoreResult};
@ -1039,12 +1039,22 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
fn create_transaction(
&self,
TransactionRequest {
action,
data,
gas,
gas_price,
nonce,
}: TransactionRequest,
) -> Result<SignedTransaction, transaction::Error> {
let transaction = TypedTransaction::Legacy(Transaction {
nonce: self.latest_nonce(&self.miner.authoring_params().author),
action: Action::Call(address),
gas: self.spec.gas_limit,
gas_price: U256::zero(),
nonce: nonce
.unwrap_or_else(|| self.latest_nonce(&self.miner.authoring_params().author)),
action,
gas: gas.unwrap_or(self.spec.gas_limit),
gas_price: gas_price.unwrap_or_else(U256::zero),
value: U256::default(),
data: data,
});
@ -1054,7 +1064,11 @@ impl BlockChainClient for TestBlockChainClient {
.engine
.sign(transaction.signature_hash(chain_id))
.unwrap();
let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap();
Ok(SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap())
}
fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> {
let signed = self.create_transaction(tx_request)?;
self.miner.import_own_transaction(self, signed.into())
}

View File

@ -40,7 +40,7 @@ use types::{
pruning_info::PruningInfo,
receipt::LocalizedReceipt,
trace_filter::Filter as TraceFilter,
transaction::{self, LocalizedTransaction, SignedTransaction},
transaction::{self, Action, LocalizedTransaction, SignedTransaction},
BlockNumber,
};
use vm::LastHashes;
@ -427,8 +427,15 @@ pub trait BlockChainClient:
/// Returns information about pruning/data availability.
fn pruning_info(&self) -> PruningInfo;
/// Schedule state-altering transaction to be executed on the next pending block.
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error>;
/// Returns a transaction signed with the key configured in the engine signer.
fn create_transaction(
&self,
tx_request: TransactionRequest,
) -> Result<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.
fn registrar_address(&self) -> Option<Address>;
@ -437,6 +444,55 @@ pub trait BlockChainClient:
fn is_processing_fork(&self) -> bool;
}
/// The data required for a `Client` to create a transaction.
///
/// Gas limit, gas price, or nonce can be set explicitly, e.g. to create service
/// transactions with zero gas price, or sequences of transactions with consecutive nonces.
/// Added for AuRa needs.
pub struct TransactionRequest {
/// Transaction action
pub action: Action,
/// Transaction data
pub data: Bytes,
/// Transaction gas usage
pub gas: Option<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
pub trait ReopenBlock {
/// Reopens an OpenBlock and updates uncles.

View File

@ -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()
}
}

View File

@ -22,6 +22,7 @@ use std::collections::{
};
use ethereum_types::{Address, H256};
use types::BlockNumber;
use engines::validator_set::SimpleList;
@ -32,20 +33,23 @@ pub struct UnknownValidator;
/// Rolling finality checker for authority round consensus.
/// Stores a chain of unfinalized hashes that can be pushed onto.
pub struct RollingFinality {
headers: VecDeque<(H256, Vec<Address>)>,
headers: VecDeque<(H256, BlockNumber, Vec<Address>)>,
signers: SimpleList,
sign_count: HashMap<Address, usize>,
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 {
/// 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 {
headers: VecDeque::new(),
signers: SimpleList::new(signers),
sign_count: HashMap::new(),
last_pushed: None,
two_thirds_majority_transition,
}
}
@ -55,45 +59,35 @@ impl RollingFinality {
/// Fails if any provided signature isn't part of the signers set.
pub fn build_ancestry_subchain<I>(&mut self, iterable: I) -> Result<(), UnknownValidator>
where
I: IntoIterator<Item = (H256, Vec<Address>)>,
I: IntoIterator<Item = (H256, BlockNumber, Vec<Address>)>,
{
self.clear();
for (hash, signers) in iterable {
for (hash, number, signers) in iterable {
if signers.iter().any(|s| !self.signers.contains(s)) {
return Err(UnknownValidator);
}
if self.last_pushed.is_none() {
self.last_pushed = Some(hash)
}
self.add_signers(&signers);
self.headers.push_front((hash, number, signers));
// break when we've got our first finalized block.
{
let current_signed = self.sign_count.len();
let new_signers = signers
.iter()
.filter(|s| !self.sign_count.contains_key(s))
.count();
let would_be_finalized = (current_signed + new_signers) * 2 > self.signers.len();
if would_be_finalized {
trace!(target: "finality", "Encountered already finalized block {}", hash);
break;
}
for signer in signers.iter() {
*self.sign_count.entry(*signer).or_insert(0) += 1;
}
if self.is_finalized() {
let (hash, _, signers) = self
.headers
.pop_front()
.expect("we just pushed a block; qed");
self.remove_signers(&signers);
trace!(target: "finality", "Encountered already finalized block {}", hash);
break;
}
self.headers.push_front((hash, signers));
}
trace!(target: "finality", "Rolling finality state: {:?}", self.headers);
Ok(())
}
/// Clear the finality status, but keeps the validator set.
/// Clears the finality status, but keeps the validator set.
pub fn clear(&mut self) {
self.headers.clear();
self.sign_count.clear();
@ -108,7 +102,7 @@ impl RollingFinality {
/// Get an iterator over stored hashes in order.
#[cfg(test)]
pub fn unfinalized_hashes(&self) -> impl Iterator<Item = &H256> {
self.headers.iter().map(|(h, _)| h)
self.headers.iter().map(|(h, _, _)| h)
}
/// Get the validator set.
@ -124,41 +118,26 @@ impl RollingFinality {
pub fn push_hash(
&mut self,
head: H256,
number: BlockNumber,
signers: Vec<Address>,
) -> Result<Vec<H256>, UnknownValidator> {
if signers.iter().any(|s| !self.signers.contains(s)) {
return Err(UnknownValidator);
}
for signer in signers.iter() {
*self.sign_count.entry(*signer).or_insert(0) += 1;
}
self.headers.push_back((head, signers));
self.add_signers(&signers);
self.headers.push_back((head, number, signers));
let mut newly_finalized = Vec::new();
while self.sign_count.len() * 2 > self.signers.len() {
let (hash, signers) = self
while self.is_finalized() {
let (hash, _, signers) = self
.headers
.pop_front()
.expect("headers length always greater than sign count length; qed");
self.remove_signers(&signers);
newly_finalized.push(hash);
for signer in signers {
match self.sign_count.entry(signer) {
Entry::Occupied(mut entry) => {
// decrement count for this signer and purge on zero.
*entry.get_mut() -= 1;
if *entry.get() == 0 {
entry.remove();
}
}
Entry::Vacant(_) => panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed"),
}
}
}
trace!(target: "finality", "Blocks finalized by {:?}: {:?}", head, newly_finalized);
@ -166,19 +145,62 @@ impl RollingFinality {
self.last_pushed = Some(head);
Ok(newly_finalized)
}
/// Returns the first block for which a 2/3 quorum (instead of 1/2) is required.
pub fn two_thirds_majority_transition(&self) -> BlockNumber {
self.two_thirds_majority_transition
}
/// Returns whether the first entry in `self.headers` is finalized.
fn is_finalized(&self) -> bool {
match self.headers.front() {
None => false,
Some((_, number, _)) if *number < self.two_thirds_majority_transition => {
self.sign_count.len() * 2 > self.signers.len()
}
Some((_, _, _)) => self.sign_count.len() * 3 > self.signers.len() * 2,
}
}
/// Adds the signers to the sign count.
fn add_signers(&mut self, signers: &[Address]) {
for signer in signers {
*self.sign_count.entry(*signer).or_insert(0) += 1;
}
}
/// Removes the signers from the sign count.
fn remove_signers(&mut self, signers: &[Address]) {
for signer in signers {
match self.sign_count.entry(*signer) {
Entry::Occupied(mut entry) => {
// decrement count for this signer and purge on zero.
if *entry.get() <= 1 {
entry.remove();
} else {
*entry.get_mut() -= 1;
}
}
Entry::Vacant(_) => {
panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed");
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::RollingFinality;
use ethereum_types::{Address, H256};
use types::BlockNumber;
#[test]
fn rejects_unknown_signers() {
let signers = (0..3).map(|_| Address::random()).collect::<Vec<_>>();
let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
assert!(finality
.push_hash(H256::random(), vec![signers[0], Address::random()])
.push_hash(H256::random(), 0, vec![signers[0], Address::random()])
.is_err());
}
@ -186,19 +208,25 @@ mod tests {
fn finalize_multiple() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect();
// 3 / 6 signers is < 51% so no finality.
for (i, hash) in hashes.iter().take(6).cloned().enumerate() {
let i = i % 3;
assert!(finality.push_hash(hash, vec![signers[i]]).unwrap().len() == 0);
assert!(
finality
.push_hash(hash, i as u64, vec![signers[i]])
.unwrap()
.len()
== 0
);
}
// after pushing a block signed by a fourth validator, the first four
// blocks of the unverified chain become verified.
assert_eq!(
finality.push_hash(hashes[6], vec![signers[4]]).unwrap(),
finality.push_hash(hashes[6], 6, vec![signers[4]]).unwrap(),
vec![hashes[0], hashes[1], hashes[2], hashes[3]]
);
}
@ -206,12 +234,12 @@ mod tests {
#[test]
fn finalize_multiple_signers() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
let hash = H256::random();
// after pushing a block signed by four validators, it becomes verified right away.
assert_eq!(
finality.push_hash(hash, signers[0..4].to_vec()).unwrap(),
finality.push_hash(hash, 0, signers[0..4].to_vec()).unwrap(),
vec![hash]
);
}
@ -220,10 +248,9 @@ mod tests {
fn from_ancestry() {
let signers: Vec<_> = (0..6).map(|_| Address::random()).collect();
let hashes: Vec<_> = (0..12)
.map(|i| (H256::random(), vec![signers[i % 6]]))
.map(|i| (H256::random(), i as u64, vec![signers[i % 6]]))
.collect();
let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
finality
.build_ancestry_subchain(hashes.iter().rev().cloned())
.unwrap();
@ -239,12 +266,13 @@ mod tests {
.map(|i| {
(
H256::random(),
i as u64,
vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]],
)
})
.collect();
let mut finality = RollingFinality::blank(signers.clone());
let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value());
finality
.build_ancestry_subchain(hashes.iter().rev().cloned())
.unwrap();
@ -254,4 +282,91 @@ mod tests {
assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0));
assert_eq!(finality.subchain_head(), Some(hashes[11].0));
}
#[test]
fn rejects_unknown_signers_2_3() {
let signers = (0..3).map(|_| Address::random()).collect::<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

View 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))
}
}
}
}

View 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()
}
}

View File

@ -20,7 +20,7 @@ use super::validator_set::{new_validator_set, SimpleList, ValidatorSet};
use block::*;
use client::EngineClient;
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 ethereum_types::{H256, H520};
use ethjson;
@ -104,8 +104,12 @@ impl Engine<EthereumMachine> for BasicAuthority {
1
}
fn seals_internally(&self) -> Option<bool> {
Some(self.signer.read().is_some())
fn sealing_state(&self) -> SealingState {
if self.signer.read().is_some() {
SealingState::Ready
} else {
SealingState::NotReady
}
}
/// Attempt to seal the block internally.
@ -197,8 +201,8 @@ impl Engine<EthereumMachine> for BasicAuthority {
self.validators.register_client(client);
}
fn set_signer(&self, signer: Box<dyn EngineSigner>) {
*self.signer.write() = Some(signer);
fn set_signer(&self, signer: Option<Box<dyn EngineSigner>>) {
*self.signer.write() = signer;
}
fn sign(&self, hash: H256) -> Result<Signature, Error> {
@ -223,7 +227,7 @@ impl Engine<EthereumMachine> for BasicAuthority {
mod tests {
use accounts::AccountProvider;
use block::*;
use engines::Seal;
use engines::{Seal, SealingState};
use ethereum_types::H520;
use hash::keccak;
use spec::Spec;
@ -269,7 +273,7 @@ mod tests {
let spec = new_test_authority();
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 db = spec
.ensure_db_good(get_temp_state_db(), &Default::default())
@ -296,13 +300,15 @@ mod tests {
}
#[test]
fn seals_internally() {
fn sealing_state() {
let tap = AccountProvider::transient_provider();
let authority = tap.insert_account(keccak("").into(), &"".into()).unwrap();
let engine = new_test_authority().engine;
assert!(!engine.seals_internally().unwrap());
engine.set_signer(Box::new((Arc::new(tap), authority, "".into())));
assert!(engine.seals_internally().unwrap());
assert_eq!(SealingState::NotReady, engine.sealing_state());
engine.set_signer(Some(Box::new((Arc::new(tap), authority, "".into()))));
assert_eq!(SealingState::Ready, engine.sealing_state());
engine.set_signer(None);
assert_eq!(SealingState::NotReady, engine.sealing_state());
}
}

View File

@ -38,8 +38,8 @@
///
/// 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
/// 2. We check that the engine seals internally through `Clique::seals_internally()`
/// Note: This is always true for Clique
/// 2. We check that the engine is ready for sealing through `Clique::sealing_state()`
/// Note: This is always `SealingState::Ready` for Clique
/// 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
/// what makes and seals a block.
@ -71,7 +71,7 @@ use client::{traits::ForceUpdateSealing, BlockId, EngineClient};
use crypto::publickey::Signature;
use engines::{
clique::util::{extract_signers, recover_creator},
Engine, EngineError, Seal,
Engine, EngineError, Seal, SealingState,
};
use error::{BlockError, Error};
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.
fn seals_internally(&self) -> Option<bool> {
Some(true)
fn sealing_state(&self) -> SealingState {
SealingState::Ready
}
/// 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>) {
trace!(target: "engine", "set_signer: {}", signer.address());
*self.signer.write() = Some(signer);
fn set_signer(&self, signer: Option<Box<dyn EngineSigner>>) {
let mut current_signer = self.signer.write();
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>) {

View File

@ -15,7 +15,7 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use block::ExecutedBlock;
use engines::{Engine, Seal};
use engines::{Engine, Seal, SealingState};
use machine::Machine;
use std::sync::atomic::{AtomicU64, Ordering};
use types::header::{ExtendedHeader, Header};
@ -63,8 +63,8 @@ impl<M: Machine> Engine<M> for InstantSeal<M> {
&self.machine
}
fn seals_internally(&self) -> Option<bool> {
Some(true)
fn sealing_state(&self) -> SealingState {
SealingState::Ready
}
fn should_reseal_on_update(&self) -> bool {

View File

@ -89,6 +89,10 @@ pub enum EngineError {
InsufficientProof(String),
/// Failed system call.
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.
MalformedMessage(String),
/// 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),
InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", 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),
RequiresClient => format!("Call requires client but none registered"),
RequiresSigner => format!("Call requires signer but none registered"),
@ -180,6 +190,17 @@ pub enum Seal {
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.
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(())
}
/// None means that it requires external input (e.g. PoW) to seal a block.
/// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator).
/// Some(false) means that the node might seal internally but is not qualified now.
fn seals_internally(&self) -> Option<bool> {
None
/// Returns the engine's current sealing state.
fn sealing_state(&self) -> SealingState {
SealingState::External
}
/// 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.
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.
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> {
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.
@ -610,6 +647,11 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
let schedule = self.schedule(best_block_number);
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.

View File

@ -16,8 +16,9 @@
//! 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};
//TODO dr
/// Everything that an Engine needs to sign messages.
pub trait EngineSigner: Send + Sync {
@ -26,6 +27,12 @@ pub trait EngineSigner: Send + Sync {
/// Signing 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.
@ -43,6 +50,14 @@ impl EngineSigner for Signer {
fn address(&self) -> 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)]
@ -78,5 +93,18 @@ mod test_signer {
fn address(&self) -> Address {
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()
}
}
}

View File

@ -19,12 +19,14 @@
use std::sync::Weak;
use bytes::Bytes;
use ethereum_types::{Address, H256};
use ethereum_types::{Address, H256, U256};
use machine::{AuxiliaryData, Call, EthereumMachine};
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};
@ -35,34 +37,87 @@ pub struct ValidatorContract {
contract_address: Address,
validators: ValidatorSafeContract,
client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
posdao_transition: Option<BlockNumber>,
}
impl ValidatorContract {
pub fn new(contract_address: Address) -> Self {
pub fn new(contract_address: Address, posdao_transition: Option<BlockNumber>) -> Self {
ValidatorContract {
contract_address,
validators: ValidatorSafeContract::new(contract_address),
validators: ValidatorSafeContract::new(contract_address, posdao_transition),
client: RwLock::new(None),
posdao_transition,
}
}
}
impl ValidatorContract {
fn transact(&self, data: Bytes) -> Result<(), String> {
fn transact(
&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
.client
.read()
.as_ref()
.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() {
Some(c) => {
c.transact_contract(self.contract_address, data)
.map_err(|e| format!("Transaction import error: {}", e))?;
Ok(())
}
None => Err("No full client!".into()),
fn do_report_benign(&self, address: &Address, block: BlockNumber) -> Result<(), EthcoreError> {
let client = self
.client
.read()
.as_ref()
.and_then(Weak::upgrade)
.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)
}
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(
&self,
first: bool,
@ -127,19 +196,14 @@ impl ValidatorSet for ValidatorContract {
block: BlockNumber,
proof: Bytes,
) {
let data =
validator_report::functions::report_malicious::encode_input(*address, block, proof);
match self.transact(data) {
Ok(_) => warn!(target: "engine", "Reported malicious validator {}", address),
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
if let Err(s) = self.do_report_malicious(address, block, proof) {
warn!(target: "engine", "Validator {} could not be reported ({}) on block {}", address, s, block);
}
}
fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) {
let data = validator_report::functions::report_benign::encode_input(*address, block);
match self.transact(data) {
Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address),
Err(s) => warn!(target: "engine", "Validator {} could not be reported {}", address, s),
if let Err(s) = self.do_report_benign(address, block) {
warn!(target: "engine", "Validator {} could not be reported ({}) on block {}", address, s, block);
}
}
@ -155,7 +219,8 @@ mod tests {
use accounts::AccountProvider;
use bytes::ToPretty;
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 hash::keccak;
use miner::{self, MinerService};
@ -169,11 +234,8 @@ mod tests {
#[test]
fn fetches_validators() {
let client = generate_dummy_client_with_spec(Spec::new_validator_contract);
let vc = Arc::new(ValidatorContract::new(
"0000000000000000000000000000000000000005"
.parse::<Address>()
.unwrap(),
));
let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
let vc = Arc::new(ValidatorContract::new(addr, None));
vc.register_client(Arc::downgrade(&client) as _);
let last_hash = client.best_block_header().hash();
assert!(vc.contains(
@ -219,6 +281,8 @@ mod tests {
assert!(client.engine().verify_block_external(&header).is_err());
client.engine().step();
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.
let mut header = Header::default();
@ -232,7 +296,7 @@ mod tests {
// Seal a block.
client.engine().step();
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!(
client
.call_contract(
@ -254,10 +318,23 @@ mod tests {
client.engine().step();
client.engine().step();
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.
client
.transact_contract(Default::default(), Default::default())
.transact(TransactionRequest::call(
Default::default(),
Default::default(),
))
.unwrap();
client.engine().step();
client.engine().step();

View File

@ -33,31 +33,49 @@ use types::{header::Header, ids::BlockId, BlockNumber};
use client::EngineClient;
use error::Error as EthcoreError;
pub use self::simple_list::SimpleList;
#[cfg(test)]
pub use self::test::TestSet;
use self::{contract::ValidatorContract, multi::Multi, safe_contract::ValidatorSafeContract};
use super::SystemCall;
/// Creates a validator set from spec.
pub fn new_validator_set(spec: ValidatorSpec) -> Box<dyn ValidatorSet> {
/// Creates a validator set from the given spec and initializes a transition to POSDAO AuRa consensus.
pub fn new_validator_set_posdao(
spec: ValidatorSpec,
posdao_transition: Option<BlockNumber>,
) -> Box<dyn ValidatorSet> {
match spec {
ValidatorSpec::List(list) => {
Box::new(SimpleList::new(list.into_iter().map(Into::into).collect()))
}
ValidatorSpec::SafeContract(address) => {
Box::new(ValidatorSafeContract::new(address.into()))
ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(
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(
sequence
.into_iter()
.map(|(block, set)| (block.into(), new_validator_set(set)))
.map(|(block, set)| {
(
block.into(),
new_validator_set_posdao(set, posdao_transition),
)
})
.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.
pub trait ValidatorSet: Send + Sync + 'static {
/// 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.
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,
/// using underlying, default call mechanism.
fn contains(&self, parent: &H256, address: &Address) -> bool {

View File

@ -25,6 +25,7 @@ use types::{header::Header, ids::BlockId, BlockNumber};
use super::{SystemCall, ValidatorSet};
use client::EngineClient;
use error::Error as EthcoreError;
use machine::{AuxiliaryData, Call, EthereumMachine};
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> {
match self.block_number.read()(id)
.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())))
}
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(
&self,
_first: bool,
header: &Header,
call: &mut SystemCall,
) -> Result<(), ::error::Error> {
let (set_block, set) = self.correct_set_by_number(header.number());
let first = set_block == header.number();
set.on_epoch_begin(first, header, call)
) -> Result<(), EthcoreError> {
self.map_children(header, &mut |set: &dyn ValidatorSet, first| {
set.on_epoch_begin(first, header, call)
})
}
fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result<Vec<u8>, String> {
@ -182,7 +207,10 @@ impl ValidatorSet for Multi {
#[cfg(test)]
mod tests {
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 engines::{validator_set::ValidatorSet, EpochChange};
use ethereum_types::Address;
@ -190,7 +218,7 @@ mod tests {
use miner::{self, MinerService};
use spec::Spec;
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 verification::queue::kind::blocks::Unverified;
@ -216,7 +244,10 @@ mod tests {
let signer = Box::new((tap.clone(), v1, "".into()));
client.miner().set_author(miner::Author::Sealer(signer));
client
.transact_contract(Default::default(), Default::default())
.transact(TransactionRequest::call(
Default::default(),
Default::default(),
))
.unwrap();
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 0);
@ -227,7 +258,10 @@ mod tests {
assert_eq!(client.chain_info().best_block_number, 1);
// This time v0 is wrong.
client
.transact_contract(Default::default(), Default::default())
.transact(TransactionRequest::call(
Default::default(),
Default::default(),
))
.unwrap();
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 1);
@ -237,14 +271,16 @@ mod tests {
assert_eq!(client.chain_info().best_block_number, 2);
// v1 is still good.
client
.transact_contract(Default::default(), Default::default())
.transact(TransactionRequest::call(
Default::default(),
Default::default(),
))
.unwrap();
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 3);
// Check syncing.
let sync_client =
generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]);
let sync_client = generate_dummy_client_with_spec(Spec::new_validator_multi);
sync_client
.engine()
.register_client(Arc::downgrade(&sync_client) as _);

View File

@ -15,25 +15,39 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
/// 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 error::{Error as EthcoreError, ErrorKind as EthcoreErrorKind};
use ethabi::FunctionOutputDecoder;
use ethereum_types::{Address, Bloom, H256, U256};
use hash::keccak;
use kvdb::DBValue;
use memory_cache::MemoryLruCache;
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
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 super::{simple_list::SimpleList, SystemCall, ValidatorSet};
use client::EngineClient;
use client::{traits::TransactionRequest, BlockChainClient, EngineClient};
use machine::{AuxiliaryData, AuxiliaryRequest, Call, EthereumMachine};
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;
// TODO: ethabi should be able to generate this.
@ -71,6 +85,12 @@ pub struct ValidatorSafeContract {
contract_address: Address,
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
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.
@ -203,14 +223,54 @@ fn prove_initial(
}
impl ValidatorSafeContract {
pub fn new(contract_address: Address) -> Self {
pub fn new(contract_address: Address, posdao_transition: Option<BlockNumber>) -> Self {
ValidatorSafeContract {
contract_address,
validators: RwLock::new(MemoryLruCache::new(MEMOIZE_CAPACITY)),
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.
fn get_list(&self, caller: &Call) -> Option<SimpleList> {
let contract_address = self.contract_address;
@ -314,6 +374,110 @@ impl ValidatorSet for ValidatorSafeContract {
}) // 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(
&self,
_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)]
mod tests {
use super::{super::ValidatorSet, ValidatorSafeContract, EVENT_NAME_HASH};
@ -488,7 +714,7 @@ mod tests {
use rustc_hex::FromHex;
use spec::Spec;
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::{
ids::BlockId,
transaction::{Action, Transaction, TypedTransaction},
@ -498,11 +724,8 @@ mod tests {
#[test]
fn fetches_validators() {
let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract);
let vc = Arc::new(ValidatorSafeContract::new(
"0000000000000000000000000000000000000005"
.parse::<Address>()
.unwrap(),
));
let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
let vc = Arc::new(ValidatorSafeContract::new(addr, None));
vc.register_client(Arc::downgrade(&client) as _);
let last_hash = client.best_block_header().hash();
assert!(vc.contains(
@ -600,8 +823,7 @@ mod tests {
assert_eq!(client.chain_info().best_block_number, 3);
// Check syncing.
let sync_client =
generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]);
let sync_client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract);
sync_client
.engine()
.register_client(Arc::downgrade(&sync_client) as _);

View File

@ -18,7 +18,9 @@ use ethereum_types::{Address, H256};
/// Preconfigured validator list.
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 types::{header::Header, BlockNumber};
@ -63,6 +65,19 @@ impl ValidatorSet for SimpleList {
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>> {
match first {
true => Some(Vec::new()), // allow transition to fixed list, and instantly

View File

@ -26,11 +26,12 @@ use ethereum_types::{Address, H256};
use parity_util_mem::MallocSizeOf;
use types::{header::Header, BlockNumber};
use super::{SimpleList, ValidatorSet};
use super::{SimpleList, SystemCall, ValidatorSet};
use error::Error as EthcoreError;
use machine::{AuxiliaryData, Call, EthereumMachine};
/// Set used for testing with a single validator.
#[derive(MallocSizeOf)]
#[derive(Clone, MallocSizeOf)]
pub struct TestSet {
validator: SimpleList,
last_malicious: Arc<AtomicUsize>,
@ -54,6 +55,21 @@ impl TestSet {
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 {
@ -61,6 +77,19 @@ impl ValidatorSet for TestSet {
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>> {
None
}

View File

@ -21,6 +21,7 @@
extern crate ansi_term;
extern crate common_types as types;
extern crate crossbeam_utils;
extern crate derive_more;
extern crate ethabi;
extern crate ethash;
extern crate ethcore_blockchain as blockchain;

View File

@ -60,7 +60,7 @@ use client::{
BlockChain, BlockId, BlockProducer, ChainInfo, ClientIoMessage, Nonce, SealedBlockImporter,
TransactionId, TransactionInfo,
};
use engines::{EngineSigner, EthEngine, Seal};
use engines::{EngineSigner, EthEngine, Seal, SealingState};
use error::{Error, ErrorKind};
use executed::ExecutionError;
use executive::contract_address;
@ -288,7 +288,8 @@ impl Miner {
Miner {
sealing: Mutex::new(SealingWork {
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_mandatory_reseal: Instant::now() + options.reseal_max_period,
last_request: None,
@ -319,6 +320,17 @@ impl Miner {
///
/// NOTE This should be only used for tests.
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();
Miner::new(
MinerOptions {
@ -329,6 +341,7 @@ impl Miner {
no_early_reject: false,
},
reseal_min_period: Duration::from_secs(0),
force_sealing,
..Default::default()
},
GasPricer::new_fixed(minimal_gas_price),
@ -419,8 +432,8 @@ impl Miner {
trace_time!("prepare_block");
let chain_info = chain.chain_info();
// Open block
let (mut open_block, original_work_hash) = {
// Some engines add transactions to the block for their own purposes, e.g. AuthorityRound RANDAO.
let (mut open_block, original_work_hash, engine_txs) = {
let mut sealing = self.sealing.lock();
let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.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;
// otherwise, leave everything alone.
// otherwise, author a fresh block.
let mut open_block = match sealing
match sealing
.queue
.get_pending_if(|b| b.header.parent_hash() == &best_hash)
{
Some(old_block) => {
trace!(target: "miner", "prepare_block: Already have previous work; updating and returning");
// add transactions to old_block
chain.reopen_block(old_block)
(chain.reopen_block(old_block), last_work_hash, Vec::new())
}
None => {
// block not found - create it.
trace!(target: "miner", "prepare_block: No existing work - making new block");
let params = self.params.read().clone();
match chain.prepare_open_block(
let block = match chain.prepare_open_block(
params.author,
params.gas_range_target,
params.extra_data,
) {
Ok(block) => block,
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;
}
}
}
};
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 not_allowed_transactions = 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(),
pool::PendingSettings {
block_number: chain_info.best_block_number,
current_timestamp: chain_info.best_block_timestamp,
nonce_cap,
max_len: max_transactions,
max_len: max_transactions.saturating_sub(engine_txs.len()),
ordering: miner::PendingOrdering::Priority,
},
);
@ -517,12 +539,14 @@ impl Miner {
};
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 transaction = tx.signed().clone();
let hash = transaction.hash();
let sender = transaction.sender();
@ -670,7 +694,7 @@ impl Miner {
// keep sealing enabled if any of the conditions is met
let sealing_enabled = self.forced_sealing()
|| self.transaction_queue.has_local_pending_transactions()
|| self.engine.seals_internally() == Some(true)
|| self.engine.sealing_state() == SealingState::Ready
|| had_requests;
let should_disable_sealing = !sealing_enabled;
@ -679,7 +703,7 @@ impl Miner {
should_disable_sealing,
self.forced_sealing(),
self.transaction_queue.has_local_pending_transactions(),
self.engine.seals_internally(),
self.engine.sealing_state(),
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 {
// --------------------------------------------------------------------------
// | NOTE Code below requires sealing locks. |
@ -896,7 +925,9 @@ impl Miner {
fn prepare_and_update_sealing<C: miner::BlockChainClient>(&self, chain: &C) {
// Make sure to do it after transaction is imported and lock is dropped.
// 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
{
// 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;
}
fn set_author(&self, author: Author) {
self.params.write().author = author.address();
fn set_author<T: Into<Option<Author>>>(&self, author: T) {
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 {
if self.engine.seals_internally().is_some() {
// Enable sealing
self.sealing.lock().enabled = true;
// --------------------------------------------------------------------------
// | 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(signer);
} else {
warn!("Setting an EngineSigner while Engine does not require one.");
match author_opt {
Some(Author::Sealer(signer)) => {
if self.engine.sealing_state() != SealingState::External {
// Enable sealing
self.sealing.lock().enabled = true;
// --------------------------------------------------------------------------
// | 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(Some(signer));
} 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;
}
let sealing_state = self.engine.sealing_state();
if sealing_state == SealingState::NotReady {
return;
}
// --------------------------------------------------------------------------
// | NOTE Code below requires sealing locks. |
// | Make sure to release the locks before calling that method. |
@ -1277,20 +1330,23 @@ impl miner::MinerService for Miner {
}
}
match self.engine.seals_internally() {
Some(true) => {
match sealing_state {
SealingState::Ready => {
trace!(target: "miner", "update_sealing: engine indicates internal sealing");
if self.seal_and_import_block_internally(chain, block) {
trace!(target: "miner", "update_sealing: imported internally sealed block");
}
return;
}
Some(false) => {
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::NotReady => {
unreachable!("We returned right after sealing_state was computed. qed.")
}
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");
self.prepare_work(block, original_work_hash);
}
@ -1305,7 +1361,7 @@ impl miner::MinerService for Miner {
where
C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync,
{
if self.engine.seals_internally().is_some() {
if self.engine.sealing_state() != SealingState::External {
return None;
}

View File

@ -152,7 +152,7 @@ pub trait MinerService: Send + Sync {
/// 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.
fn set_author(&self, author: Author);
fn set_author<T: Into<Option<Author>>>(&self, author: T);
// Transaction Pool

View File

@ -1012,7 +1012,8 @@ mod tests {
#[test]
fn sends_async_messages() {
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 spec = Spec::new_test();

View File

@ -46,8 +46,13 @@ fn restored_is_equivalent() {
const TX_PER: usize = 5;
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
let client =
generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, TX_PER, &gas_prices);
let client = generate_dummy_client_with_spec_and_data(
Spec::new_null,
NUM_BLOCKS,
TX_PER,
&gas_prices,
false,
);
let tempdir = TempDir::new("").unwrap();
let client_db = tempdir.path().join("client_db");
@ -112,7 +117,8 @@ fn restored_is_equivalent() {
#[test]
fn guards_delete_folders() {
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 tempdir = TempDir::new("").unwrap();
@ -178,7 +184,7 @@ fn keep_ancient_blocks() {
let spec_f = Spec::new_null;
let spec = spec_f();
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();
@ -298,7 +304,7 @@ fn recover_aborted_recovery() {
const NUM_BLOCKS: u32 = 400;
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
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 tempdir = TempDir::new("oe_snapshot").unwrap();

View File

@ -1063,6 +1063,12 @@ impl Spec {
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
/// requiring work).
/// Accounts with secrets keccak("0") and keccak("1") are the validators.

View File

@ -112,7 +112,7 @@ pub fn create_test_block_with_data(
/// Generates dummy client (not test client) with corresponding amount of blocks
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
@ -126,6 +126,7 @@ pub fn generate_dummy_client_with_data(
block_number,
txs_per_block,
tx_gas_prices,
false,
)
}
@ -134,7 +135,7 @@ pub fn generate_dummy_client_with_spec<F>(test_spec: F) -> Arc<Client>
where
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
@ -143,6 +144,7 @@ pub fn generate_dummy_client_with_spec_and_data<F>(
block_number: u32,
txs_per_block: usize,
tx_gas_prices: &[U256],
force_sealing: bool,
) -> Arc<Client>
where
F: Fn() -> Spec,
@ -150,11 +152,13 @@ where
let test_spec = test_spec();
let client_db = new_db();
let miner = Miner::new_for_tests_force_sealing(&test_spec, None, force_sealing);
let client = Client::new(
ClientConfig::default(),
&test_spec,
client_db,
Arc::new(Miner::new_for_tests(&test_spec, None)),
Arc::new(miner),
IoChannel::disconnected(),
)
.unwrap();

View File

@ -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)]
pub struct Call {
/// 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)]
pub struct Create {
/// The address of the creator.

View File

@ -35,6 +35,10 @@ use_contract!(
"res/contracts/tx_acl_deprecated.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;
@ -99,6 +103,7 @@ impl TransactionFilter {
let sender = transaction.sender();
let value = transaction.tx().value;
let gas_price = transaction.tx().gas_price;
let key = (*parent_hash, sender);
if let Some(permissions) = permission_cache.get_mut(&key) {
@ -138,6 +143,24 @@ impl TransactionFilter {
(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");
(tx_permissions::NONE, true)
@ -161,10 +184,7 @@ impl TransactionFilter {
if filter_only_sender {
permission_cache.insert((*parent_hash, sender), permissions);
}
trace!(target: "tx_filter",
"Given transaction data: sender: {:?} to: {:?} value: {}. Permissions required: {:X}, got: {:X}",
sender, to, value, tx_type, permissions
);
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);
permissions & tx_type != 0
}
}
@ -185,7 +205,7 @@ mod test {
/// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
#[test]
fn transaction_filter() {
fn transaction_filter_ver_2() {
let spec_data = include_str!("../res/chainspec/test/contract_ver_2_genesis.json");
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
#[test]
fn transaction_filter_deprecated() {

View File

@ -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
for t in &block.transactions {
@ -358,25 +367,26 @@ pub fn verify_header_params(
found: *header.gas_used(),
})));
}
let min_gas_limit = engine.params().min_gas_limit;
if header.gas_limit() < &min_gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: Some(min_gas_limit),
max: None,
found: *header.gas_limit(),
})));
}
if let Some(limit) = engine.maximum_gas_limit() {
if header.gas_limit() > &limit {
return Err(From::from(::error::BlockError::InvalidGasLimit(
OutOfBounds {
if engine.gas_limit_override(header).is_none() {
let min_gas_limit = engine.min_gas_limit();
if header.gas_limit() < &min_gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: Some(min_gas_limit),
max: None,
found: *header.gas_limit(),
})));
}
if let Some(limit) = engine.maximum_gas_limit() {
if header.gas_limit() > &limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: None,
max: Some(limit),
found: *header.gas_limit(),
},
)));
})));
}
}
}
let maximum_extra_data_size = engine.maximum_extra_data_size();
if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size {
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"
);
let gas_limit_divisor = engine.params().gas_limit_bound_divisor;
if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) {
let now = SystemTime::now();
let min = CheckedSystemTime::checked_add(
@ -468,15 +476,18 @@ fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Re
.into());
}
let parent_gas_limit = *parent.gas_limit();
let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor;
let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor;
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: Some(min_gas),
max: Some(max_gas),
found: *header.gas_limit(),
})));
if engine.gas_limit_override(header).is_none() {
let gas_limit_divisor = engine.params().gas_limit_bound_divisor;
let parent_gas_limit = *parent.gas_limit();
let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor;
let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor;
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds {
min: Some(min_gas),
max: Some(max_gas),
found: *header.gas_limit(),
})));
}
}
Ok(())
@ -740,7 +751,7 @@ mod tests {
// that's an invalid transaction list rlp
let invalid_transactions = vec![vec![0u8]];
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_list::<Vec<u8>, _>(&invalid_transactions);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
@ -759,7 +770,7 @@ mod tests {
let spec = Spec::new_test();
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_timestamp(40);
good.set_number(10);

View File

@ -131,7 +131,7 @@ impl fmt::Display for Error {
InvalidChainId => "Transaction of this chain ID is not allowed on this chain.".into(),
InvalidSignature(ref err) => format!("Transaction has invalid signature: {}.", err),
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(),
InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err),

View File

@ -14,10 +14,33 @@
// You should have received a copy of the GNU General Public License
// 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 std::collections::BTreeMap;
/// Authority params deserialization.
#[derive(Debug, PartialEq, Deserialize)]
@ -25,7 +48,7 @@ use crate::{bytes::Bytes, hash::Address, uint::Uint};
#[serde(rename_all = "camelCase")]
pub struct AuthorityRoundParams {
/// Block duration, in seconds.
pub step_duration: Uint,
pub step_duration: StepDuration,
/// Valid authorities
pub validators: ValidatorSet,
/// Starting step. Determined automatically if not specified.
@ -39,11 +62,24 @@ pub struct AuthorityRoundParams {
pub immediate_transitions: Option<bool>,
/// Reward per block in wei.
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>,
/// Block reward contract address (setting the block reward contract
/// overrides the static block reward definition).
/// Block reward contract address which overrides the `block_reward` setting. This option allows
/// 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>,
/// 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.
pub block_reward_contract_code: Option<Bytes>,
/// Block at which maximum uncle count should be considered.
@ -56,13 +92,23 @@ pub struct AuthorityRoundParams {
pub maximum_empty_steps: Option<Uint>,
/// Strict validation of empty steps transition block.
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.
#[derive(Debug, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AuthorityRound {
/// Ethash params.
/// Authority Round parameters.
pub params: AuthorityRoundParams,
}
@ -73,7 +119,10 @@ mod tests {
use super::BlockReward;
use crate::{
hash::Address,
spec::{authority_round::AuthorityRound, validator_set::ValidatorSet},
spec::{
authority_round::AuthorityRound, step_duration::StepDuration,
validator_set::ValidatorSet,
},
uint::Uint,
};
use ethereum_types::{H160, U256};
@ -92,12 +141,23 @@ mod tests {
"validateStepTransition": 150,
"blockReward": 5000000,
"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();
assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02)));
assert_eq!(
deserialized.params.step_duration,
StepDuration::Single(Uint(U256::from(2)))
);
assert_eq!(
deserialized.params.validators,
ValidatorSet::List(vec![Address(
@ -115,9 +175,34 @@ mod tests {
Some(Uint(5.into()))
);
assert_eq!(
deserialized.params.block_reward,
Some(BlockReward::Single(Uint(5000000.into())))
)
deserialized.params.randomness_contract_address.unwrap(),
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]
@ -136,7 +221,10 @@ mod tests {
}"#;
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!(
deserialized.params.validators,
ValidatorSet::Contract(Address(

View File

@ -30,6 +30,7 @@ pub mod params;
pub mod seal;
pub mod spec;
pub mod state;
pub mod step_duration;
pub mod validator_set;
pub use self::{
@ -47,5 +48,6 @@ pub use self::{
seal::{AuthorityRoundSeal, Ethereum, Seal, TendermintSeal},
spec::{ForkSpec, Spec},
state::State,
step_duration::StepDuration,
validator_set::ValidatorSet,
};

View 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>),
}

View File

@ -17,7 +17,7 @@
use std::sync::Arc;
use accounts::AccountProvider;
use crypto::publickey::{self, Address};
use crypto::publickey::{self, Address, Error, Public};
use ethkey::Password;
/// 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 {
self.address
}
fn public(&self) -> Option<Public> {
self.accounts
.account_public(self.address, &self.password)
.ok()
}
}

View File

@ -157,6 +157,11 @@ where
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> {
match self.net.add_reserved_peer(peer) {
Ok(()) => Ok(true),

View File

@ -134,10 +134,16 @@ impl MinerService for TestMinerService {
self.authoring_params.read().clone()
}
fn set_author(&self, author: miner::Author) {
self.authoring_params.write().author = author.address();
if let miner::Author::Sealer(signer) = author {
*self.signer.write() = Some(signer);
fn set_author<T: Into<Option<miner::Author>>>(&self, author: T) {
let author_opt = author.into();
self.authoring_params.write().author = author_opt
.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,
}
}

View File

@ -57,6 +57,10 @@ pub trait ParitySet {
#[rpc(name = "parity_setEngineSignerSecret")]
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.
#[rpc(name = "parity_setTransactionsLimit")]
fn set_transactions_limit(&self, _: usize) -> Result<bool>;

View File

@ -28,6 +28,7 @@ extern crate parking_lot;
extern crate rustc_hex;
extern crate vm;
use bytes::Bytes;
use criterion::{black_box, Bencher, Criterion};
use ethereum_types::{Address, U256};
use evm::Factory;
@ -37,6 +38,28 @@ use vm::{tests::FakeExt, ActionParams, Ext, GasLeft, Result};
criterion_group!(
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_u256,
mem_gas_calculation_same_usize,
@ -206,3 +229,188 @@ fn result(r: Result<evm::GasLeft>) -> U256 {
_ => 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());
});
}