Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Block reward contract #8419

Merged
merged 8 commits into from
Apr 20, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions ethcore/res/authority_round_block_reward_contract.json
Original file line number Diff line number Diff line change
@@ -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": "6060604052341561000f57600080fd5b6102b88061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f91c289814610046575b600080fd5b341561005157600080fd5b610086600480803590602001908201803590602001919091929080359060200190820180359060200191909192905050610125565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100cd5780820151818401526020810190506100b2565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561010f5780820151818401526020810190506100f4565b5050505090500194505050505060405180910390f35b61012d610264565b610135610278565b61013d610278565b600073fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561018d57600080fd5b85859050888890501415156101a157600080fd5b878790506040518059106101b25750595b90808252806020026020018201604052509150600090505b815181101561021d5785858281811015156101e157fe5b9050602002013561ffff166103e80161ffff16828281518110151561020257fe5b906020019060200201818152505080806001019150506101ca565b878783828280806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050915090915093509350505094509492505050565b602060405190810160405280600081525090565b6020604051908101604052806000815250905600a165627a7a723058203ec48a056fdc2e3567414ab18ad3da7a272e23cc2ea7c5f9c8f38cbcf569fdaf0029"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's not possible to add a comment here linking to some source? IMO we should keep test specs as localized as possible instead of putting them all in the res folder. Then we can annotate the loading code with a link to the contract source.

}
}
}
29 changes: 29 additions & 0 deletions ethcore/res/contracts/block_reward.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
109 changes: 100 additions & 9 deletions ethcore/src/engines/authority_round/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ use account_provider::AccountProvider;
use block::*;
use client::EngineClient;
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
use engines::block_reward;
use engines::block_reward::{BlockRewardContract, RewardKind};
use error::{Error, BlockError};
use ethjson;
use machine::{AuxiliaryData, Call, EthereumMachine};
Expand Down Expand Up @@ -68,6 +70,8 @@ pub struct AuthorityRoundParams {
pub immediate_transitions: bool,
/// Block reward in base units.
pub block_reward: U256,
/// Block reward contract.
pub block_reward_contract: Option<BlockRewardContract>,
/// Number of accepted uncles transition block.
pub maximum_uncle_count_transition: u64,
/// Number of accepted uncles.
Expand Down Expand Up @@ -95,6 +99,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
validate_step_transition: p.validate_step_transition.map_or(0, Into::into),
immediate_transitions: p.immediate_transitions.unwrap_or(false),
block_reward: p.block_reward.map_or_else(Default::default, 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: 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)),
Expand Down Expand Up @@ -388,6 +393,7 @@ pub struct AuthorityRound {
epoch_manager: Mutex<EpochManager>,
immediate_transitions: bool,
block_reward: U256,
block_reward_contract: Option<BlockRewardContract>,
maximum_uncle_count_transition: u64,
maximum_uncle_count: usize,
empty_steps_transition: u64,
Expand Down Expand Up @@ -620,6 +626,7 @@ impl AuthorityRound {
epoch_manager: Mutex::new(EpochManager::blank()),
immediate_transitions: our_params.immediate_transitions,
block_reward: our_params.block_reward,
block_reward_contract: our_params.block_reward_contract,
maximum_uncle_count_transition: our_params.maximum_uncle_count_transition,
maximum_uncle_count: our_params.maximum_uncle_count,
empty_steps_transition: our_params.empty_steps_transition,
Expand Down Expand Up @@ -970,9 +977,7 @@ impl Engine<EthereumMachine> for AuthorityRound {

/// Apply the block reward on finalisation of the block.
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
use parity_machine::WithBalances;

let mut rewards = Vec::new();
let mut benefactors = Vec::new();
if block.header().number() >= self.empty_steps_transition {
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
Expand All @@ -998,17 +1003,33 @@ impl Engine<EthereumMachine> for AuthorityRound {

for empty_step in empty_steps {
let author = empty_step.author()?;
rewards.push((author, self.block_reward));
benefactors.push((author, RewardKind::EmptyStep));
}
}

let author = *block.header().author();
rewards.push((author, self.block_reward));
benefactors.push((author, RewardKind::Author));

let rewards = match self.block_reward_contract {
Some(ref c) => {
let mut call = |to, data| {
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))
};

for &(ref author, ref block_reward) in rewards.iter() {
self.machine.add_balance(block, author, block_reward)?;
}
self.machine.note_rewards(block, &rewards, &[])
c.reward(&benefactors, &mut call)?
},
None => {
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.
Expand Down Expand Up @@ -1521,6 +1542,7 @@ mod tests {
empty_steps_transition: u64::max_value(),
maximum_empty_steps: 0,
block_reward: Default::default(),
block_reward_contract: Default::default(),
};

let aura = {
Expand Down Expand Up @@ -1563,6 +1585,7 @@ mod tests {
empty_steps_transition: u64::max_value(),
maximum_empty_steps: 0,
block_reward: Default::default(),
block_reward_contract: Default::default(),
};

let aura = {
Expand Down Expand Up @@ -1617,6 +1640,7 @@ mod tests {
empty_steps_transition: u64::max_value(),
maximum_empty_steps: 0,
block_reward: Default::default(),
block_reward_contract: Default::default(),
};

let mut c_params = ::spec::CommonParams::default();
Expand Down Expand Up @@ -1894,4 +1918,71 @@ mod tests {
_ => 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(),
)
}
}
Loading