mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 06:43:07 +00:00
Kernel code to do jumpdest analysis
This commit is contained in:
parent
08cabf2ad8
commit
d23cecfcd8
@ -16,6 +16,7 @@ pub(crate) fn combined_kernel() -> Kernel {
|
|||||||
include_str!("asm/core/create_addresses.asm"),
|
include_str!("asm/core/create_addresses.asm"),
|
||||||
include_str!("asm/core/intrinsic_gas.asm"),
|
include_str!("asm/core/intrinsic_gas.asm"),
|
||||||
include_str!("asm/core/invalid.asm"),
|
include_str!("asm/core/invalid.asm"),
|
||||||
|
include_str!("asm/core/jumpdest_analysis.asm"),
|
||||||
include_str!("asm/core/nonce.asm"),
|
include_str!("asm/core/nonce.asm"),
|
||||||
include_str!("asm/core/process_txn.asm"),
|
include_str!("asm/core/process_txn.asm"),
|
||||||
include_str!("asm/core/syscall.asm"),
|
include_str!("asm/core/syscall.asm"),
|
||||||
|
|||||||
@ -96,8 +96,8 @@ extcodecopy_end:
|
|||||||
// Loads the code at `address` in the `SEGMENT_KERNEL_ACCOUNT_CODE` at the current context and starting at offset 0.
|
// Loads the code at `address` in the `SEGMENT_KERNEL_ACCOUNT_CODE` at the current context and starting at offset 0.
|
||||||
// Checks that the hash of the loaded code corresponds to the `codehash` in the state trie.
|
// Checks that the hash of the loaded code corresponds to the `codehash` in the state trie.
|
||||||
// Pre stack: address, retdest
|
// Pre stack: address, retdest
|
||||||
// Post stack: extcodesize(address)
|
// Post stack: code_len
|
||||||
load_code:
|
global load_code:
|
||||||
%stack (address, retdest) -> (extcodehash, address, load_code_ctd, retdest)
|
%stack (address, retdest) -> (extcodehash, address, load_code_ctd, retdest)
|
||||||
JUMP
|
JUMP
|
||||||
load_code_ctd:
|
load_code_ctd:
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
// Loads some prover-provided contract code into the code segment of memory,
|
// Loads some prover-provided contract code into the code segment of memory,
|
||||||
// then hashes the code and returns the hash.
|
// then hashes the code and returns the hash.
|
||||||
|
|
||||||
global bootload_contract:
|
global bootload_contract:
|
||||||
// stack: retdest
|
// stack: address, retdest
|
||||||
|
// %stack (address, retdest) -> (address, after_load_code, retdest)
|
||||||
|
// %jump(load_code)
|
||||||
|
PANIC // TODO
|
||||||
|
|
||||||
// TODO
|
global bootload_code:
|
||||||
|
// stack: code_len, retdest
|
||||||
|
PANIC // TODO
|
||||||
|
|
||||||
// stack: code_hash, retdest
|
// stack: code_hash, retdest
|
||||||
SWAP1
|
SWAP1
|
||||||
|
|||||||
64
evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm
Normal file
64
evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Populates @SEGMENT_JUMPDEST_BITS for the given context's code.
|
||||||
|
// Pre stack: ctx, code_len, retdest
|
||||||
|
// Post stack: (empty)
|
||||||
|
global jumpdest_analysis:
|
||||||
|
// stack: ctx, code_len, retdest
|
||||||
|
PUSH 0 // i = 0
|
||||||
|
|
||||||
|
loop:
|
||||||
|
// stack: i, ctx, code_len, retdest
|
||||||
|
// Ideally we would break if i >= code_len, but checking i > code_len is
|
||||||
|
// cheaper. It doesn't hurt to over-read by 1, since we'll read 0 which is
|
||||||
|
// a no-op.
|
||||||
|
DUP3 DUP2 GT // i > code_len
|
||||||
|
%jumpi(return)
|
||||||
|
|
||||||
|
// stack: i, ctx, code_len, retdest
|
||||||
|
%stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx)
|
||||||
|
MLOAD_GENERAL
|
||||||
|
// stack: opcode, i, ctx, code_len, retdest
|
||||||
|
|
||||||
|
DUP1 %eq_const(0x5b)
|
||||||
|
// stack: opcode == JUMPDEST, opcode, i, ctx, code_len, retdest
|
||||||
|
%jumpi(encountered_jumpdest)
|
||||||
|
|
||||||
|
// stack: opcode, i, ctx, code_len, retdest
|
||||||
|
%code_bytes_to_skip
|
||||||
|
// stack: bytes_to_skip, i, ctx, code_len, retdest
|
||||||
|
ADD
|
||||||
|
%jump(continue)
|
||||||
|
|
||||||
|
encountered_jumpdest:
|
||||||
|
// stack: opcode, i, ctx, code_len, retdest
|
||||||
|
POP
|
||||||
|
// stack: i, ctx, code_len, retdest
|
||||||
|
%stack (i, ctx) -> (ctx, @SEGMENT_JUMPDEST_BITS, i, 1, i, ctx)
|
||||||
|
MSTORE_GENERAL
|
||||||
|
|
||||||
|
continue:
|
||||||
|
// stack: i, ctx, code_len, retdest
|
||||||
|
%increment
|
||||||
|
%jump(loop)
|
||||||
|
|
||||||
|
return:
|
||||||
|
// stack: i, ctx, code_len, retdest
|
||||||
|
%pop3
|
||||||
|
JUMP
|
||||||
|
|
||||||
|
// Determines how many bytes to skip, if any, based on the opcode we read.
|
||||||
|
// If we read a PUSH<n> opcode, we skip over n bytes, otherwise we skip 0.
|
||||||
|
//
|
||||||
|
// Note that the range of PUSH opcodes is [0x60, 0x80). I.e. PUSH1 is 0x60
|
||||||
|
// and PUSH32 is 0x7f.
|
||||||
|
%macro code_bytes_to_skip
|
||||||
|
// stack: opcode
|
||||||
|
%sub_const(0x60)
|
||||||
|
// stack: opcode - 0x60
|
||||||
|
DUP1 %lt_const(0x20)
|
||||||
|
// stack: is_push_opcode, opcode - 0x60
|
||||||
|
SWAP1
|
||||||
|
%increment // n = opcode - 0x60 + 1
|
||||||
|
// stack: n, is_push_opcode
|
||||||
|
MUL
|
||||||
|
// stack: bytes_to_skip
|
||||||
|
%endmacro
|
||||||
@ -202,6 +202,25 @@ impl<'a> Interpreter<'a> {
|
|||||||
rlp.into_iter().map(U256::from).collect();
|
rlp.into_iter().map(U256::from).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_code(&mut self, context: usize, code: Vec<u8>) {
|
||||||
|
assert_ne!(context, 0, "Can't modify kernel code.");
|
||||||
|
while self.memory.context_memory.len() <= context {
|
||||||
|
self.memory
|
||||||
|
.context_memory
|
||||||
|
.push(MemoryContextState::default());
|
||||||
|
}
|
||||||
|
self.memory.context_memory[context].segments[Segment::Code as usize].content =
|
||||||
|
code.into_iter().map(U256::from).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec<bool> {
|
||||||
|
self.memory.context_memory[context].segments[Segment::JumpdestBits as usize]
|
||||||
|
.content
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.bit(0))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn incr(&mut self, n: usize) {
|
fn incr(&mut self, n: usize) {
|
||||||
self.offset += n;
|
self.offset += n;
|
||||||
}
|
}
|
||||||
|
|||||||
42
evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs
Normal file
42
evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::cpu::kernel::aggregator::KERNEL;
|
||||||
|
use crate::cpu::kernel::interpreter::Interpreter;
|
||||||
|
use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_jumpdest_analysis() -> Result<()> {
|
||||||
|
let jumpdest_analysis = KERNEL.global_labels["jumpdest_analysis"];
|
||||||
|
const CONTEXT: usize = 3; // arbitrary
|
||||||
|
|
||||||
|
let add = get_opcode("ADD");
|
||||||
|
let push2 = get_push_opcode(2);
|
||||||
|
let jumpdest = get_opcode("JUMPDEST");
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let code: Vec<u8> = vec![
|
||||||
|
add,
|
||||||
|
jumpdest,
|
||||||
|
push2,
|
||||||
|
jumpdest, // part of PUSH2
|
||||||
|
jumpdest, // part of PUSH2
|
||||||
|
jumpdest,
|
||||||
|
add,
|
||||||
|
jumpdest,
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected_jumpdest_bits = vec![false, true, false, false, false, true, false, true];
|
||||||
|
|
||||||
|
// Contract creation transaction.
|
||||||
|
let initial_stack = vec![0xDEADBEEFu32.into(), code.len().into(), CONTEXT.into()];
|
||||||
|
let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack);
|
||||||
|
interpreter.set_code(CONTEXT, code);
|
||||||
|
interpreter.run()?;
|
||||||
|
assert_eq!(interpreter.stack(), vec![]);
|
||||||
|
assert_eq!(
|
||||||
|
interpreter.get_jumpdest_bits(CONTEXT),
|
||||||
|
expected_jumpdest_bits
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -1,2 +1,3 @@
|
|||||||
mod create_addresses;
|
mod create_addresses;
|
||||||
mod intrinsic_gas;
|
mod intrinsic_gas;
|
||||||
|
mod jumpdest_analysis;
|
||||||
|
|||||||
@ -38,10 +38,11 @@ pub(crate) enum Segment {
|
|||||||
/// A table of values 2^i for i=0..255 for use with shift
|
/// A table of values 2^i for i=0..255 for use with shift
|
||||||
/// instructions; initialised by `kernel/asm/shift.asm::init_shift_table()`.
|
/// instructions; initialised by `kernel/asm/shift.asm::init_shift_table()`.
|
||||||
ShiftTable = 16,
|
ShiftTable = 16,
|
||||||
|
JumpdestBits = 17,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Segment {
|
impl Segment {
|
||||||
pub(crate) const COUNT: usize = 17;
|
pub(crate) const COUNT: usize = 18;
|
||||||
|
|
||||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||||
[
|
[
|
||||||
@ -62,6 +63,7 @@ impl Segment {
|
|||||||
Self::TrieEncodedChild,
|
Self::TrieEncodedChild,
|
||||||
Self::TrieEncodedChildLen,
|
Self::TrieEncodedChildLen,
|
||||||
Self::ShiftTable,
|
Self::ShiftTable,
|
||||||
|
Self::JumpdestBits,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +87,7 @@ impl Segment {
|
|||||||
Segment::TrieEncodedChild => "SEGMENT_TRIE_ENCODED_CHILD",
|
Segment::TrieEncodedChild => "SEGMENT_TRIE_ENCODED_CHILD",
|
||||||
Segment::TrieEncodedChildLen => "SEGMENT_TRIE_ENCODED_CHILD_LEN",
|
Segment::TrieEncodedChildLen => "SEGMENT_TRIE_ENCODED_CHILD_LEN",
|
||||||
Segment::ShiftTable => "SEGMENT_SHIFT_TABLE",
|
Segment::ShiftTable => "SEGMENT_SHIFT_TABLE",
|
||||||
|
Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +111,7 @@ impl Segment {
|
|||||||
Segment::TrieEncodedChild => 256,
|
Segment::TrieEncodedChild => 256,
|
||||||
Segment::TrieEncodedChildLen => 6,
|
Segment::TrieEncodedChildLen => 6,
|
||||||
Segment::ShiftTable => 256,
|
Segment::ShiftTable => 256,
|
||||||
|
Segment::JumpdestBits => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user