Merge branch 'master' into util_error_chain
This commit is contained in:
@@ -43,7 +43,7 @@ use client::ancient_import::AncientVerifier;
|
||||
use client::Error as ClientError;
|
||||
use client::{
|
||||
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
|
||||
MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
|
||||
MiningBlockChainClient, EngineClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
|
||||
ChainNotify, PruningInfo, ProvingBlockChainClient,
|
||||
};
|
||||
use encoded;
|
||||
@@ -771,7 +771,7 @@ impl Client {
|
||||
res.map(|(output, proof)| (output, proof.into_iter().map(|x| x.into_vec()).collect()))
|
||||
};
|
||||
|
||||
match with_state.generate_proof(&call) {
|
||||
match (with_state)(&call) {
|
||||
Ok(proof) => proof,
|
||||
Err(e) => {
|
||||
warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e);
|
||||
@@ -1132,7 +1132,9 @@ impl Client {
|
||||
T: trace::Tracer,
|
||||
V: trace::VMTracer,
|
||||
{
|
||||
let options = options.dont_check_nonce();
|
||||
let options = options
|
||||
.dont_check_nonce()
|
||||
.save_output_from_contract();
|
||||
let original_state = if state_diff { Some(state.clone()) } else { None };
|
||||
|
||||
let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?;
|
||||
@@ -1935,7 +1937,7 @@ impl MiningBlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
impl super::traits::EngineClient for Client {
|
||||
impl EngineClient for Client {
|
||||
fn update_sealing(&self) {
|
||||
self.miner.update_sealing(self)
|
||||
}
|
||||
@@ -1953,22 +1955,6 @@ impl super::traits::EngineClient for Client {
|
||||
fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> {
|
||||
self.chain.read().epoch_transition_for(parent_hash)
|
||||
}
|
||||
|
||||
fn chain_info(&self) -> BlockChainInfo {
|
||||
BlockChainClient::chain_info(self)
|
||||
}
|
||||
|
||||
fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||
BlockChainClient::call_contract(self, id, address, data)
|
||||
}
|
||||
|
||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError> {
|
||||
BlockChainClient::transact_contract(self, address, data)
|
||||
}
|
||||
|
||||
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
|
||||
BlockChainClient::block_number(self, id)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvingBlockChainClient for Client {
|
||||
@@ -1983,29 +1969,27 @@ impl ProvingBlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec<DBValue>)> {
|
||||
let (header, mut env_info) = match (self.block_header(id), self.env_info(id)) {
|
||||
let (state, mut env_info) = match (self.state_at(id), self.env_info(id)) {
|
||||
(Some(s), Some(e)) => (s, e),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
env_info.gas_limit = transaction.gas.clone();
|
||||
let mut jdb = self.state_db.lock().journal_db().boxed_clone();
|
||||
let backend = state::backend::Proving::new(jdb.as_hashdb_mut());
|
||||
|
||||
state::prove_transaction(
|
||||
jdb.as_hashdb_mut(),
|
||||
header.state_root().clone(),
|
||||
&transaction,
|
||||
&*self.engine,
|
||||
&env_info,
|
||||
self.factories.clone(),
|
||||
false,
|
||||
)
|
||||
}
|
||||
let mut state = state.replace_backend(backend);
|
||||
let options = TransactOptions::with_no_tracing().dont_check_nonce();
|
||||
let res = Executive::new(&mut state, &env_info, &*self.engine).transact(&transaction, options);
|
||||
|
||||
fn epoch_signal(&self, hash: H256) -> Option<Vec<u8>> {
|
||||
// pending transitions are never deleted, and do not contain
|
||||
// finality proofs by definition.
|
||||
self.chain.read().get_pending_transition(hash).map(|pending| pending.proof)
|
||||
match res {
|
||||
Err(ExecutionError::Internal(_)) => None,
|
||||
Err(e) => {
|
||||
trace!(target: "client", "Proved call failed: {}", e);
|
||||
Some((Vec::new(), state.drop().1.extract_proof()))
|
||||
}
|
||||
Ok(res) => Some((res.output, state.drop().1.extract_proof())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ use devtools::*;
|
||||
use transaction::{Transaction, LocalizedTransaction, PendingTransaction, SignedTransaction, Action};
|
||||
use blockchain::TreeRoute;
|
||||
use client::{
|
||||
BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId,
|
||||
BlockChainClient, MiningBlockChainClient, EngineClient, BlockChainInfo, BlockStatus, BlockId,
|
||||
TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
|
||||
ProvingBlockChainClient,
|
||||
};
|
||||
@@ -801,13 +801,9 @@ impl ProvingBlockChainClient for TestBlockChainClient {
|
||||
fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec<DBValue>)> {
|
||||
None
|
||||
}
|
||||
|
||||
fn epoch_signal(&self, _: H256) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl super::traits::EngineClient for TestBlockChainClient {
|
||||
impl EngineClient for TestBlockChainClient {
|
||||
fn update_sealing(&self) {
|
||||
self.miner.update_sealing(self)
|
||||
}
|
||||
@@ -823,20 +819,4 @@ impl super::traits::EngineClient for TestBlockChainClient {
|
||||
fn epoch_transition_for(&self, _block_hash: H256) -> Option<::engines::EpochTransition> {
|
||||
None
|
||||
}
|
||||
|
||||
fn chain_info(&self) -> BlockChainInfo {
|
||||
BlockChainClient::chain_info(self)
|
||||
}
|
||||
|
||||
fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||
BlockChainClient::call_contract(self, id, address, data)
|
||||
}
|
||||
|
||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError> {
|
||||
BlockChainClient::transact_contract(self, address, data)
|
||||
}
|
||||
|
||||
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
|
||||
BlockChainClient::block_number(self, id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ pub trait MiningBlockChainClient: BlockChainClient {
|
||||
}
|
||||
|
||||
/// Client facilities used by internally sealing Engines.
|
||||
pub trait EngineClient: Sync + Send {
|
||||
pub trait EngineClient: MiningBlockChainClient {
|
||||
/// Make a new block and seal it.
|
||||
fn update_sealing(&self);
|
||||
|
||||
@@ -333,17 +333,6 @@ pub trait EngineClient: Sync + Send {
|
||||
///
|
||||
/// The block corresponding the the parent hash must be stored already.
|
||||
fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition>;
|
||||
|
||||
/// Get block chain info.
|
||||
fn chain_info(&self) -> BlockChainInfo;
|
||||
|
||||
/// Like `call`, but with various defaults. Designed to be used for calling contracts.
|
||||
fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String>;
|
||||
|
||||
/// Import a transaction: used for misbehaviour reporting.
|
||||
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError>;
|
||||
|
||||
fn block_number(&self, id: BlockId) -> Option<BlockNumber>;
|
||||
}
|
||||
|
||||
/// Extended client interface for providing proofs of the state.
|
||||
@@ -363,7 +352,4 @@ pub trait ProvingBlockChainClient: BlockChainClient {
|
||||
/// Returns the output of the call and a vector of database items necessary
|
||||
/// to reproduce it.
|
||||
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec<DBValue>)>;
|
||||
|
||||
/// Get an epoch change signal by block hash.
|
||||
fn epoch_signal(&self, hash: H256) -> Option<Vec<u8>>;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::cmp;
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use builtin::Builtin;
|
||||
use client::EngineClient;
|
||||
use client::{Client, EngineClient};
|
||||
use engines::{Call, Engine, Seal, EngineError, ConstructedVerifier};
|
||||
use error::{Error, TransactionError, BlockError};
|
||||
use ethjson;
|
||||
@@ -647,8 +647,6 @@ impl Engine for AuthorityRound {
|
||||
(&active_set as &_, epoch_manager.epoch_transition_number)
|
||||
};
|
||||
|
||||
// always report with "self.validators" so that the report actually gets
|
||||
// to the contract.
|
||||
let report = |report| match report {
|
||||
Report::Benign(address, block_number) =>
|
||||
self.validators.report_benign(&address, set_number, block_number),
|
||||
@@ -741,18 +739,13 @@ impl Engine for AuthorityRound {
|
||||
{
|
||||
if let Ok(finalized) = epoch_manager.finality_checker.push_hash(chain_head.hash(), *chain_head.author()) {
|
||||
let mut finalized = finalized.into_iter();
|
||||
while let Some(finalized_hash) = finalized.next() {
|
||||
if let Some(pending) = transition_store(finalized_hash) {
|
||||
let finality_proof = ::std::iter::once(finalized_hash)
|
||||
while let Some(hash) = finalized.next() {
|
||||
if let Some(pending) = transition_store(hash) {
|
||||
let finality_proof = ::std::iter::once(hash)
|
||||
.chain(finalized)
|
||||
.chain(epoch_manager.finality_checker.unfinalized_hashes())
|
||||
.map(|h| if h == chain_head.hash() {
|
||||
// chain closure only stores ancestry, but the chain head is also
|
||||
// unfinalized.
|
||||
chain_head.clone()
|
||||
} else {
|
||||
chain(h).expect("these headers fetched before when constructing finality checker; qed")
|
||||
})
|
||||
.map(|hash| chain(hash)
|
||||
.expect("these headers fetched before when constructing finality checker; qed"))
|
||||
.collect::<Vec<Header>>();
|
||||
|
||||
// this gives us the block number for `hash`, assuming it's ancestry.
|
||||
@@ -816,9 +809,9 @@ impl Engine for AuthorityRound {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<EngineClient>) {
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_client(client);
|
||||
self.validators.register_contract(client);
|
||||
}
|
||||
|
||||
fn set_signer(&self, ap: Arc<AccountProvider>, address: Address, password: String) {
|
||||
|
||||
@@ -34,7 +34,7 @@ use error::{BlockError, Error};
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
use header::{Header, BlockNumber};
|
||||
use client::EngineClient;
|
||||
use client::Client;
|
||||
use semantic_version::SemanticVersion;
|
||||
use super::signer::EngineSigner;
|
||||
use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
|
||||
@@ -237,8 +237,8 @@ impl Engine for BasicAuthority {
|
||||
}
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<EngineClient>) {
|
||||
self.validators.register_client(client);
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
self.validators.register_contract(client);
|
||||
}
|
||||
|
||||
fn set_signer(&self, ap: Arc<AccountProvider>, address: Address, password: String) {
|
||||
|
||||
@@ -44,7 +44,7 @@ use self::epoch::PendingTransition;
|
||||
use account_provider::AccountProvider;
|
||||
use block::ExecutedBlock;
|
||||
use builtin::Builtin;
|
||||
use client::EngineClient;
|
||||
use client::Client;
|
||||
use vm::{EnvInfo, LastHashes, Schedule, CreateContractAddress};
|
||||
use error::Error;
|
||||
use header::{Header, BlockNumber};
|
||||
@@ -124,22 +124,12 @@ pub type Headers<'a> = Fn(H256) -> Option<Header> + 'a;
|
||||
/// Type alias for a function we can query pending transitions by block hash through.
|
||||
pub type PendingTransitionStore<'a> = Fn(H256) -> Option<PendingTransition> + 'a;
|
||||
|
||||
/// Proof dependent on state.
|
||||
pub trait StateDependentProof: Send + Sync {
|
||||
/// Generate a proof, given the state.
|
||||
fn generate_proof(&self, caller: &Call) -> Result<Vec<u8>, String>;
|
||||
/// Check a proof generated elsewhere (potentially by a peer).
|
||||
// `engine` needed to check state proofs, while really this should
|
||||
// just be state machine params.
|
||||
fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String>;
|
||||
}
|
||||
|
||||
/// Proof generated on epoch change.
|
||||
pub enum Proof {
|
||||
/// Known proof (extracted from signal)
|
||||
/// Known proof (exctracted from signal)
|
||||
Known(Vec<u8>),
|
||||
/// State dependent proof.
|
||||
WithState(Arc<StateDependentProof>),
|
||||
/// Extract proof from caller.
|
||||
WithState(Box<Fn(&Call) -> Result<Vec<u8>, String>>),
|
||||
}
|
||||
|
||||
/// Generated epoch verifier.
|
||||
@@ -371,7 +361,7 @@ pub trait Engine : Sync + Send {
|
||||
fn sign(&self, _hash: H256) -> Result<Signature, Error> { unimplemented!() }
|
||||
|
||||
/// Add Client which can be used for sealing, querying the state and sending messages.
|
||||
fn register_client(&self, _client: Weak<EngineClient>) {}
|
||||
fn register_client(&self, _client: Weak<Client>) {}
|
||||
|
||||
/// Trigger next step of the consensus engine.
|
||||
fn step(&self) {}
|
||||
|
||||
@@ -35,7 +35,7 @@ use bigint::hash::{H256, H520};
|
||||
use parking_lot::RwLock;
|
||||
use util::*;
|
||||
use unexpected::{OutOfBounds, Mismatch};
|
||||
use client::EngineClient;
|
||||
use client::{Client, EngineClient};
|
||||
use error::{Error, BlockError};
|
||||
use header::{Header, BlockNumber};
|
||||
use builtin::Builtin;
|
||||
@@ -571,35 +571,18 @@ impl Engine for Tendermint {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify gas limit.
|
||||
/// Verify validators and gas limit.
|
||||
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if header.number() == 0 {
|
||||
return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into());
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.params().gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default());
|
||||
return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if let Ok(proposal) = ConsensusMessage::new_proposal(header) {
|
||||
let proposer = proposal.verify()?;
|
||||
if !self.is_authority(&proposer) {
|
||||
return Err(EngineError::NotAuthorized(proposer).into());
|
||||
}
|
||||
self.check_view_proposer(
|
||||
header.parent_hash(),
|
||||
proposal.vote_step.height,
|
||||
proposal.vote_step.view,
|
||||
&proposer
|
||||
).map_err(Into::into)
|
||||
self.check_view_proposer(header.parent_hash(), proposal.vote_step.height, proposal.vote_step.view, &proposer)?;
|
||||
} else {
|
||||
let vote_step = VoteStep::new(header.number() as usize, consensus_view(header)?, Step::Precommit);
|
||||
let precommit_hash = message_hash(vote_step.clone(), header.bare_hash());
|
||||
@@ -625,8 +608,18 @@ impl Engine for Tendermint {
|
||||
}
|
||||
}
|
||||
|
||||
self.check_above_threshold(origins.len()).map_err(Into::into)
|
||||
self.check_above_threshold(origins.len())?
|
||||
}
|
||||
|
||||
let gas_limit_divisor = self.params().gas_limit_bound_divisor;
|
||||
let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
|
||||
let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
|
||||
if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
|
||||
self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default());
|
||||
return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>)
|
||||
@@ -761,12 +754,13 @@ impl Engine for Tendermint {
|
||||
self.to_step(next_step);
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<EngineClient>) {
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
use client::BlockChainClient;
|
||||
if let Some(c) = client.upgrade() {
|
||||
self.height.store(c.chain_info().best_block_number as usize + 1, AtomicOrdering::SeqCst);
|
||||
}
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_client(client);
|
||||
self.validators.register_contract(client);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,14 +888,14 @@ mod tests {
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Good proposer.
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
|
||||
let validator = insert_and_unlock(&tap, "0");
|
||||
header.set_author(validator);
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Bad proposer.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::NotProposer(_))) => {},
|
||||
_ => panic!(),
|
||||
}
|
||||
@@ -911,7 +905,7 @@ mod tests {
|
||||
let seal = proposal_seal(&tap, &header, 0);
|
||||
header.set_seal(seal);
|
||||
// Not authority.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||
_ => panic!(),
|
||||
};
|
||||
@@ -941,7 +935,7 @@ mod tests {
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
// One good signature is not enough.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {},
|
||||
_ => panic!(),
|
||||
}
|
||||
@@ -952,7 +946,7 @@ mod tests {
|
||||
seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).into_vec();
|
||||
header.set_seal(seal.clone());
|
||||
|
||||
assert!(engine.verify_block_external(&header, None).is_ok());
|
||||
assert!(engine.verify_block_family(&header, &parent_header, None).is_ok());
|
||||
|
||||
let bad_voter = insert_and_unlock(&tap, "101");
|
||||
let bad_signature = tap.sign(bad_voter, None, keccak(vote_info)).unwrap();
|
||||
@@ -961,7 +955,7 @@ mod tests {
|
||||
header.set_seal(seal);
|
||||
|
||||
// One good and one bad signature.
|
||||
match engine.verify_block_external(&header, None) {
|
||||
match engine.verify_block_family(&header, &parent_header, None) {
|
||||
Err(Error::Engine(EngineError::NotAuthorized(_))) => {},
|
||||
_ => panic!(),
|
||||
};
|
||||
@@ -1007,7 +1001,7 @@ mod tests {
|
||||
let client = generate_dummy_client(0);
|
||||
let notify = Arc::new(TestNotify::default());
|
||||
client.add_notify(notify.clone());
|
||||
engine.register_client(Arc::downgrade(&client) as _);
|
||||
engine.register_client(Arc::downgrade(&client));
|
||||
|
||||
let prevote_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
|
||||
|
||||
@@ -1025,6 +1019,7 @@ mod tests {
|
||||
fn seal_submission() {
|
||||
use ethkey::{Generator, Random};
|
||||
use transaction::{Transaction, Action};
|
||||
use client::BlockChainClient;
|
||||
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
// Accounts for signing votes.
|
||||
@@ -1037,7 +1032,7 @@ mod tests {
|
||||
|
||||
let notify = Arc::new(TestNotify::default());
|
||||
client.add_notify(notify.clone());
|
||||
engine.register_client(Arc::downgrade(&client) as _);
|
||||
engine.register_client(Arc::downgrade(&client));
|
||||
|
||||
let keypair = Random.generate().unwrap();
|
||||
let transaction = Transaction {
|
||||
|
||||
@@ -25,7 +25,7 @@ use util::*;
|
||||
use futures::Future;
|
||||
use native_contracts::ValidatorReport as Provider;
|
||||
|
||||
use client::EngineClient;
|
||||
use client::{Client, BlockChainClient};
|
||||
use engines::{Call, Engine};
|
||||
use header::{Header, BlockNumber};
|
||||
|
||||
@@ -36,7 +36,7 @@ use super::safe_contract::ValidatorSafeContract;
|
||||
pub struct ValidatorContract {
|
||||
validators: ValidatorSafeContract,
|
||||
provider: Provider,
|
||||
client: RwLock<Option<Weak<EngineClient>>>, // TODO [keorn]: remove
|
||||
client: RwLock<Option<Weak<Client>>>, // TODO [keorn]: remove
|
||||
}
|
||||
|
||||
impl ValidatorContract {
|
||||
@@ -120,8 +120,8 @@ impl ValidatorSet for ValidatorContract {
|
||||
}
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<EngineClient>) {
|
||||
self.validators.register_client(client.clone());
|
||||
fn register_contract(&self, client: Weak<Client>) {
|
||||
self.validators.register_contract(client.clone());
|
||||
*self.client.write() = Some(client);
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ mod tests {
|
||||
fn fetches_validators() {
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, None);
|
||||
let vc = Arc::new(ValidatorContract::new("0000000000000000000000000000000000000005".parse::<Address>().unwrap()));
|
||||
vc.register_client(Arc::downgrade(&client) as _);
|
||||
vc.register_contract(Arc::downgrade(&client));
|
||||
let last_hash = client.best_block_header().hash();
|
||||
assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::<Address>().unwrap()));
|
||||
assert!(vc.contains(&last_hash, &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1".parse::<Address>().unwrap()));
|
||||
@@ -159,7 +159,7 @@ mod tests {
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let v1 = tap.insert_account(keccak("1").into(), "").unwrap();
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, Some(tap.clone()));
|
||||
client.engine().register_client(Arc::downgrade(&client) as _);
|
||||
client.engine().register_client(Arc::downgrade(&client));
|
||||
let validator_contract = "0000000000000000000000000000000000000005".parse::<Address>().unwrap();
|
||||
|
||||
// Make sure reporting can be done.
|
||||
|
||||
@@ -28,7 +28,7 @@ use ids::BlockId;
|
||||
use bigint::hash::H256;
|
||||
use util::{Bytes, Address};
|
||||
use ethjson::spec::ValidatorSet as ValidatorSpec;
|
||||
use client::EngineClient;
|
||||
use client::Client;
|
||||
use header::{Header, BlockNumber};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -142,5 +142,5 @@ pub trait ValidatorSet: Send + Sync {
|
||||
/// Notifies about benign misbehaviour.
|
||||
fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber) {}
|
||||
/// Allows blockchain state access.
|
||||
fn register_client(&self, _client: Weak<EngineClient>) {}
|
||||
fn register_contract(&self, _client: Weak<Client>) {}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ use parking_lot::RwLock;
|
||||
use util::{Bytes, Address};
|
||||
use ids::BlockId;
|
||||
use header::{BlockNumber, Header};
|
||||
use client::EngineClient;
|
||||
use client::{Client, BlockChainClient};
|
||||
use super::{SystemCall, ValidatorSet};
|
||||
|
||||
type BlockNumberLookup = Box<Fn(BlockId) -> Result<BlockNumber, String> + Send + Sync + 'static>;
|
||||
@@ -131,9 +131,9 @@ impl ValidatorSet for Multi {
|
||||
self.correct_set_by_number(set_block).1.report_benign(validator, set_block, block);
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<EngineClient>) {
|
||||
fn register_contract(&self, client: Weak<Client>) {
|
||||
for set in self.sets.values() {
|
||||
set.register_client(client.clone());
|
||||
set.register_contract(client.clone());
|
||||
}
|
||||
*self.block_number.write() = Box::new(move |id| client
|
||||
.upgrade()
|
||||
@@ -148,7 +148,7 @@ mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
use hash::keccak;
|
||||
use account_provider::AccountProvider;
|
||||
use client::BlockChainClient;
|
||||
use client::{BlockChainClient, EngineClient};
|
||||
use engines::EpochChange;
|
||||
use engines::validator_set::ValidatorSet;
|
||||
use ethkey::Secret;
|
||||
@@ -170,7 +170,7 @@ mod tests {
|
||||
let v0 = tap.insert_account(s0.clone(), "").unwrap();
|
||||
let v1 = tap.insert_account(keccak("1").into(), "").unwrap();
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_multi, Some(tap));
|
||||
client.engine().register_client(Arc::downgrade(&client) as _);
|
||||
client.engine().register_client(Arc::downgrade(&client));
|
||||
|
||||
// Make sure txs go through.
|
||||
client.miner().set_gas_floor_target(1_000_000.into());
|
||||
@@ -178,27 +178,27 @@ mod tests {
|
||||
// Wrong signer for the first block.
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
client.transact_contract(Default::default(), Default::default()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 0);
|
||||
// Right signer for the first block.
|
||||
client.miner().set_engine_signer(v0, "".into()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
// This time v0 is wrong.
|
||||
client.transact_contract(Default::default(), Default::default()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 2);
|
||||
// v1 is still good.
|
||||
client.transact_contract(Default::default(), Default::default()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
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, &[]);
|
||||
sync_client.engine().register_client(Arc::downgrade(&sync_client) as _);
|
||||
sync_client.engine().register_client(Arc::downgrade(&sync_client));
|
||||
for i in 1..4 {
|
||||
sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap();
|
||||
}
|
||||
|
||||
@@ -23,15 +23,14 @@ use hash::keccak;
|
||||
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::{H160, H256};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use util::*;
|
||||
use util::cache::MemoryLruCache;
|
||||
use unexpected::Mismatch;
|
||||
use rlp::{UntrustedRlp, RlpStream};
|
||||
|
||||
use basic_types::LogBloom;
|
||||
use client::EngineClient;
|
||||
use client::{Client, BlockChainClient};
|
||||
use engines::{Call, Engine};
|
||||
use header::Header;
|
||||
use ids::BlockId;
|
||||
@@ -50,35 +49,12 @@ lazy_static! {
|
||||
static ref EVENT_NAME_HASH: H256 = keccak(EVENT_NAME);
|
||||
}
|
||||
|
||||
// state-dependent proofs for the safe contract:
|
||||
// only "first" proofs are such.
|
||||
struct StateProof {
|
||||
header: Mutex<Header>,
|
||||
provider: Provider,
|
||||
}
|
||||
|
||||
impl ::engines::StateDependentProof for StateProof {
|
||||
fn generate_proof(&self, caller: &Call) -> Result<Vec<u8>, String> {
|
||||
prove_initial(&self.provider, &*self.header.lock(), caller)
|
||||
}
|
||||
|
||||
fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String> {
|
||||
let (header, state_items) = decode_first_proof(&UntrustedRlp::new(proof))
|
||||
.map_err(|e| format!("proof incorrectly encoded: {}", e))?;
|
||||
if &header != &*self.header.lock(){
|
||||
return Err("wrong header in proof".into());
|
||||
}
|
||||
|
||||
check_first_proof(engine, &self.provider, header, &state_items).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// The validator contract should have the following interface:
|
||||
pub struct ValidatorSafeContract {
|
||||
pub address: Address,
|
||||
validators: RwLock<MemoryLruCache<H256, SimpleList>>,
|
||||
provider: Provider,
|
||||
client: RwLock<Option<Weak<EngineClient>>>, // TODO [keorn]: remove
|
||||
client: RwLock<Option<Weak<Client>>>, // TODO [keorn]: remove
|
||||
}
|
||||
|
||||
// first proof is just a state proof call of `getValidators` at header's state.
|
||||
@@ -92,59 +68,6 @@ fn encode_first_proof(header: &Header, state_items: &[Vec<u8>]) -> Bytes {
|
||||
stream.out()
|
||||
}
|
||||
|
||||
// check a first proof: fetch the validator set at the given block.
|
||||
fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, state_items: &[DBValue])
|
||||
-> Result<Vec<Address>, String>
|
||||
{
|
||||
use transaction::{Action, Transaction};
|
||||
|
||||
// TODO: match client contract_call_tx more cleanly without duplication.
|
||||
const PROVIDED_GAS: u64 = 50_000_000;
|
||||
|
||||
let env_info = ::vm::EnvInfo {
|
||||
number: old_header.number(),
|
||||
author: *old_header.author(),
|
||||
difficulty: *old_header.difficulty(),
|
||||
gas_limit: PROVIDED_GAS.into(),
|
||||
timestamp: old_header.timestamp(),
|
||||
last_hashes: {
|
||||
// this will break if we don't inclue all 256 last hashes.
|
||||
let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect();
|
||||
last_hashes[255] = *old_header.parent_hash();
|
||||
Arc::new(last_hashes)
|
||||
},
|
||||
gas_used: 0.into(),
|
||||
};
|
||||
|
||||
// check state proof using given engine.
|
||||
let number = old_header.number();
|
||||
provider.get_validators(move |a, d| {
|
||||
let from = Address::default();
|
||||
let tx = Transaction {
|
||||
nonce: engine.account_start_nonce(number),
|
||||
action: Action::Call(a),
|
||||
gas: PROVIDED_GAS.into(),
|
||||
gas_price: U256::default(),
|
||||
value: U256::default(),
|
||||
data: d,
|
||||
}.fake_sign(from);
|
||||
|
||||
let res = ::state::check_proof(
|
||||
state_items,
|
||||
*old_header.state_root(),
|
||||
&tx,
|
||||
engine,
|
||||
&env_info,
|
||||
);
|
||||
|
||||
match res {
|
||||
::state::ProvedExecution::BadProof => Err("Bad proof".into()),
|
||||
::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)),
|
||||
::state::ProvedExecution::Complete(e) => Ok(e.output),
|
||||
}
|
||||
}).wait()
|
||||
}
|
||||
|
||||
fn decode_first_proof(rlp: &UntrustedRlp) -> Result<(Header, Vec<DBValue>), ::error::Error> {
|
||||
let header = rlp.val_at(0)?;
|
||||
let state_items = rlp.at(1)?.iter().map(|x| {
|
||||
@@ -182,7 +105,8 @@ fn prove_initial(provider: &Provider, header: &Header, caller: &Call) -> Result<
|
||||
Ok(result)
|
||||
};
|
||||
|
||||
provider.get_validators(caller).wait()
|
||||
provider.get_validators(caller)
|
||||
.wait()
|
||||
};
|
||||
|
||||
res.map(|validators| {
|
||||
@@ -336,11 +260,9 @@ impl ValidatorSet for ValidatorSafeContract {
|
||||
// transition to the first block of a contract requires finality but has no log event.
|
||||
if first {
|
||||
debug!(target: "engine", "signalling transition to fresh contract.");
|
||||
let state_proof = Arc::new(StateProof {
|
||||
header: Mutex::new(header.clone()),
|
||||
provider: self.provider.clone(),
|
||||
});
|
||||
return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>));
|
||||
let (provider, header) = (self.provider.clone(), header.clone());
|
||||
let with_caller: Box<Fn(&Call) -> _> = Box::new(move |caller| prove_initial(&provider, &header, caller));
|
||||
return ::engines::EpochChange::Yes(::engines::Proof::WithState(with_caller))
|
||||
}
|
||||
|
||||
// otherwise, we're checking for logs.
|
||||
@@ -369,16 +291,61 @@ impl ValidatorSet for ValidatorSafeContract {
|
||||
fn epoch_set(&self, first: bool, engine: &Engine, _number: ::header::BlockNumber, proof: &[u8])
|
||||
-> Result<(SimpleList, Option<H256>), ::error::Error>
|
||||
{
|
||||
use transaction::{Action, Transaction};
|
||||
|
||||
let rlp = UntrustedRlp::new(proof);
|
||||
|
||||
if first {
|
||||
trace!(target: "engine", "Recovering initial epoch set");
|
||||
|
||||
// TODO: match client contract_call_tx more cleanly without duplication.
|
||||
const PROVIDED_GAS: u64 = 50_000_000;
|
||||
|
||||
let (old_header, state_items) = decode_first_proof(&rlp)?;
|
||||
let number = old_header.number();
|
||||
let old_hash = old_header.hash();
|
||||
let addresses = check_first_proof(engine, &self.provider, old_header, &state_items)
|
||||
.map_err(::engines::EngineError::InsufficientProof)?;
|
||||
|
||||
let env_info = ::vm::EnvInfo {
|
||||
number: old_header.number(),
|
||||
author: *old_header.author(),
|
||||
difficulty: *old_header.difficulty(),
|
||||
gas_limit: PROVIDED_GAS.into(),
|
||||
timestamp: old_header.timestamp(),
|
||||
last_hashes: {
|
||||
// this will break if we don't inclue all 256 last hashes.
|
||||
let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect();
|
||||
last_hashes[255] = *old_header.parent_hash();
|
||||
Arc::new(last_hashes)
|
||||
},
|
||||
gas_used: 0.into(),
|
||||
};
|
||||
|
||||
// check state proof using given engine.
|
||||
let number = old_header.number();
|
||||
let addresses = self.provider.get_validators(move |a, d| {
|
||||
let from = Address::default();
|
||||
let tx = Transaction {
|
||||
nonce: engine.account_start_nonce(number),
|
||||
action: Action::Call(a),
|
||||
gas: PROVIDED_GAS.into(),
|
||||
gas_price: U256::default(),
|
||||
value: U256::default(),
|
||||
data: d,
|
||||
}.fake_sign(from);
|
||||
|
||||
let res = ::state::check_proof(
|
||||
&state_items,
|
||||
*old_header.state_root(),
|
||||
&tx,
|
||||
engine,
|
||||
&env_info,
|
||||
);
|
||||
|
||||
match res {
|
||||
::state::ProvedExecution::BadProof => Err("Bad proof".into()),
|
||||
::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)),
|
||||
::state::ProvedExecution::Complete(e) => Ok(e.output),
|
||||
}
|
||||
}).wait().map_err(::engines::EngineError::InsufficientProof)?;
|
||||
|
||||
trace!(target: "engine", "extracted epoch set at #{}: {} addresses",
|
||||
number, addresses.len());
|
||||
@@ -452,7 +419,7 @@ impl ValidatorSet for ValidatorSafeContract {
|
||||
}))
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<EngineClient>) {
|
||||
fn register_contract(&self, client: Weak<Client>) {
|
||||
trace!(target: "engine", "Setting up contract caller.");
|
||||
*self.client.write() = Some(client);
|
||||
}
|
||||
@@ -468,7 +435,7 @@ mod tests {
|
||||
use spec::Spec;
|
||||
use account_provider::AccountProvider;
|
||||
use transaction::{Transaction, Action};
|
||||
use client::BlockChainClient;
|
||||
use client::{BlockChainClient, EngineClient};
|
||||
use ethkey::Secret;
|
||||
use miner::MinerService;
|
||||
use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data};
|
||||
@@ -479,7 +446,7 @@ mod tests {
|
||||
fn fetches_validators() {
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None);
|
||||
let vc = Arc::new(ValidatorSafeContract::new("0000000000000000000000000000000000000005".parse::<Address>().unwrap()));
|
||||
vc.register_client(Arc::downgrade(&client) as _);
|
||||
vc.register_contract(Arc::downgrade(&client));
|
||||
let last_hash = client.best_block_header().hash();
|
||||
assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::<Address>().unwrap()));
|
||||
assert!(vc.contains(&last_hash, &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1".parse::<Address>().unwrap()));
|
||||
@@ -493,7 +460,7 @@ mod tests {
|
||||
let v1 = tap.insert_account(keccak("0").into(), "").unwrap();
|
||||
let chain_id = Spec::new_validator_safe_contract().chain_id();
|
||||
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, Some(tap));
|
||||
client.engine().register_client(Arc::downgrade(&client) as _);
|
||||
client.engine().register_client(Arc::downgrade(&client));
|
||||
let validator_contract = "0000000000000000000000000000000000000005".parse::<Address>().unwrap();
|
||||
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
@@ -507,7 +474,7 @@ mod tests {
|
||||
data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(),
|
||||
}.sign(&s0, Some(chain_id));
|
||||
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
// Add "1" validator back in.
|
||||
let tx = Transaction {
|
||||
@@ -519,13 +486,13 @@ mod tests {
|
||||
data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(),
|
||||
}.sign(&s0, Some(chain_id));
|
||||
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
// The transaction is not yet included so still unable to seal.
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
|
||||
// Switch to the validator that is still there.
|
||||
client.miner().set_engine_signer(v0, "".into()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
assert_eq!(client.chain_info().best_block_number, 2);
|
||||
// Switch back to the added validator, since the state is updated.
|
||||
client.miner().set_engine_signer(v1, "".into()).unwrap();
|
||||
@@ -538,13 +505,13 @@ mod tests {
|
||||
data: Vec::new(),
|
||||
}.sign(&s0, Some(chain_id));
|
||||
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
|
||||
::client::EngineClient::update_sealing(&*client);
|
||||
client.update_sealing();
|
||||
// Able to seal again.
|
||||
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, &[]);
|
||||
sync_client.engine().register_client(Arc::downgrade(&sync_client) as _);
|
||||
sync_client.engine().register_client(Arc::downgrade(&sync_client));
|
||||
for i in 1..4 {
|
||||
sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap();
|
||||
}
|
||||
|
||||
@@ -77,6 +77,8 @@ pub struct TransactOptions<T, V> {
|
||||
pub vm_tracer: V,
|
||||
/// Check transaction nonce before execution.
|
||||
pub check_nonce: bool,
|
||||
/// Records the output from init contract calls.
|
||||
pub output_from_init_contract: bool,
|
||||
}
|
||||
|
||||
impl<T, V> TransactOptions<T, V> {
|
||||
@@ -86,6 +88,7 @@ impl<T, V> TransactOptions<T, V> {
|
||||
tracer,
|
||||
vm_tracer,
|
||||
check_nonce: true,
|
||||
output_from_init_contract: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +97,12 @@ impl<T, V> TransactOptions<T, V> {
|
||||
self.check_nonce = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Saves the output from contract creation.
|
||||
pub fn save_output_from_contract(mut self) -> Self {
|
||||
self.output_from_init_contract = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactOptions<trace::ExecutiveTracer, trace::ExecutiveVMTracer> {
|
||||
@@ -103,6 +112,7 @@ impl TransactOptions<trace::ExecutiveTracer, trace::ExecutiveVMTracer> {
|
||||
tracer: trace::ExecutiveTracer::default(),
|
||||
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
|
||||
check_nonce: true,
|
||||
output_from_init_contract: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,6 +124,7 @@ impl TransactOptions<trace::ExecutiveTracer, trace::NoopVMTracer> {
|
||||
tracer: trace::ExecutiveTracer::default(),
|
||||
vm_tracer: trace::NoopVMTracer,
|
||||
check_nonce: true,
|
||||
output_from_init_contract: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +136,7 @@ impl TransactOptions<trace::NoopTracer, trace::ExecutiveVMTracer> {
|
||||
tracer: trace::NoopTracer,
|
||||
vm_tracer: trace::ExecutiveVMTracer::toplevel(),
|
||||
check_nonce: true,
|
||||
output_from_init_contract: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,6 +148,7 @@ impl TransactOptions<trace::NoopTracer, trace::NoopVMTracer> {
|
||||
tracer: trace::NoopTracer,
|
||||
vm_tracer: trace::NoopVMTracer,
|
||||
check_nonce: true,
|
||||
output_from_init_contract: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,7 +217,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
pub fn transact<T, V>(&'a mut self, t: &SignedTransaction, options: TransactOptions<T, V>)
|
||||
-> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer,
|
||||
{
|
||||
self.transact_with_tracer(t, options.check_nonce, options.tracer, options.vm_tracer)
|
||||
self.transact_with_tracer(t, options.check_nonce, options.output_from_init_contract, options.tracer, options.vm_tracer)
|
||||
}
|
||||
|
||||
/// Execute a transaction in a "virtual" context.
|
||||
@@ -229,6 +242,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
&'a mut self,
|
||||
t: &SignedTransaction,
|
||||
check_nonce: bool,
|
||||
output_from_create: bool,
|
||||
mut tracer: T,
|
||||
mut vm_tracer: V
|
||||
) -> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer {
|
||||
@@ -297,7 +311,8 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
data: None,
|
||||
call_type: CallType::None,
|
||||
};
|
||||
(self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
|
||||
let mut out = if output_from_create { Some(vec![]) } else { None };
|
||||
(self.create(params, &mut substate, &mut out, &mut tracer, &mut vm_tracer), out.unwrap_or_else(Vec::new))
|
||||
},
|
||||
Action::Call(ref address) => {
|
||||
let params = ActionParams {
|
||||
@@ -490,6 +505,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
&mut self,
|
||||
params: ActionParams,
|
||||
substate: &mut Substate,
|
||||
output: &mut Option<Bytes>,
|
||||
tracer: &mut T,
|
||||
vm_tracer: &mut V,
|
||||
) -> vm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer {
|
||||
@@ -531,7 +547,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
|
||||
|
||||
let res = {
|
||||
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
|
||||
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(output.as_mut().or(trace_output.as_mut())), &mut subtracer, &mut subvmtracer)
|
||||
};
|
||||
|
||||
vm_tracer.done_subtrace(subvmtracer);
|
||||
@@ -540,7 +556,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
||||
Ok(ref res) => tracer.trace_create(
|
||||
trace_info,
|
||||
gas - res.gas_left,
|
||||
trace_output,
|
||||
trace_output.map(|data| output.as_ref().map(|out| out.to_vec()).unwrap_or(data)),
|
||||
created,
|
||||
subtracer.drain()
|
||||
),
|
||||
@@ -701,7 +717,7 @@ mod tests {
|
||||
|
||||
let (gas_left, _) = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(79_975));
|
||||
@@ -759,7 +775,7 @@ mod tests {
|
||||
|
||||
let (gas_left, _) = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(62_976));
|
||||
@@ -926,7 +942,7 @@ mod tests {
|
||||
|
||||
let (gas_left, _) = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap()
|
||||
ex.create(params.clone(), &mut substate, &mut None, &mut tracer, &mut vm_tracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(96_776));
|
||||
@@ -1011,7 +1027,7 @@ mod tests {
|
||||
|
||||
let (gas_left, _) = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(62_976));
|
||||
@@ -1062,7 +1078,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap();
|
||||
ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(substate.contracts_created.len(), 1);
|
||||
@@ -1335,7 +1351,7 @@ mod tests {
|
||||
|
||||
let result = {
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer)
|
||||
ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer)
|
||||
};
|
||||
|
||||
match result {
|
||||
|
||||
@@ -224,7 +224,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
||||
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag);
|
||||
|
||||
// TODO: handle internal error separately
|
||||
match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
|
||||
match ex.create(params, self.substate, &mut None, self.tracer, self.vm_tracer) {
|
||||
Ok((gas_left, _)) => {
|
||||
self.substate.contracts_created.push(address.clone());
|
||||
ContractCreateResult::Created(address, gas_left)
|
||||
|
||||
@@ -261,13 +261,8 @@ impl Header {
|
||||
s.out()
|
||||
}
|
||||
|
||||
/// Get the SHA3 (Keccak) of this header, optionally `with_seal`.
|
||||
/// Get the KECCAK (Keccak) of this header, optionally `with_seal`.
|
||||
pub fn rlp_keccak(&self, with_seal: Seal) -> H256 { keccak(self.rlp(with_seal)) }
|
||||
|
||||
/// Encode the header, getting a type-safe wrapper around the RLP.
|
||||
pub fn encoded(&self) -> ::encoded::Header {
|
||||
::encoded::Header::new(self.rlp(Seal::With))
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Header {
|
||||
|
||||
@@ -116,7 +116,7 @@ impl ClientService {
|
||||
});
|
||||
io_service.register_handler(client_io)?;
|
||||
|
||||
spec.engine.register_client(Arc::downgrade(&client) as _);
|
||||
spec.engine.register_client(Arc::downgrade(&client));
|
||||
|
||||
let stop_guard = ::devtools::StopGuard::new();
|
||||
run_ipc(ipc_path, client.clone(), snapshot.clone(), stop_guard.share());
|
||||
|
||||
@@ -93,7 +93,7 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
|
||||
let mut cur_signers = vec![*RICH_ADDR];
|
||||
{
|
||||
let engine = client.engine();
|
||||
engine.register_client(Arc::downgrade(&client) as _);
|
||||
engine.register_client(Arc::downgrade(&client));
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -36,6 +36,7 @@ use factory::Factories;
|
||||
use header::{BlockNumber, Header};
|
||||
use pod_state::*;
|
||||
use rlp::{Rlp, RlpStream};
|
||||
use state_db::StateDB;
|
||||
use state::{Backend, State, Substate};
|
||||
use state::backend::Basic as BasicBackend;
|
||||
use trace::{NoopTracer, NoopVMTracer};
|
||||
@@ -355,7 +356,7 @@ impl Spec {
|
||||
|
||||
{
|
||||
let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref());
|
||||
if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) {
|
||||
if let Err(e) = exec.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer) {
|
||||
warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e);
|
||||
}
|
||||
}
|
||||
@@ -464,7 +465,7 @@ impl Spec {
|
||||
}
|
||||
|
||||
/// Ensure that the given state DB has the trie nodes in for the genesis state.
|
||||
pub fn ensure_db_good<T: Backend>(&self, db: T, factories: &Factories) -> Result<T, Error> {
|
||||
pub fn ensure_db_good(&self, db: StateDB, factories: &Factories) -> Result<StateDB, Error> {
|
||||
if db.as_hashdb().contains(&self.state_root()) {
|
||||
return Ok(db)
|
||||
}
|
||||
@@ -486,63 +487,6 @@ impl Spec {
|
||||
.and_then(|x| load_from(cache_dir, x).map_err(fmt))
|
||||
}
|
||||
|
||||
/// initialize genesis epoch data, using in-memory database for
|
||||
/// constructor.
|
||||
pub fn genesis_epoch_data(&self) -> Result<Vec<u8>, String> {
|
||||
use transaction::{Action, Transaction};
|
||||
use util::{journaldb, kvdb};
|
||||
|
||||
let genesis = self.genesis_header();
|
||||
|
||||
let factories = Default::default();
|
||||
let mut db = journaldb::new(
|
||||
Arc::new(kvdb::in_memory(0)),
|
||||
journaldb::Algorithm::Archive,
|
||||
None,
|
||||
);
|
||||
|
||||
self.ensure_db_good(BasicBackend(db.as_hashdb_mut()), &factories)
|
||||
.map_err(|e| format!("Unable to initialize genesis state: {}", e))?;
|
||||
|
||||
let call = |a, d| {
|
||||
let mut db = db.boxed_clone();
|
||||
let env_info = ::evm::EnvInfo {
|
||||
number: 0,
|
||||
author: *genesis.author(),
|
||||
timestamp: genesis.timestamp(),
|
||||
difficulty: *genesis.difficulty(),
|
||||
gas_limit: *genesis.gas_limit(),
|
||||
last_hashes: Arc::new(Vec::new()),
|
||||
gas_used: 0.into()
|
||||
};
|
||||
|
||||
let from = Address::default();
|
||||
let tx = Transaction {
|
||||
nonce: self.engine.account_start_nonce(0),
|
||||
action: Action::Call(a),
|
||||
gas: U256::from(50_000_000), // TODO: share with client.
|
||||
gas_price: U256::default(),
|
||||
value: U256::default(),
|
||||
data: d,
|
||||
}.fake_sign(from);
|
||||
|
||||
let res = ::state::prove_transaction(
|
||||
db.as_hashdb_mut(),
|
||||
*genesis.state_root(),
|
||||
&tx,
|
||||
&*self.engine,
|
||||
&env_info,
|
||||
factories.clone(),
|
||||
true,
|
||||
);
|
||||
|
||||
res.map(|(out, proof)| (out, proof.into_iter().map(|x| x.into_vec()).collect()))
|
||||
.ok_or_else(|| "Failed to prove call: insufficient state".into())
|
||||
};
|
||||
|
||||
self.engine.genesis_epoch_data(&genesis, &call)
|
||||
}
|
||||
|
||||
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus.
|
||||
pub fn new_test() -> Spec { load_bundled!("null_morden") }
|
||||
|
||||
|
||||
@@ -212,7 +212,8 @@ pub fn check_proof(
|
||||
Err(_) => return ProvedExecution::BadProof,
|
||||
};
|
||||
|
||||
match state.execute(env_info, engine, transaction, TransactOptions::with_no_tracing(), true) {
|
||||
let options = TransactOptions::with_no_tracing().save_output_from_contract();
|
||||
match state.execute(env_info, engine, transaction, options, true) {
|
||||
Ok(executed) => ProvedExecution::Complete(executed),
|
||||
Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof,
|
||||
Err(e) => ProvedExecution::Failed(e),
|
||||
@@ -246,7 +247,7 @@ pub fn prove_transaction<H: AsHashDB + Send + Sync>(
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let options = TransactOptions::with_no_tracing().dont_check_nonce();
|
||||
let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract();
|
||||
match state.execute(env_info, engine, transaction, options, virt) {
|
||||
Err(ExecutionError::Internal(_)) => None,
|
||||
Err(e) => {
|
||||
|
||||
@@ -21,7 +21,8 @@ use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use native_contracts::TransactAcl as Contract;
|
||||
use client::{BlockChainClient, BlockId, ChainNotify};
|
||||
use util::{Address, H256, Bytes};
|
||||
use util::{Address, Bytes};
|
||||
use bigint::hash::H256;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use futures::{self, Future};
|
||||
use spec::CommonParams;
|
||||
|
||||
Reference in New Issue
Block a user