From ef344218f9a08e530b1a4de64f24453ac2c1c082 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Mon, 28 Jan 2019 12:00:33 -0500 Subject: [PATCH] Call `emitInitiateChange` at each block if needed It is now called by the new method `ValidatorSet::on_new_block()`. Closes #71 --- ethcore/res/contracts/validator_set_aura.json | 1050 +++++++++++++++++ ethcore/src/engines/authority_round/mod.rs | 26 +- ethcore/src/engines/validator_set/contract.rs | 6 + ethcore/src/engines/validator_set/mod.rs | 9 + ethcore/src/engines/validator_set/multi.rs | 17 +- .../engines/validator_set/safe_contract.rs | 41 +- 6 files changed, 1127 insertions(+), 22 deletions(-) create mode 100644 ethcore/res/contracts/validator_set_aura.json diff --git a/ethcore/res/contracts/validator_set_aura.json b/ethcore/res/contracts/validator_set_aura.json new file mode 100644 index 00000000000..257c29873d0 --- /dev/null +++ b/ethcore/res/contracts/validator_set_aura.json @@ -0,0 +1,1050 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "_pool", + "type": "address" + }, + { + "name": "_delegator", + "type": "address" + } + ], + "name": "poolDelegatorIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_pool", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + }, + { + "name": "_stakingEpoch", + "type": "uint256" + } + ], + "name": "stakeAmountByEpoch", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initiateChangeAllowed", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_erc20TokenContract", + "type": "address" + } + ], + "name": "setErc20TokenContract", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_fromPool", + "type": "address" + }, + { + "name": "_toPool", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "moveStake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_minStake", + "type": "uint256" + } + ], + "name": "setDelegatorMinStake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_who", + "type": "address" + } + ], + "name": "isValidatorOnPreviousEpoch", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "emitInitiateChangeCallable", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "removePool", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_minStake", + "type": "uint256" + } + ], + "name": "setCandidateMinStake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "name": "stakeAmountTotal", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "blockRewardContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_who", + "type": "address" + } + ], + "name": "bannedUntil", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_who", + "type": "address" + } + ], + "name": "poolInactiveIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPools", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getQueueValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + }, + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_pool", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "maxWithdrawAllowed", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_VALIDATORS", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "finalizeChange", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakingEpoch", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getDelegatorMinStake", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPreviousValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "erc20TokenContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getCandidateMinStake", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_validator", + "type": "address" + } + ], + "name": "isReportValidatorValid", + "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": "_validator", + "type": "address" + } + ], + "name": "validatorIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_pool", + "type": "address" + } + ], + "name": "poolDelegators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_pool", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "stakeAmount", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_who", + "type": "address" + } + ], + "name": "isPoolActive", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_validator", + "type": "address" + } + ], + "name": "isValidatorBanned", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_toPool", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_who", + "type": "address" + } + ], + "name": "poolIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_who", + "type": "address" + } + ], + "name": "doesPoolExist", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "STAKE_UNIT", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "validatorSetApplyBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_pool", + "type": "address" + }, + { + "name": "_staker", + "type": "address[]" + }, + { + "name": "_stakingEpoch", + "type": "uint256" + } + ], + "name": "clearStakeHistory", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "randomContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "changeRequestCount", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPoolsInactive", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPendingValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_CANDIDATES", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_fromPool", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_who", + "type": "address" + } + ], + "name": "isValidator", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "reportingValidator", + "type": "address" + }, + { + "indexed": false, + "name": "maliciousValidator", + "type": "address" + }, + { + "indexed": false, + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "ReportedMalicious", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "parentHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "newSet", + "type": "address[]" + } + ], + "name": "InitiateChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "toPool", + "type": "address" + }, + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", + "type": "uint256" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "fromPool", + "type": "address" + }, + { + "indexed": true, + "name": "toPool", + "type": "address" + }, + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", + "type": "uint256" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "StakeMoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "fromPool", + "type": "address" + }, + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", + "type": "uint256" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "addPool", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_blockRewardContract", + "type": "address" + }, + { + "name": "_randomContract", + "type": "address" + }, + { + "name": "_erc20TokenContract", + "type": "address" + }, + { + "name": "_initialValidators", + "type": "address[]" + }, + { + "name": "_delegatorMinStake", + "type": "uint256" + }, + { + "name": "_candidateMinStake", + "type": "uint256" + }, + { + "name": "_stakingEpochDuration", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "newValidatorSet", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_validator", + "type": "address" + } + ], + "name": "removeMaliciousValidator", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "reportBenign", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_maliciousValidator", + "type": "address" + }, + { + "name": "_blockNumber", + "type": "uint256" + }, + { + "name": "", + "type": "bytes" + } + ], + "name": "reportMalicious", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_validator", + "type": "address" + }, + { + "name": "_blockNumber", + "type": "uint256" + } + ], + "name": "maliceReportedForBlock", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakeWithdrawDisallowPeriod", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakingEpochDuration", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakingEpochStartBlock", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index c307fb0ee20..81724e47b7d 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -51,7 +51,7 @@ use unexpected::{Mismatch, OutOfBounds}; mod finality; mod randomness; -mod util; +pub(crate) mod util; /// `AuthorityRound` params. pub struct AuthorityRoundParams { @@ -1147,14 +1147,14 @@ impl Engine for AuthorityRound { // This will add local service transactions to the queue. Since `on_new_block` is called before the transactions // are selected from the queue and local transactions are prioritized, they should end up in this block. // TODO: Verify this! + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + debug!(target: "engine", "Unable to close block: missing client ref."); + return Err(EngineError::RequiresClient.into()) + }, + }; if let (Some(contract_addr), Some(our_addr)) = (self.randomness_contract_address, self.signer.read().address()) { - let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { - Some(client) => client, - None => { - debug!(target: "engine", "Unable to close block: missing client ref."); - return Err(EngineError::RequiresClient.into()) - }, - }; let block_id = BlockId::Latest; let mut contract = util::BoundContract::bind(&*client, block_id, contract_addr); let accounts = self.signer.read().account_provider().clone(); @@ -1166,10 +1166,6 @@ impl Engine for AuthorityRound { .map_err(|err| EngineError::FailedSystemCall(format!("Randomness error: {:?}", err)))?; } - // with immediate transitions, we don't use the epoch mechanism anyway. - // the genesis is always considered an epoch, but we ignore it intentionally. - if self.immediate_transitions || !epoch_begin { return Ok(()) } - // genesis is never a new block, but might as well check. let header = block.header().clone(); let first = header.number() == 0; @@ -1185,6 +1181,12 @@ impl Engine for AuthorityRound { result.map_err(|e| format!("{}", e)) }; + self.validators.on_new_block(first, &header, &mut call)?; + + // with immediate transitions, we don't use the epoch mechanism anyway. + // the genesis is always considered an epoch, but we ignore it intentionally. + if self.immediate_transitions || !epoch_begin { return Ok(()) } + self.validators.on_epoch_begin(first, &header, &mut call) } diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 7ab2b67a2db..0e989defab6 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -73,6 +73,12 @@ impl ValidatorSet for ValidatorContract { self.validators.default_caller(id) } + fn on_new_block(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { + error!("on_new_block"); + self.validators.on_new_block(first, header, call) + } + + fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { self.validators.on_epoch_begin(first, header, call) } diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index cac6344b9fd..f5392b6f4f1 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -93,6 +93,15 @@ pub trait ValidatorSet: Send + Sync + 'static { Ok(()) } + #[cfg(all())] + /// Called for each new block. If this block is the first block of an + /// epoch, this is called *before* on_epoch_begin(), but with the same + /// parameters. + fn on_new_block(&self, _first: bool, _header: &Header, _call: &mut SystemCall) -> Result<(), ::error::Error> { + error!("on_new_block"); + Ok(()) + } + /// Extract genesis epoch data from the genesis state and header. fn genesis_epoch_data(&self, _header: &Header, _call: &Call) -> Result, String> { Ok(Vec::new()) } diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 4b3c33abac8..2e1af6262b5 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -46,6 +46,12 @@ impl Multi { } } + fn map_children(&self, header: &Header, func: &mut dyn FnMut(&dyn ValidatorSet, bool) -> Result<(), ::error::Error>) -> Result<(), ::error::Error> { + 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<&ValidatorSet> { match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) { Ok((_, set)) => Some(set), @@ -77,11 +83,14 @@ impl ValidatorSet for Multi { .unwrap_or(Box::new(|_, _| Err("No validator set for given ID.".into()))) } - 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(); + fn on_new_block(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { + error!("on_new_block"); + self.map_children(header, &mut |set: &dyn ValidatorSet, first| set.on_new_block(first, header, call)) + } - set.on_epoch_begin(first, header, call) + + fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { + 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, String> { diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index a5f94d4127f..ede9da95593 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -34,10 +34,10 @@ use unexpected::Mismatch; use client::EngineClient; use machine::{AuxiliaryData, Call, EthereumMachine, AuxiliaryRequest}; -use super::{SystemCall, ValidatorSet}; -use super::simple_list::SimpleList; +use super::{SystemCall, ValidatorSet, simple_list::SimpleList}; +use super::super::authority_round::util::BoundContract; -use_contract!(validator_set, "res/contracts/validator_set.json"); +use_contract!(validator_set, "res/contracts/validator_set_aura.json"); const MEMOIZE_CAPACITY: usize = 500; @@ -291,12 +291,41 @@ impl ValidatorSet for ValidatorSafeContract { .map(|out| (out, Vec::new()))) // generate no proofs in general } + fn on_new_block(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), ::error::Error> { + error!("New block issued ― calling emitInitiateChange()"); + let (data, decoder) = validator_set::functions::emit_initiate_change_callable::call(); + if !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)? { + debug!(target: "engine", "No need to call emitInitiateChange()"); + return Ok(()); + } + + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + error!(target: "engine", "Unable to close block: missing client ref."); + return Err(::engines::EngineError::RequiresClient.into()) + }, + }; + + + let bound_contract = BoundContract::bind(&*client, BlockId::Latest, self.contract_address); + let data = validator_set::functions::emit_initiate_change::call(); + bound_contract.schedule_service_transaction(data) + .map_err(|x|format!("Error scheduling a transaction: {:?}", x)) + .map_err(::engines::EngineError::FailedSystemCall)?; + debug!(target: "engine", "Successfully called emitInitiateChange()"); + Ok(()) + } + fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), ::error::Error> { let data = validator_set::functions::finalize_change::encode_input(); caller(self.contract_address, data) - .map(|_| ()) - .map_err(::engines::EngineError::FailedSystemCall) - .map_err(Into::into) + .map(drop) + .map_err(::engines::EngineError::FailedSystemCall)?; + Ok(()) } fn genesis_epoch_data(&self, header: &Header, call: &Call) -> Result, String> {