Block reward contract (#8419)
* engine: add block reward contract abi and helper client * aura: add support for block reward contract * engine: test block reward contract client * aura: test block reward contract * engine + aura: add missing docs * engine: share SystemCall type alias * aura: add transition for block reward contract * engine: fix example block reward contract source link and bytecode
This commit is contained in:
parent
9c5e35548d
commit
24f6d8296b
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -462,7 +462,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethabi-contract"
|
name = "ethabi-contract"
|
||||||
version = "5.0.3"
|
version = "5.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -514,7 +514,7 @@ dependencies = [
|
|||||||
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethash 1.11.0",
|
"ethash 1.11.0",
|
||||||
"ethcore-bloom-journal 0.1.0",
|
"ethcore-bloom-journal 0.1.0",
|
||||||
@ -745,7 +745,7 @@ version = "1.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.11.0",
|
"ethcore 1.11.0",
|
||||||
"ethcore-bytes 0.1.0",
|
"ethcore-bytes 0.1.0",
|
||||||
@ -780,7 +780,7 @@ version = "1.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.11.0",
|
"ethcore 1.11.0",
|
||||||
"ethcore-bytes 0.1.0",
|
"ethcore-bytes 0.1.0",
|
||||||
@ -1786,7 +1786,7 @@ name = "node-filter"
|
|||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.11.0",
|
"ethcore 1.11.0",
|
||||||
"ethcore-io 1.11.0",
|
"ethcore-io 1.11.0",
|
||||||
@ -2084,7 +2084,7 @@ name = "parity-hash-fetch"
|
|||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-bytes 0.1.0",
|
"ethcore-bytes 0.1.0",
|
||||||
"ethereum-types 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethereum-types 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -2305,7 +2305,7 @@ name = "parity-updater"
|
|||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore 1.11.0",
|
"ethcore 1.11.0",
|
||||||
"ethcore-bytes 0.1.0",
|
"ethcore-bytes 0.1.0",
|
||||||
@ -2708,7 +2708,7 @@ name = "registrar"
|
|||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"keccak-hash 0.1.0",
|
"keccak-hash 0.1.0",
|
||||||
@ -3815,7 +3815,7 @@ dependencies = [
|
|||||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||||
"checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
"checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
||||||
"checksum ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05e33a914b94b763f0a92333e4e5c95c095563f06ef7d6b295b3d3c2cf31e21f"
|
"checksum ethabi 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05e33a914b94b763f0a92333e4e5c95c095563f06ef7d6b295b3d3c2cf31e21f"
|
||||||
"checksum ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca2263c24359e827348ac99aa1f2e28ba5bab0d6c0b83941fa252de8a9e9c073"
|
"checksum ethabi-contract 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "210c9e21d164c15b6ef64fe601e0e12a3c84a031d5ef558e38463e53edbd22ed"
|
||||||
"checksum ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2bc7099baa147187aedaecd9fe04a6c0541c82bc43ff317cb6900fe2b983d74"
|
"checksum ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2bc7099baa147187aedaecd9fe04a6c0541c82bc43ff317cb6900fe2b983d74"
|
||||||
"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386"
|
"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386"
|
||||||
"checksum ethereum-types 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3ae691a36ce5d25b433e63128ce5579f4a18457b6a9c849832b2c9e0fec92a"
|
"checksum ethereum-types 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3ae691a36ce5d25b433e63128ce5579f4a18457b6a9c849832b2c9e0fec92a"
|
||||||
|
61
ethcore/res/authority_round_block_reward_contract.json
Normal file
61
ethcore/res/authority_round_block_reward_contract.json
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"name": "TestAuthorityRoundBlockRewardContract",
|
||||||
|
"engine": {
|
||||||
|
"authorityRound": {
|
||||||
|
"params": {
|
||||||
|
"stepDuration": 1,
|
||||||
|
"startStep": 2,
|
||||||
|
"validators": {
|
||||||
|
"list": [
|
||||||
|
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
|
||||||
|
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"immediateTransitions": true,
|
||||||
|
"emptyStepsTransition": "1",
|
||||||
|
"maximumEmptySteps": "2",
|
||||||
|
"blockRewardContractAddress": "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": {
|
||||||
|
"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", "activate_at": 0, "pricing": { "linear": { "base": 500, "word": 0 } } } },
|
||||||
|
"0000000000000000000000000000000000000007": { "balance": "1", "builtin": { "name": "alt_bn128_mul", "activate_at": 0, "pricing": { "linear": { "base": 40000, "word": 0 } } } },
|
||||||
|
"0000000000000000000000000000000000000008": { "balance": "1", "builtin": { "name": "alt_bn128_pairing", "activate_at": 0, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } },
|
||||||
|
"9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" },
|
||||||
|
"0000000000000000000000000000000000000042": {
|
||||||
|
"balance": "1",
|
||||||
|
"constructor": "6060604052341561000f57600080fd5b6102b88061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f91c289814610046575b600080fd5b341561005157600080fd5b610086600480803590602001908201803590602001919091929080359060200190820180359060200191909192905050610125565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100cd5780820151818401526020810190506100b2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561010f5780820151818401526020810190506100f4565b5050505090500194505050505060405180910390f35b61012d610264565b610135610278565b61013d610278565b600073fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561018d57600080fd5b85859050888890501415156101a157600080fd5b878790506040518059106101b25750595b90808252806020026020018201604052509150600090505b815181101561021d5785858281811015156101e157fe5b9050602002013561ffff166103e80161ffff16828281518110151561020257fe5b906020019060200201818152505080806001019150506101ca565b878783828280806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050915090915093509350505094509492505050565b602060405190810160405280600081525090565b6020604051908101604052806000815250905600a165627a7a723058201da0f164e75517fb8baf51f030b904032cb748334938e7386f63025bfb23f3de0029"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
ethcore/res/contracts/block_reward.json
Normal file
29
ethcore/res/contracts/block_reward.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "benefactors",
|
||||||
|
"type": "address[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kind",
|
||||||
|
"type": "uint16[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "reward",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "address[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "uint256[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]
|
@ -27,6 +27,8 @@ use account_provider::AccountProvider;
|
|||||||
use block::*;
|
use block::*;
|
||||||
use client::EngineClient;
|
use client::EngineClient;
|
||||||
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
|
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
|
||||||
|
use engines::block_reward;
|
||||||
|
use engines::block_reward::{BlockRewardContract, RewardKind};
|
||||||
use error::{Error, BlockError};
|
use error::{Error, BlockError};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use machine::{AuxiliaryData, Call, EthereumMachine};
|
use machine::{AuxiliaryData, Call, EthereumMachine};
|
||||||
@ -68,6 +70,10 @@ pub struct AuthorityRoundParams {
|
|||||||
pub immediate_transitions: bool,
|
pub immediate_transitions: bool,
|
||||||
/// Block reward in base units.
|
/// Block reward in base units.
|
||||||
pub block_reward: U256,
|
pub block_reward: U256,
|
||||||
|
/// Block reward contract transition block.
|
||||||
|
pub block_reward_contract_transition: u64,
|
||||||
|
/// Block reward contract.
|
||||||
|
pub block_reward_contract: Option<BlockRewardContract>,
|
||||||
/// Number of accepted uncles transition block.
|
/// Number of accepted uncles transition block.
|
||||||
pub maximum_uncle_count_transition: u64,
|
pub maximum_uncle_count_transition: u64,
|
||||||
/// Number of accepted uncles.
|
/// Number of accepted uncles.
|
||||||
@ -95,6 +101,8 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
|||||||
validate_step_transition: p.validate_step_transition.map_or(0, Into::into),
|
validate_step_transition: p.validate_step_transition.map_or(0, Into::into),
|
||||||
immediate_transitions: p.immediate_transitions.unwrap_or(false),
|
immediate_transitions: p.immediate_transitions.unwrap_or(false),
|
||||||
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
|
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
|
||||||
|
block_reward_contract_transition: p.block_reward_contract_transition.map_or(0, Into::into),
|
||||||
|
block_reward_contract: p.block_reward_contract_address.map(BlockRewardContract::new),
|
||||||
maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into),
|
maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into),
|
||||||
maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into),
|
maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into),
|
||||||
empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)),
|
empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)),
|
||||||
@ -388,6 +396,8 @@ pub struct AuthorityRound {
|
|||||||
epoch_manager: Mutex<EpochManager>,
|
epoch_manager: Mutex<EpochManager>,
|
||||||
immediate_transitions: bool,
|
immediate_transitions: bool,
|
||||||
block_reward: U256,
|
block_reward: U256,
|
||||||
|
block_reward_contract_transition: u64,
|
||||||
|
block_reward_contract: Option<BlockRewardContract>,
|
||||||
maximum_uncle_count_transition: u64,
|
maximum_uncle_count_transition: u64,
|
||||||
maximum_uncle_count: usize,
|
maximum_uncle_count: usize,
|
||||||
empty_steps_transition: u64,
|
empty_steps_transition: u64,
|
||||||
@ -620,6 +630,8 @@ impl AuthorityRound {
|
|||||||
epoch_manager: Mutex::new(EpochManager::blank()),
|
epoch_manager: Mutex::new(EpochManager::blank()),
|
||||||
immediate_transitions: our_params.immediate_transitions,
|
immediate_transitions: our_params.immediate_transitions,
|
||||||
block_reward: our_params.block_reward,
|
block_reward: our_params.block_reward,
|
||||||
|
block_reward_contract_transition: our_params.block_reward_contract_transition,
|
||||||
|
block_reward_contract: our_params.block_reward_contract,
|
||||||
maximum_uncle_count_transition: our_params.maximum_uncle_count_transition,
|
maximum_uncle_count_transition: our_params.maximum_uncle_count_transition,
|
||||||
maximum_uncle_count: our_params.maximum_uncle_count,
|
maximum_uncle_count: our_params.maximum_uncle_count,
|
||||||
empty_steps_transition: our_params.empty_steps_transition,
|
empty_steps_transition: our_params.empty_steps_transition,
|
||||||
@ -970,9 +982,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
|||||||
|
|
||||||
/// Apply the block reward on finalisation of the block.
|
/// Apply the block reward on finalisation of the block.
|
||||||
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
|
||||||
use parity_machine::WithBalances;
|
let mut benefactors = Vec::new();
|
||||||
|
|
||||||
let mut rewards = Vec::new();
|
|
||||||
if block.header().number() >= self.empty_steps_transition {
|
if block.header().number() >= self.empty_steps_transition {
|
||||||
let empty_steps = if block.header().seal().is_empty() {
|
let empty_steps = if block.header().seal().is_empty() {
|
||||||
// this is a new block, calculate rewards based on the empty steps messages we have accumulated
|
// this is a new block, calculate rewards based on the empty steps messages we have accumulated
|
||||||
@ -998,17 +1008,33 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
|||||||
|
|
||||||
for empty_step in empty_steps {
|
for empty_step in empty_steps {
|
||||||
let author = empty_step.author()?;
|
let author = empty_step.author()?;
|
||||||
rewards.push((author, self.block_reward));
|
benefactors.push((author, RewardKind::EmptyStep));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let author = *block.header().author();
|
let author = *block.header().author();
|
||||||
rewards.push((author, self.block_reward));
|
benefactors.push((author, RewardKind::Author));
|
||||||
|
|
||||||
for &(ref author, ref block_reward) in rewards.iter() {
|
let rewards = match self.block_reward_contract {
|
||||||
self.machine.add_balance(block, author, block_reward)?;
|
Some(ref c) if block.header().number() >= self.block_reward_contract_transition => {
|
||||||
}
|
let mut call = |to, data| {
|
||||||
self.machine.note_rewards(block, &rewards, &[])
|
let result = self.machine.execute_as_system(
|
||||||
|
block,
|
||||||
|
to,
|
||||||
|
U256::max_value(), // unbounded gas? maybe make configurable.
|
||||||
|
Some(data),
|
||||||
|
);
|
||||||
|
result.map_err(|e| format!("{}", e))
|
||||||
|
};
|
||||||
|
|
||||||
|
c.reward(&benefactors, &mut call)?
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
benefactors.into_iter().map(|(author, _)| (author, self.block_reward)).collect()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
block_reward::apply_block_rewards(&rewards, block, &self.machine)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check the number of seal fields.
|
/// Check the number of seal fields.
|
||||||
@ -1521,6 +1547,8 @@ mod tests {
|
|||||||
empty_steps_transition: u64::max_value(),
|
empty_steps_transition: u64::max_value(),
|
||||||
maximum_empty_steps: 0,
|
maximum_empty_steps: 0,
|
||||||
block_reward: Default::default(),
|
block_reward: Default::default(),
|
||||||
|
block_reward_contract_transition: 0,
|
||||||
|
block_reward_contract: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let aura = {
|
let aura = {
|
||||||
@ -1563,6 +1591,8 @@ mod tests {
|
|||||||
empty_steps_transition: u64::max_value(),
|
empty_steps_transition: u64::max_value(),
|
||||||
maximum_empty_steps: 0,
|
maximum_empty_steps: 0,
|
||||||
block_reward: Default::default(),
|
block_reward: Default::default(),
|
||||||
|
block_reward_contract_transition: 0,
|
||||||
|
block_reward_contract: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let aura = {
|
let aura = {
|
||||||
@ -1617,6 +1647,8 @@ mod tests {
|
|||||||
empty_steps_transition: u64::max_value(),
|
empty_steps_transition: u64::max_value(),
|
||||||
maximum_empty_steps: 0,
|
maximum_empty_steps: 0,
|
||||||
block_reward: Default::default(),
|
block_reward: Default::default(),
|
||||||
|
block_reward_contract_transition: 0,
|
||||||
|
block_reward_contract: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut c_params = ::spec::CommonParams::default();
|
let mut c_params = ::spec::CommonParams::default();
|
||||||
@ -1894,4 +1926,71 @@ mod tests {
|
|||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_reward_contract() {
|
||||||
|
let spec = Spec::new_test_round_block_reward_contract();
|
||||||
|
let tap = Arc::new(AccountProvider::transient_provider());
|
||||||
|
|
||||||
|
let addr1 = tap.insert_account(keccak("1").into(), "1").unwrap();
|
||||||
|
|
||||||
|
let engine = &*spec.engine;
|
||||||
|
let genesis_header = spec.genesis_header();
|
||||||
|
let db1 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||||
|
let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||||
|
|
||||||
|
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||||
|
|
||||||
|
let client = generate_dummy_client_with_spec_and_accounts(
|
||||||
|
Spec::new_test_round_block_reward_contract,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
engine.register_client(Arc::downgrade(&client) as _);
|
||||||
|
|
||||||
|
// step 2
|
||||||
|
let b1 = OpenBlock::new(
|
||||||
|
engine,
|
||||||
|
Default::default(),
|
||||||
|
false,
|
||||||
|
db1,
|
||||||
|
&genesis_header,
|
||||||
|
last_hashes.clone(),
|
||||||
|
addr1,
|
||||||
|
(3141562.into(), 31415620.into()),
|
||||||
|
vec![],
|
||||||
|
false,
|
||||||
|
).unwrap();
|
||||||
|
let b1 = b1.close_and_lock();
|
||||||
|
|
||||||
|
// since the block is empty it isn't sealed and we generate empty steps
|
||||||
|
engine.set_signer(tap.clone(), addr1, "1".into());
|
||||||
|
assert_eq!(engine.generate_seal(b1.block(), &genesis_header), Seal::None);
|
||||||
|
engine.step();
|
||||||
|
|
||||||
|
// step 3
|
||||||
|
// the signer of the accumulated empty step message should be rewarded
|
||||||
|
let b2 = OpenBlock::new(
|
||||||
|
engine,
|
||||||
|
Default::default(),
|
||||||
|
false,
|
||||||
|
db2,
|
||||||
|
&genesis_header,
|
||||||
|
last_hashes.clone(),
|
||||||
|
addr1,
|
||||||
|
(3141562.into(), 31415620.into()),
|
||||||
|
vec![],
|
||||||
|
false,
|
||||||
|
).unwrap();
|
||||||
|
let addr1_balance = b2.block().state().balance(&addr1).unwrap();
|
||||||
|
|
||||||
|
// after closing the block `addr1` should be reward twice, one for the included empty step
|
||||||
|
// message and another for block creation
|
||||||
|
let b2 = b2.close_and_lock();
|
||||||
|
|
||||||
|
// the contract rewards (1000 + kind) for each benefactor/reward kind
|
||||||
|
assert_eq!(
|
||||||
|
b2.block().state().balance(&addr1).unwrap(),
|
||||||
|
addr1_balance + (1000 + 0).into() + (1000 + 2).into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
184
ethcore/src/engines/block_reward.rs
Normal file
184
ethcore/src/engines/block_reward.rs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// Copyright 2015-2018 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/>.
|
||||||
|
|
||||||
|
use ethabi;
|
||||||
|
use ethabi::ParamType;
|
||||||
|
use ethereum_types::{H160, Address, U256};
|
||||||
|
|
||||||
|
use block::ExecutedBlock;
|
||||||
|
use error::Error;
|
||||||
|
use machine::EthereumMachine;
|
||||||
|
use super::SystemCall;
|
||||||
|
|
||||||
|
use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json");
|
||||||
|
|
||||||
|
/// The kind of block reward.
|
||||||
|
/// Depending on the consensus engine the allocated block reward might have
|
||||||
|
/// different semantics which could lead e.g. to different reward values.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum RewardKind {
|
||||||
|
/// Reward attributed to the block author.
|
||||||
|
Author = 0,
|
||||||
|
/// Reward attributed to the block uncle(s).
|
||||||
|
Uncle = 1,
|
||||||
|
/// Reward attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine).
|
||||||
|
EmptyStep = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RewardKind> for u16 {
|
||||||
|
fn from(reward_kind: RewardKind) -> Self {
|
||||||
|
reward_kind as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A client for the block reward contract.
|
||||||
|
pub struct BlockRewardContract {
|
||||||
|
/// Address of the contract.
|
||||||
|
address: Address,
|
||||||
|
block_reward_contract: block_reward_contract::BlockReward,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockRewardContract {
|
||||||
|
/// Create a new block reward contract client targeting the given address.
|
||||||
|
pub fn new(address: Address) -> BlockRewardContract {
|
||||||
|
BlockRewardContract {
|
||||||
|
address,
|
||||||
|
block_reward_contract: block_reward_contract::BlockReward::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the block reward contract with the given benefactors list (and associated reward kind)
|
||||||
|
/// and returns the reward allocation (address - value). The block reward contract *must* be
|
||||||
|
/// called by the system address so the `caller` must ensure that (e.g. using
|
||||||
|
/// `machine.execute_as_system`).
|
||||||
|
pub fn reward(
|
||||||
|
&self,
|
||||||
|
benefactors: &[(Address, RewardKind)],
|
||||||
|
caller: &mut SystemCall,
|
||||||
|
) -> Result<Vec<(Address, U256)>, Error> {
|
||||||
|
let reward = self.block_reward_contract.functions().reward();
|
||||||
|
|
||||||
|
let input = reward.input(
|
||||||
|
benefactors.iter().map(|&(address, _)| H160::from(address)),
|
||||||
|
benefactors.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = caller(self.address, input)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(::engines::EngineError::FailedSystemCall)?;
|
||||||
|
|
||||||
|
// since this is a non-constant call we can't use ethabi's function output
|
||||||
|
// deserialization, sadness ensues.
|
||||||
|
let types = &[
|
||||||
|
ParamType::Array(Box::new(ParamType::Address)),
|
||||||
|
ParamType::Array(Box::new(ParamType::Uint(256))),
|
||||||
|
];
|
||||||
|
|
||||||
|
let tokens = ethabi::decode(types, &output)
|
||||||
|
.map_err(|err| err.to_string())
|
||||||
|
.map_err(::engines::EngineError::FailedSystemCall)?;
|
||||||
|
|
||||||
|
assert!(tokens.len() == 2);
|
||||||
|
|
||||||
|
let addresses = tokens[0].clone().to_array().expect("type checked by ethabi::decode; qed");
|
||||||
|
let rewards = tokens[1].clone().to_array().expect("type checked by ethabi::decode; qed");
|
||||||
|
|
||||||
|
if addresses.len() != rewards.len() {
|
||||||
|
return Err(::engines::EngineError::FailedSystemCall(
|
||||||
|
"invalid data returned by reward contract: both arrays must have the same size".into()
|
||||||
|
).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let addresses = addresses.into_iter().map(|t| t.to_address().expect("type checked by ethabi::decode; qed"));
|
||||||
|
let rewards = rewards.into_iter().map(|t| t.to_uint().expect("type checked by ethabi::decode; qed"));
|
||||||
|
|
||||||
|
Ok(addresses.zip(rewards).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies the given block rewards, i.e. adds the given balance to each benefactors' address.
|
||||||
|
/// If tracing is enabled the operations are recorded.
|
||||||
|
pub fn apply_block_rewards(rewards: &[(Address, U256)], block: &mut ExecutedBlock, machine: &EthereumMachine) -> Result<(), Error> {
|
||||||
|
use parity_machine::WithBalances;
|
||||||
|
|
||||||
|
for &(ref author, ref block_reward) in rewards {
|
||||||
|
machine.add_balance(block, author, block_reward)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
machine.note_rewards(block, &rewards, &[])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use client::PrepareOpenBlock;
|
||||||
|
use ethereum_types::U256;
|
||||||
|
use spec::Spec;
|
||||||
|
use test_helpers::generate_dummy_client_with_spec_and_accounts;
|
||||||
|
|
||||||
|
use super::{BlockRewardContract, RewardKind};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_reward_contract() {
|
||||||
|
let client = generate_dummy_client_with_spec_and_accounts(
|
||||||
|
Spec::new_test_round_block_reward_contract,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let machine = Spec::new_test_machine();
|
||||||
|
|
||||||
|
// the spec has a block reward contract defined at the given address
|
||||||
|
let block_reward_contract = BlockRewardContract::new(
|
||||||
|
"0000000000000000000000000000000000000042".into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut call = |to, data| {
|
||||||
|
let mut block = client.prepare_open_block(
|
||||||
|
"0000000000000000000000000000000000000001".into(),
|
||||||
|
(3141562.into(), 31415620.into()),
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = machine.execute_as_system(
|
||||||
|
block.block_mut(),
|
||||||
|
to,
|
||||||
|
U256::max_value(),
|
||||||
|
Some(data),
|
||||||
|
);
|
||||||
|
|
||||||
|
result.map_err(|e| format!("{}", e))
|
||||||
|
};
|
||||||
|
|
||||||
|
// if no benefactors are given no rewards are attributed
|
||||||
|
assert!(block_reward_contract.reward(&vec![], &mut call).unwrap().is_empty());
|
||||||
|
|
||||||
|
// the contract rewards (1000 + kind) for each benefactor
|
||||||
|
let benefactors = vec![
|
||||||
|
("0000000000000000000000000000000000000033".into(), RewardKind::Author),
|
||||||
|
("0000000000000000000000000000000000000034".into(), RewardKind::Uncle),
|
||||||
|
("0000000000000000000000000000000000000035".into(), RewardKind::EmptyStep),
|
||||||
|
];
|
||||||
|
|
||||||
|
let rewards = block_reward_contract.reward(&benefactors, &mut call).unwrap();
|
||||||
|
let expected = vec![
|
||||||
|
("0000000000000000000000000000000000000033".into(), U256::from(1000)),
|
||||||
|
("0000000000000000000000000000000000000034".into(), U256::from(1000 + 1)),
|
||||||
|
("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(expected, rewards);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
mod authority_round;
|
mod authority_round;
|
||||||
mod basic_authority;
|
mod basic_authority;
|
||||||
|
mod block_reward;
|
||||||
mod instant_seal;
|
mod instant_seal;
|
||||||
mod null_engine;
|
mod null_engine;
|
||||||
mod signer;
|
mod signer;
|
||||||
@ -56,7 +57,7 @@ use ethereum_types::{H256, U256, Address};
|
|||||||
use unexpected::{Mismatch, OutOfBounds};
|
use unexpected::{Mismatch, OutOfBounds};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
/// Default EIP-210 contrat code.
|
/// Default EIP-210 contract code.
|
||||||
/// As defined in https://github.com/ethereum/EIPs/pull/210
|
/// As defined in https://github.com/ethereum/EIPs/pull/210
|
||||||
pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b";
|
pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b";
|
||||||
|
|
||||||
@ -119,6 +120,9 @@ pub enum Seal {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A system-calling closure. Enacts calls on a block's state from the system address.
|
||||||
|
pub type SystemCall<'a> = FnMut(Address, Vec<u8>) -> Result<Vec<u8>, String> + 'a;
|
||||||
|
|
||||||
/// Type alias for a function we can get headers by hash through.
|
/// Type alias for a function we can get headers by hash through.
|
||||||
pub type Headers<'a, H> = Fn(H256) -> Option<H> + 'a;
|
pub type Headers<'a, H> = Fn(H256) -> Option<H> + 'a;
|
||||||
|
|
||||||
|
@ -38,9 +38,7 @@ pub use self::simple_list::SimpleList;
|
|||||||
use self::contract::ValidatorContract;
|
use self::contract::ValidatorContract;
|
||||||
use self::safe_contract::ValidatorSafeContract;
|
use self::safe_contract::ValidatorSafeContract;
|
||||||
use self::multi::Multi;
|
use self::multi::Multi;
|
||||||
|
use super::SystemCall;
|
||||||
/// A system-calling closure. Enacts calls on a block's state from the system address.
|
|
||||||
pub type SystemCall<'a> = FnMut(Address, Bytes) -> Result<Bytes, String> + 'a;
|
|
||||||
|
|
||||||
/// Creates a validator set from spec.
|
/// Creates a validator set from spec.
|
||||||
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
|
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
|
||||||
|
@ -848,6 +848,13 @@ impl Spec {
|
|||||||
load_bundled!("authority_round_empty_steps")
|
load_bundled!("authority_round_empty_steps")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new Spec with AuthorityRound consensus (with empty steps) using a block reward
|
||||||
|
/// contract. The contract source code can be found at:
|
||||||
|
/// https://github.com/parity-contracts/block-reward/blob/daf7d44383b6cdb11cb6b953b018648e2b027cfb/contracts/ExampleBlockReward.sol
|
||||||
|
pub fn new_test_round_block_reward_contract() -> Self {
|
||||||
|
load_bundled!("authority_round_block_reward_contract")
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring
|
/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring
|
||||||
/// work).
|
/// work).
|
||||||
/// Account keccak("0") and keccak("1") are a authorities.
|
/// Account keccak("0") and keccak("1") are a authorities.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
//! Authority params deserialization.
|
//! Authority params deserialization.
|
||||||
|
|
||||||
|
use ethereum_types::Address;
|
||||||
use uint::Uint;
|
use uint::Uint;
|
||||||
use super::ValidatorSet;
|
use super::ValidatorSet;
|
||||||
|
|
||||||
@ -43,6 +44,13 @@ pub struct AuthorityRoundParams {
|
|||||||
/// Reward per block in wei.
|
/// Reward per block in wei.
|
||||||
#[serde(rename="blockReward")]
|
#[serde(rename="blockReward")]
|
||||||
pub block_reward: Option<Uint>,
|
pub block_reward: Option<Uint>,
|
||||||
|
/// Block at which the block reward contract should start being used.
|
||||||
|
#[serde(rename="blockRewardContractTransition")]
|
||||||
|
pub block_reward_contract_transition: Option<Uint>,
|
||||||
|
/// Block reward contract address (setting the block reward contract
|
||||||
|
/// overrides the static block reward definition).
|
||||||
|
#[serde(rename="blockRewardContractAddress")]
|
||||||
|
pub block_reward_contract_address: Option<Address>,
|
||||||
/// Block at which maximum uncle count should be considered.
|
/// Block at which maximum uncle count should be considered.
|
||||||
#[serde(rename="maximumUncleCountTransition")]
|
#[serde(rename="maximumUncleCountTransition")]
|
||||||
pub maximum_uncle_count_transition: Option<Uint>,
|
pub maximum_uncle_count_transition: Option<Uint>,
|
||||||
|
Loading…
Reference in New Issue
Block a user