Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
Implement out of gas exception (0xPolygonZero#1328)
Browse files Browse the repository at this point in the history
* Implement out of gas exception

* Use gas constants in gas_cost_for_opcode

* Remove comment
  • Loading branch information
hratoanina authored Nov 15, 2023
1 parent 01f229a commit 0e63e66
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 9 deletions.
134 changes: 132 additions & 2 deletions evm/src/cpu/kernel/asm/core/exception.asm
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,31 @@ global exception_jumptable:


global exc_out_of_gas:
// TODO
%jump(fault_exception)
// stack: trap_info
%ctx_gas_limit
// stack: gas_limit, trap_info
DUP2 %shr_const(192)
// stack: gas_used, gas_limit, trap_info
DUP2 DUP2
// stack: gas_used, gas_limit, gas_used, gas_limit, trap_info
// If gas_used is already over the limit, panic. The exception should have
// been raised earlier.
GT %jumpi(panic)
// stack: gas_used, gas_limit, trap_info
DUP3 %opcode_from_exp_trap_info
// stack: opcode, gas_used, gas_limit, trap_info
%add_const(gas_cost_for_opcode)
%mload_kernel_code
// stack: gas_cost, gas_used, gas_limit, trap_info
ADD
// stack: new_gas_used, gas_limit, trap_info
GT
// stack: is_oog, trap_info
SWAP1 POP
// stack: is_oog
%jumpi(fault_exception)
// If we didn't jump, we shouldn't have raised the exception.
PANIC


global exc_invalid_opcode:
Expand Down Expand Up @@ -302,3 +325,110 @@ min_stack_len_for_opcode:
BYTES 2 // 0xfd, REVERT
BYTES 0 // 0xfe, invalid
BYTES 1 // 0xff, SELFDESTRUCT

// A zero indicates either that the opcode is kernel-only,
// or that it's handled with a syscall.
gas_cost_for_opcode:
BYTES 0 // 0x00, STOP
BYTES @GAS_VERYLOW // 0x01, ADD
BYTES @GAS_LOW // 0x02, MUL
BYTES @GAS_VERYLOW // 0x03, SUB
BYTES @GAS_LOW // 0x04, DIV
BYTES @GAS_LOW // 0x05, SDIV
BYTES @GAS_LOW // 0x06, MOD
BYTES @GAS_LOW // 0x07, SMOD
BYTES @GAS_MID // 0x08, ADDMOD
BYTES @GAS_MID // 0x09, MULMOD
BYTES 0 // 0x0a, EXP
BYTES 0 // 0x0b, SIGNEXTEND
%rep 4 // 0x0c-0x0f, invalid
BYTES 0
%endrep

BYTES @GAS_VERYLOW // 0x10, LT
BYTES @GAS_VERYLOW // 0x11, GT
BYTES @GAS_VERYLOW // 0x12, SLT
BYTES @GAS_VERYLOW // 0x13, SGT
BYTES @GAS_VERYLOW // 0x14, EQ
BYTES @GAS_VERYLOW // 0x15, ISZERO
BYTES @GAS_VERYLOW // 0x16, AND
BYTES @GAS_VERYLOW // 0x17, OR
BYTES @GAS_VERYLOW // 0x18, XOR
BYTES @GAS_VERYLOW // 0x19, NOT
BYTES @GAS_VERYLOW // 0x1a, BYTE
BYTES @GAS_VERYLOW // 0x1b, SHL
BYTES @GAS_VERYLOW // 0x1c, SHR
BYTES @GAS_VERYLOW // 0x1d, SAR
BYTES 0 // 0x1e, invalid
BYTES 0 // 0x1f, invalid

BYTES 0 // 0x20, KECCAK256
%rep 15 // 0x21-0x2f, invalid
BYTES 0
%endrep

%rep 25 //0x30-0x48, only syscalls
BYTES 0
%endrep

%rep 7 // 0x49-0x4f, invalid
BYTES 0
%endrep

BYTES @GAS_BASE // 0x50, POP
BYTES 0 // 0x51, MLOAD
BYTES 0 // 0x52, MSTORE
BYTES 0 // 0x53, MSTORE8
BYTES 0 // 0x54, SLOAD
BYTES 0 // 0x55, SSTORE
BYTES @GAS_MID // 0x56, JUMP
BYTES @GAS_HIGH // 0x57, JUMPI
BYTES @GAS_BASE // 0x58, PC
BYTES 0 // 0x59, MSIZE
BYTES 0 // 0x5a, GAS
BYTES @GAS_JUMPDEST // 0x5b, JUMPDEST
%rep 3 // 0x5c-0x5e, invalid
BYTES 0
%endrep

BYTES @GAS_BASE // 0x5f, PUSH0
%rep 32 // 0x60-0x7f, PUSH1-PUSH32
BYTES @GAS_VERYLOW
%endrep

%rep 16 // 0x80-0x8f, DUP1-DUP16
BYTES @GAS_VERYLOW
%endrep

%rep 16 // 0x90-0x9f, SWAP1-SWAP16
BYTES @GAS_VERYLOW
%endrep

BYTES 0 // 0xa0, LOG0
BYTES 0 // 0xa1, LOG1
BYTES 0 // 0xa2, LOG2
BYTES 0 // 0xa3, LOG3
BYTES 0 // 0xa4, LOG4
%rep 11 // 0xa5-0xaf, invalid
BYTES 0
%endrep

%rep 64 // 0xb0-0xef, invalid
BYTES 0
%endrep

BYTES 0 // 0xf0, CREATE
BYTES 0 // 0xf1, CALL
BYTES 0 // 0xf2, CALLCODE
BYTES 0 // 0xf3, RETURN
BYTES 0 // 0xf4, DELEGATECALL
BYTES 0 // 0xf5, CREATE2
%rep 4 // 0xf6-0xf9, invalid
BYTES 0
%endrep
BYTES 0 // 0xfa, STATICCALL
BYTES 0 // 0xfb, invalid
BYTES 0 // 0xfc, invalid
BYTES 0 // 0xfd, REVERT
BYTES 0 // 0xfe, invalid
BYTES 0 // 0xff, SELFDESTRUCT
33 changes: 30 additions & 3 deletions evm/src/cpu/kernel/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use keccak_hash::keccak;
use log::debug;
use serde::{Deserialize, Serialize};

use super::ast::PushTarget;
use super::ast::{BytesTarget, PushTarget};
use crate::cpu::kernel::ast::Item::LocalLabelDeclaration;
use crate::cpu::kernel::ast::{File, Item, StackReplacement};
use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode};
Expand Down Expand Up @@ -277,6 +277,23 @@ fn inline_constants(body: Vec<Item>, constants: &HashMap<String, U256>) -> Vec<I
.map(|item| {
if let Item::Push(PushTarget::Constant(c)) = item {
Item::Push(PushTarget::Literal(resolve_const(c)))
} else if let Item::Bytes(targets) = item {
let targets = targets
.into_iter()
.map(|target| {
if let BytesTarget::Constant(c) = target {
let c = resolve_const(c);
assert!(
c < U256::from(256),
"Constant in a BYTES object should be a byte"
);
BytesTarget::Literal(c.byte(0))
} else {
target
}
})
.collect();
Item::Bytes(targets)
} else if let Item::StackManipulation(from, to) = item {
let to = to
.into_iter()
Expand Down Expand Up @@ -387,7 +404,14 @@ fn assemble_file(
Item::StandardOp(opcode) => {
code.push(get_opcode(&opcode));
}
Item::Bytes(bytes) => code.extend(bytes),
Item::Bytes(targets) => {
for target in targets {
match target {
BytesTarget::Literal(n) => code.push(n),
BytesTarget::Constant(c) => panic!("Constant wasn't inlined: {c}"),
}
}
}
Item::Jumptable(labels) => {
for label in labels {
let bytes = look_up_label(&label, &local_labels, global_labels);
Expand Down Expand Up @@ -507,7 +531,10 @@ mod tests {
#[test]
fn literal_bytes() {
let file = File {
body: vec![Item::Bytes(vec![0x12, 42]), Item::Bytes(vec![0xFE, 255])],
body: vec![
Item::Bytes(vec![BytesTarget::Literal(0x12), BytesTarget::Literal(42)]),
Item::Bytes(vec![BytesTarget::Literal(0xFE), BytesTarget::Literal(255)]),
],
};
let code = assemble(vec![file], HashMap::new(), false).code;
assert_eq!(code, vec![0x12, 42, 0xfe, 255]);
Expand Down
9 changes: 8 additions & 1 deletion evm/src/cpu/kernel/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub(crate) enum Item {
/// Any opcode besides a PUSH opcode.
StandardOp(String),
/// Literal hex data; should contain an even number of hex chars.
Bytes(Vec<u8>),
Bytes(Vec<BytesTarget>),
/// Creates a table of addresses from a list of labels.
Jumptable(Vec<String>),
}
Expand Down Expand Up @@ -75,3 +75,10 @@ pub(crate) enum PushTarget {
MacroVar(String),
Constant(String),
}

/// The target of a `BYTES` item.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) enum BytesTarget {
Literal(u8),
Constant(String),
}
3 changes: 2 additions & 1 deletion evm/src/cpu/kernel/evm_asm.pest
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ local_label_decl = ${ identifier ~ ":" }
macro_label_decl = ${ "%%" ~ identifier ~ ":" }
macro_label = ${ "%%" ~ identifier }

bytes_item = { ^"BYTES " ~ literal ~ ("," ~ literal)* }
bytes_item = { ^"BYTES " ~ bytes_target ~ ("," ~ bytes_target)* }
bytes_target = { literal | constant }
jumptable_item = { ^"JUMPTABLE " ~ identifier ~ ("," ~ identifier)* }
push_instruction = { ^"PUSH " ~ push_target }
push_target = { literal | identifier | macro_label | variable | constant }
Expand Down
14 changes: 12 additions & 2 deletions evm/src/cpu/kernel/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ethereum_types::U256;
use pest::iterators::Pair;
use pest::Parser;

use super::ast::StackPlaceholder;
use super::ast::{BytesTarget, StackPlaceholder};
use crate::cpu::kernel::ast::{File, Item, PushTarget, StackReplacement};

/// Parses EVM assembly code.
Expand Down Expand Up @@ -38,7 +38,7 @@ fn parse_item(item: Pair<Rule>) -> Item {
Rule::macro_label_decl => {
Item::MacroLabelDeclaration(item.into_inner().next().unwrap().as_str().into())
}
Rule::bytes_item => Item::Bytes(item.into_inner().map(parse_literal_u8).collect()),
Rule::bytes_item => Item::Bytes(item.into_inner().map(parse_bytes_target).collect()),
Rule::jumptable_item => {
Item::Jumptable(item.into_inner().map(|i| i.as_str().into()).collect())
}
Expand Down Expand Up @@ -167,6 +167,16 @@ fn parse_push_target(target: Pair<Rule>) -> PushTarget {
}
}

fn parse_bytes_target(target: Pair<Rule>) -> BytesTarget {
assert_eq!(target.as_rule(), Rule::bytes_target);
let inner = target.into_inner().next().unwrap();
match inner.as_rule() {
Rule::literal => BytesTarget::Literal(parse_literal_u8(inner)),
Rule::constant => BytesTarget::Constant(inner.into_inner().next().unwrap().as_str().into()),
_ => panic!("Unexpected {:?}", inner.as_rule()),
}
}

fn parse_literal_u8(literal: Pair<Rule>) -> u8 {
let literal = literal.into_inner().next().unwrap();
match literal.as_rule() {
Expand Down
18 changes: 18 additions & 0 deletions evm/src/witness/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use super::memory::{MemoryOp, MemoryOpKind};
use super::util::fill_channel_with_value;
use crate::cpu::columns::CpuColumnsView;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::constants::context_metadata::ContextMetadata;
use crate::cpu::stack::{
EQ_STACK_BEHAVIOR, IS_ZERO_STACK_BEHAVIOR, JUMPI_OP, JUMP_OP, STACK_BEHAVIORS,
};
Expand Down Expand Up @@ -273,6 +274,23 @@ fn perform_op<F: Field>(

state.registers.gas_used += gas_to_charge(op);

let gas_limit_address = MemoryAddress {
context: state.registers.context,
segment: Segment::ContextMetadata as usize,
virt: ContextMetadata::GasLimit as usize,
};
if !state.registers.is_kernel {
let gas_limit = TryInto::<u64>::try_into(state.memory.get(gas_limit_address));
match gas_limit {
Ok(limit) => {
if state.registers.gas_used > limit {
return Err(ProgramError::OutOfGas);
}
}
Err(_) => return Err(ProgramError::IntegerTooLarge),
}
}

Ok(())
}

Expand Down

0 comments on commit 0e63e66

Please sign in to comment.