light-fetch: Differentiate between out-of-gas/manual throw and use required gas from response on failure (#9824)
* fix start_gas, handle OOG exceptions & NotEnoughGas * Change START_GAS: 50_000 -> 60_000 * When the `OutOfGas exception` is received then try to double the gas until it succeeds or block gas limit is reached * When `NotEnoughBasGas error` is received then use the required gas provided in the response * fix(light-fetch): ensure block_gas_limit is tried Try the `block_gas_limit` before regard the execution as an error * Update rpc/src/v1/helpers/light_fetch.rs Co-Authored-By: niklasad1 <niklasadolfsson1@gmail.com>
This commit is contained in:
parent
a8617e2862
commit
23a29439c0
@ -19,12 +19,12 @@
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
|
||||
use light::on_demand::error::Error as OnDemandError;
|
||||
use ethcore::basic_account::BasicAccount;
|
||||
use ethcore::encoded;
|
||||
use ethcore::filter::Filter as EthcoreFilter;
|
||||
use ethcore::ids::BlockId;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethcore::executed::ExecutionError;
|
||||
|
||||
use jsonrpc_core::{Result, Error};
|
||||
use jsonrpc_core::futures::{future, Future};
|
||||
@ -38,6 +38,7 @@ use light::on_demand::{
|
||||
request, OnDemand, HeaderRef, Request as OnDemandRequest,
|
||||
Response as OnDemandResponse, ExecutionResult,
|
||||
};
|
||||
use light::on_demand::error::Error as OnDemandError;
|
||||
use light::request::Field;
|
||||
|
||||
use sync::LightSync;
|
||||
@ -202,8 +203,8 @@ impl LightFetch {
|
||||
/// Helper for getting proved execution.
|
||||
pub fn proved_read_only_execution(&self, req: CallRequest, num: Trailing<BlockNumber>) -> impl Future<Item = ExecutionResult, Error = Error> + Send {
|
||||
const DEFAULT_GAS_PRICE: u64 = 21_000;
|
||||
// starting gas when gas not provided.
|
||||
const START_GAS: u64 = 50_000;
|
||||
// (21000 G_transaction + 32000 G_create + some marginal to allow a few operations)
|
||||
const START_GAS: u64 = 60_000;
|
||||
|
||||
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
|
||||
let req: CallRequestHelper = req.into();
|
||||
@ -615,28 +616,41 @@ struct ExecuteParams {
|
||||
sync: Arc<LightSync>,
|
||||
}
|
||||
|
||||
// has a peer execute the transaction with given params. If `gas_known` is false,
|
||||
// this will double the gas on each `OutOfGas` error.
|
||||
// Has a peer execute the transaction with given params. If `gas_known` is false, this will set the `gas value` to the
|
||||
// `required gas value` unless it exceeds the block gas limit
|
||||
fn execute_read_only_tx(gas_known: bool, params: ExecuteParams) -> impl Future<Item = ExecutionResult, Error = Error> + Send {
|
||||
if !gas_known {
|
||||
Box::new(future::loop_fn(params, |mut params| {
|
||||
execute_read_only_tx(true, params.clone()).and_then(move |res| {
|
||||
match res {
|
||||
Ok(executed) => {
|
||||
// TODO: how to distinguish between actual OOG and
|
||||
// exception?
|
||||
if executed.exception.is_some() {
|
||||
let old_gas = params.tx.gas;
|
||||
params.tx.gas = params.tx.gas * 2u32;
|
||||
if params.tx.gas > params.hdr.gas_limit() {
|
||||
params.tx.gas = old_gas;
|
||||
// `OutOfGas` exception, try double the gas
|
||||
if let Some(vm::Error::OutOfGas) = executed.exception {
|
||||
// block gas limit already tried, regard as an error and don't retry
|
||||
if params.tx.gas >= params.hdr.gas_limit() {
|
||||
trace!(target: "light_fetch", "OutOutGas exception received, gas increase: failed");
|
||||
} else {
|
||||
params.tx.gas = cmp::min(params.tx.gas * 2_u32, params.hdr.gas_limit());
|
||||
trace!(target: "light_fetch", "OutOutGas exception received, gas increased to {}",
|
||||
params.tx.gas);
|
||||
return Ok(future::Loop::Continue(params))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(future::Loop::Break(Ok(executed)))
|
||||
}
|
||||
Err(ExecutionError::NotEnoughBaseGas { required, got }) => {
|
||||
trace!(target: "light_fetch", "Not enough start gas provided required: {}, got: {}",
|
||||
required, got);
|
||||
if required <= params.hdr.gas_limit() {
|
||||
params.tx.gas = required;
|
||||
return Ok(future::Loop::Continue(params))
|
||||
} else {
|
||||
warn!(target: "light_fetch",
|
||||
"Required gas is bigger than block header's gas dropping the request");
|
||||
Ok(future::Loop::Break(Err(ExecutionError::NotEnoughBaseGas { required, got })))
|
||||
}
|
||||
}
|
||||
// Non-recoverable execution error
|
||||
failed => Ok(future::Loop::Break(failed)),
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user