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:
committed by
Gav Wood
parent
eb9ee35d6c
commit
1bf2b27708
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user