mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 15:11:02 +00:00
Allow to distinguish out of gas from other traps (#4883)
* contracts: Allow to distinguish out of gas from other traps
When a contract encounters a runtime error a wasm trap is
triggered and the execution is halted. Currently, no matter
what was the cause for the trap it is always reported as:
DispatchError::Other("contract trapped during execution").
However, the trap that is triggered if a contract exhausts
its gas budget is particulary interesting. Therefore we add
a seperate error message for this cause:
DispatchError::Other("ran out of gas during contract execution").
A test is added hat executes a contract that never terminates.
Therefore it always exhausts is gas budget.
* fixup! contracts: Allow to distinguish out of gas from other traps
Remove overlong lines.
* fixup! contracts: Allow to distinguish out of gas from other traps
Rename Contract to Contracts
This commit is contained in:
committed by
GitHub
parent
5b7512e2e4
commit
b999911bcf
@@ -39,6 +39,8 @@ const TRAP_RETURN_CODE: u32 = 0x0100;
|
||||
enum SpecialTrap {
|
||||
/// Signals that trap was generated in response to call `ext_return` host function.
|
||||
Return(Vec<u8>),
|
||||
/// Signals that trap was generated because the contract exhausted its gas limit.
|
||||
OutOfGas,
|
||||
}
|
||||
|
||||
/// Can only be used for one call.
|
||||
@@ -74,9 +76,21 @@ pub(crate) fn to_execution_result<E: Ext>(
|
||||
runtime: Runtime<E>,
|
||||
sandbox_result: Result<sp_sandbox::ReturnValue, sp_sandbox::Error>,
|
||||
) -> ExecResult {
|
||||
// Special case. The trap was the result of the execution `return` host function.
|
||||
if let Some(SpecialTrap::Return(data)) = runtime.special_trap {
|
||||
return Ok(ExecReturnValue { status: STATUS_SUCCESS, data });
|
||||
match runtime.special_trap {
|
||||
// The trap was the result of the execution `return` host function.
|
||||
Some(SpecialTrap::Return(data)) => {
|
||||
return Ok(ExecReturnValue {
|
||||
status: STATUS_SUCCESS,
|
||||
data,
|
||||
})
|
||||
}
|
||||
Some(SpecialTrap::OutOfGas) => {
|
||||
return Err(ExecError {
|
||||
reason: "ran out of gas during contract execution".into(),
|
||||
buffer: runtime.scratch_buf,
|
||||
})
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// Check the exact type of the error.
|
||||
@@ -179,11 +193,15 @@ impl<T: Trait> Token<T> for RuntimeToken {
|
||||
fn charge_gas<T: Trait, Tok: Token<T>>(
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
metadata: &Tok::Metadata,
|
||||
special_trap: &mut Option<SpecialTrap>,
|
||||
token: Tok,
|
||||
) -> Result<(), sp_sandbox::HostError> {
|
||||
match gas_meter.charge(metadata, token) {
|
||||
GasMeterResult::Proceed => Ok(()),
|
||||
GasMeterResult::OutOfGas => Err(sp_sandbox::HostError),
|
||||
GasMeterResult::OutOfGas => {
|
||||
*special_trap = Some(SpecialTrap::OutOfGas);
|
||||
Err(sp_sandbox::HostError)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +218,12 @@ fn read_sandbox_memory<E: Ext>(
|
||||
ptr: u32,
|
||||
len: u32,
|
||||
) -> Result<Vec<u8>, sp_sandbox::HostError> {
|
||||
charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReadMemory(len))?;
|
||||
charge_gas(
|
||||
ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::ReadMemory(len),
|
||||
)?;
|
||||
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
ctx.memory.get(ptr, buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?;
|
||||
@@ -220,7 +243,12 @@ fn read_sandbox_memory_into_scratch<E: Ext>(
|
||||
ptr: u32,
|
||||
len: u32,
|
||||
) -> Result<(), sp_sandbox::HostError> {
|
||||
charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReadMemory(len))?;
|
||||
charge_gas(
|
||||
ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::ReadMemory(len),
|
||||
)?;
|
||||
|
||||
ctx.scratch_buf.resize(len as usize, 0);
|
||||
ctx.memory.get(ptr, ctx.scratch_buf.as_mut_slice()).map_err(|_| sp_sandbox::HostError)?;
|
||||
@@ -240,7 +268,12 @@ fn read_sandbox_memory_into_buf<E: Ext>(
|
||||
ptr: u32,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(), sp_sandbox::HostError> {
|
||||
charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReadMemory(buf.len() as u32))?;
|
||||
charge_gas(
|
||||
ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::ReadMemory(buf.len() as u32),
|
||||
)?;
|
||||
|
||||
ctx.memory.get(ptr, buf).map_err(Into::into)
|
||||
}
|
||||
@@ -273,12 +306,18 @@ fn read_sandbox_memory_as<E: Ext, D: Decode>(
|
||||
/// - designated area is not within the bounds of the sandbox memory.
|
||||
fn write_sandbox_memory<T: Trait>(
|
||||
schedule: &Schedule,
|
||||
special_trap: &mut Option<SpecialTrap>,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
memory: &sp_sandbox::Memory,
|
||||
ptr: u32,
|
||||
buf: &[u8],
|
||||
) -> Result<(), sp_sandbox::HostError> {
|
||||
charge_gas(gas_meter, schedule, RuntimeToken::WriteMemory(buf.len() as u32))?;
|
||||
charge_gas(
|
||||
gas_meter,
|
||||
schedule,
|
||||
special_trap,
|
||||
RuntimeToken::WriteMemory(buf.len() as u32),
|
||||
)?;
|
||||
|
||||
memory.set(ptr, buf)?;
|
||||
|
||||
@@ -300,7 +339,12 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// - amount: How much gas is used.
|
||||
gas(ctx, amount: u32) => {
|
||||
charge_gas(&mut ctx.gas_meter, ctx.schedule, RuntimeToken::Explicit(amount))?;
|
||||
charge_gas(
|
||||
&mut ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::Explicit(amount)
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
@@ -520,7 +564,12 @@ define_env!(Env, <E: Ext>,
|
||||
//
|
||||
// This is the only way to return a data buffer to the caller.
|
||||
ext_return(ctx, data_ptr: u32, data_len: u32) => {
|
||||
charge_gas(ctx.gas_meter, ctx.schedule, RuntimeToken::ReturnData(data_len))?;
|
||||
charge_gas(
|
||||
ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::ReturnData(data_len)
|
||||
)?;
|
||||
|
||||
read_sandbox_memory_into_scratch(ctx, data_ptr, data_len)?;
|
||||
let output_buf = mem::replace(&mut ctx.scratch_buf, Vec::new());
|
||||
@@ -652,7 +701,12 @@ define_env!(Env, <E: Ext>,
|
||||
let balance_fee = <<E as Ext>::T as Trait>::ComputeDispatchFee::compute_dispatch_fee(&call);
|
||||
approx_gas_for_balance(ctx.gas_meter.gas_price(), balance_fee)
|
||||
};
|
||||
charge_gas(&mut ctx.gas_meter, ctx.schedule, RuntimeToken::ComputedDispatchFee(fee))?;
|
||||
charge_gas(
|
||||
&mut ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::ComputedDispatchFee(fee)
|
||||
)?;
|
||||
|
||||
ctx.ext.note_dispatch_call(call);
|
||||
|
||||
@@ -756,6 +810,7 @@ define_env!(Env, <E: Ext>,
|
||||
// Finally, perform the write.
|
||||
write_sandbox_memory(
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
ctx.gas_meter,
|
||||
&ctx.memory,
|
||||
dest_ptr,
|
||||
@@ -803,6 +858,7 @@ define_env!(Env, <E: Ext>,
|
||||
charge_gas(
|
||||
ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::DepositEvent(topics.len() as u32, data_len)
|
||||
)?;
|
||||
ctx.ext.deposit_event(topics, event_data);
|
||||
|
||||
Reference in New Issue
Block a user