Propagate trie errors upwards from State (#4655)

* state backend trait mirroring state_db API

* minimal state backend trait

make state module public

* fix json tests

* return errors on database corruption

* fix tests, json tests

* fix remainder of build

* add Backend bound on state
This commit is contained in:
Robert Habermeier
2017-02-26 13:10:50 +01:00
committed by Gav Wood
parent eb9ee35d6c
commit 1bf2b27708
27 changed files with 598 additions and 456 deletions

View File

@@ -890,17 +890,20 @@ impl BlockChainClient for Client {
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
let sender = t.sender();
let balance = state.balance(&sender);
let balance = state.balance(&sender).map_err(|_| CallError::StateCorrupt)?;
let needed_balance = t.value + t.gas * t.gas_price;
if balance < needed_balance {
// give the sender a sufficient balance
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty)
.map_err(|_| CallError::StateCorrupt)?;
}
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options)?;
// TODO gav move this into Executive.
ret.state_diff = original_state.map(|original| state.diff_from(original));
if let Some(original) = original_state {
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
}
Ok(ret)
}
@@ -921,7 +924,7 @@ impl BlockChainClient for Client {
// that's just a copy of the state.
let original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
let sender = t.sender();
let balance = original_state.balance(&sender);
let balance = original_state.balance(&sender).map_err(ExecutionError::from)?;
let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };
let cond = |gas| {
@@ -933,27 +936,29 @@ impl BlockChainClient for Client {
let needed_balance = tx.value + tx.gas * tx.gas_price;
if balance < needed_balance {
// give the sender a sufficient balance
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty);
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty)
.map_err(ExecutionError::from)?;
}
Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm)
Ok(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm)
.transact(&tx, options.clone())
.map(|r| r.exception.is_none())
.unwrap_or(false)
.unwrap_or(false))
};
let mut upper = header.gas_limit();
if !cond(upper) {
if !cond(upper)? {
// impossible at block gas limit - try `UPPER_CEILING` instead.
// TODO: consider raising limit by powers of two.
upper = UPPER_CEILING.into();
if !cond(upper) {
if !cond(upper)? {
trace!(target: "estimate_gas", "estimate_gas failed with {}", upper);
return Err(CallError::Execution(ExecutionError::Internal))
let err = ExecutionError::Internal(format!("Requires higher than upper limit of {}", upper));
return Err(err.into())
}
}
let lower = t.gas_required(&self.engine.schedule(&env_info)).into();
if cond(lower) {
if cond(lower)? {
trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower);
return Ok(lower)
}
@@ -961,23 +966,25 @@ impl BlockChainClient for Client {
/// Find transition point between `lower` and `upper` where `cond` changes from `false` to `true`.
/// Returns the lowest value between `lower` and `upper` for which `cond` returns true.
/// We assert: `cond(lower) = false`, `cond(upper) = true`
fn binary_chop<F>(mut lower: U256, mut upper: U256, mut cond: F) -> U256 where F: FnMut(U256) -> bool {
fn binary_chop<F, E>(mut lower: U256, mut upper: U256, mut cond: F) -> Result<U256, E>
where F: FnMut(U256) -> Result<bool, E>
{
while upper - lower > 1.into() {
let mid = (lower + upper) / 2.into();
trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper);
let c = cond(mid);
let c = cond(mid)?;
match c {
true => upper = mid,
false => lower = mid,
};
trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper);
}
upper
Ok(upper)
}
// binary chop to non-excepting call with gas somewhere between 21000 and block gas limit
trace!(target: "estimate_gas", "estimate_gas chopping {} .. {}", lower, upper);
Ok(binary_chop(lower, upper, cond))
binary_chop(lower, upper, cond)
}
fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
@@ -1006,17 +1013,16 @@ impl BlockChainClient for Client {
let rest = txs.split_off(address.index);
for t in txs {
let t = SignedTransaction::new(t).expect(PROOF);
match Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(&t, Default::default()) {
Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; }
Err(ee) => { return Err(CallError::Execution(ee)) }
}
let x = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(&t, Default::default())?;
env_info.gas_used = env_info.gas_used + x.gas_used;
}
let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
let t = SignedTransaction::new(first).expect(PROOF);
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
let mut ret = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(&t, options)?;
ret.state_diff = original_state.map(|original| state.diff_from(original));
if let Some(original) = original_state {
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?)
}
Ok(ret)
}
@@ -1108,11 +1114,11 @@ impl BlockChainClient for Client {
}
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
self.state_at(id).map(|s| s.nonce(address))
self.state_at(id).and_then(|s| s.nonce(address).ok())
}
fn storage_root(&self, address: &Address, id: BlockId) -> Option<H256> {
self.state_at(id).and_then(|s| s.storage_root(address))
self.state_at(id).and_then(|s| s.storage_root(address).ok()).and_then(|x| x)
}
fn block_hash(&self, id: BlockId) -> Option<H256> {
@@ -1121,15 +1127,15 @@ impl BlockChainClient for Client {
}
fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>> {
self.state_at(id).map(|s| s.code(address).map(|c| (*c).clone()))
self.state_at(id).and_then(|s| s.code(address).ok()).map(|c| c.map(|c| (&*c).clone()))
}
fn balance(&self, address: &Address, id: BlockId) -> Option<U256> {
self.state_at(id).map(|s| s.balance(address))
self.state_at(id).and_then(|s| s.balance(address).ok())
}
fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option<H256> {
self.state_at(id).map(|s| s.storage_at(address, position))
self.state_at(id).and_then(|s| s.storage_at(address, position).ok())
}
fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option<Vec<Address>> {
@@ -1182,7 +1188,7 @@ impl BlockChainClient for Client {
};
let root = match state.storage_root(account) {
Some(root) => root,
Ok(Some(root)) => root,
_ => return None,
};