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