store epoch transition proofs in DB

This commit is contained in:
Robert Habermeier
2017-04-19 14:58:19 +02:00
parent 6da6c755a5
commit a278dd5a0a
26 changed files with 234 additions and 69 deletions

View File

@@ -32,13 +32,13 @@ use util::kvdb::*;
// other
use basic_types::Seal;
use block::*;
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use blockchain::{BlockChain, BlockProvider, EpochTransition, TreeRoute, ImportRoute};
use blockchain::extras::TransactionAddress;
use client::Error as ClientError;
use client::{
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
MiningBlockChainClient, EngineClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
ChainNotify, PruningInfo,
ChainNotify, PruningInfo, ProvingBlockChainClient,
};
use encoded;
use engines::Engine;
@@ -578,15 +578,15 @@ impl Client {
// generate validation proof if the engine requires them.
// TODO: make conditional?
let generate_proof = {
let entering_new_epoch = {
use engines::EpochChange;
match self.engine.is_epoch_end(block.header(), Some(block_data), Some(&receipts)) {
EpochChange::Yes(_) => true,
EpochChange::No => false,
EpochChange::Yes(e, p) => Some((block.header().clone(), e, p)),
EpochChange::No => None,
EpochChange::Unsure(_) => {
warn!(target: "client", "Detected invalid engine implementation.");
warn!(target: "client", "Engine claims to require more block data, but everything provided.");
false
None
}
}
};
@@ -596,14 +596,53 @@ impl Client {
// TODO: Prove it with a test.
let mut state = block.drain();
if generate_proof {
debug!(target: "client", "Generating validation proof for block {}", hash);
// TODO
}
state.journal_under(&mut batch, number, hash).expect("DB commit failed");
let route = chain.insert_block(&mut batch, block_data, receipts);
if let Some((header, epoch, expected)) = entering_new_epoch {
use std::cell::RefCell;
use std::collections::BTreeSet;
debug!(target: "client", "Generating validation proof for block {}", hash);
// proof is two-part. state items read in lexicographical order,
// and the secondary "proof" part.
let read_values = RefCell::new(BTreeSet::new());
let block_id = BlockId::Hash(hash.clone());
let proof = {
let call = |a, d| {
let tx = self.contract_call_tx(block_id, a, d);
let (result, items) = self.prove_transaction(tx, block_id)
.ok_or_else(|| format!("Unable to make call to generate epoch proof."))?;
read_values.borrow_mut().extend(items);
Ok(result)
};
self.engine.epoch_proof(&header, &call)
};
match proof {
Ok(proof) => {
if proof != expected {
warn!(target: "client", "Extracted epoch change proof different than expected.");
warn!(target: "client", "Using a custom engine implementation?");
}
// insert into database, using the generated proof.
chain.insert_epoch_transition(&mut batch, epoch, EpochTransition {
block_hash: hash.clone(),
proof: proof,
state_proof: read_values.into_inner().into_iter().collect(),
});
}
Err(e) => {
warn!(target: "client", "Error generating epoch change proof for block {}: {}", hash, e);
warn!(target: "client", "Snapshots generated by this node will be incomplete.");
}
}
}
self.tracedb.read().import(&mut batch, TraceImportRequest {
traces: traces.into(),
block_hash: hash.clone(),
@@ -893,6 +932,20 @@ impl Client {
}
}
}
// transaction for calling contracts from services like engine.
// from the null sender, with 50M gas.
fn contract_call_tx(&self, block_id: BlockId, address: Address, data: Bytes) -> SignedTransaction {
let from = Address::default();
Transaction {
nonce: self.nonce(&from, block_id).unwrap_or_else(|| self.engine.account_start_nonce()),
action: Action::Call(address),
gas: U256::from(50_000_000),
gas_price: U256::default(),
value: U256::default(),
data: data,
}.fake_sign(from)
}
}
impl snapshot::DatabaseRestore for Client {
@@ -1483,15 +1536,7 @@ impl BlockChainClient for Client {
}
fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
let from = Address::default();
let transaction = Transaction {
nonce: self.latest_nonce(&from),
action: Action::Call(address),
gas: U256::from(50_000_000),
gas_price: U256::default(),
value: U256::default(),
data: data,
}.fake_sign(from);
let transaction = self.contract_call_tx(block_id, address, data);
self.call(&transaction, block_id, Default::default())
.map_err(|e| format!("{:?}", e))
@@ -1643,7 +1688,7 @@ impl MayPanic for Client {
}
}
impl ::client::ProvingBlockChainClient for Client {
impl ProvingBlockChainClient for Client {
fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec<Bytes>, H256)> {
self.state_at(id)
.and_then(move |state| state.prove_storage(key1, key2).ok())
@@ -1654,7 +1699,7 @@ impl ::client::ProvingBlockChainClient for Client {
.and_then(move |state| state.prove_account(key1).ok())
}
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<Vec<DBValue>> {
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec<DBValue>)> {
let (state, mut env_info) = match (self.state_at(id), self.env_info(id)) {
(Some(s), Some(e)) => (s, e),
_ => return None,
@@ -1669,8 +1714,9 @@ impl ::client::ProvingBlockChainClient for Client {
let res = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(&transaction, options);
match res {
Err(ExecutionError::Internal(_)) => return None,
_ => return Some(state.drop().1.extract_proof()),
Err(ExecutionError::Internal(_)) => None,
Err(_) => Some((Vec::new(), state.drop().1.extract_proof())),
Ok(res) => Some((res.output, state.drop().1.extract_proof())),
}
}
}

View File

@@ -767,7 +767,7 @@ impl ProvingBlockChainClient for TestBlockChainClient {
None
}
fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<Vec<DBValue>> {
fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec<DBValue>)> {
None
}
}

View File

@@ -324,5 +324,7 @@ pub trait ProvingBlockChainClient: BlockChainClient {
fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec<Bytes>, BasicAccount)>;
/// Prove execution of a transaction at the given block.
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<Vec<DBValue>>;
/// 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>)>;
}