Merge branch 'main' of github.com:mir-protocol/plonky2 into bls-fp2

This commit is contained in:
Dmitry Vagner 2023-04-11 15:29:53 -07:00
commit f265041845
3 changed files with 289 additions and 18 deletions

View File

@ -3,15 +3,22 @@
// Creates a new sub context and executes the code of the given account.
global sys_call:
// stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
// TODO: Charge gas.
SWAP2
// stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size
%u256_to_addr // Truncate to 160 bits
DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation.
SWAP2
// stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
DUP1 %insert_accessed_addresses
%call_charge_gas
%stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) ->
(args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size)
%checked_mem_expansion
%stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) ->
(ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size)
%checked_mem_expansion
%create_context
// stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
// stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size
// TODO: Consider call depth
// Each line in the block below does not change the stack.
@ -20,15 +27,15 @@ global sys_call:
DUP5 %set_new_ctx_value
DUP5 DUP5 %address %transfer_eth %jumpi(panic) // TODO: Fix this panic.
%set_new_ctx_parent_pc(after_call_instruction)
DUP3 %set_new_ctx_gas_limit // TODO: This is not correct in most cases. Use C_callgas as in the YP.
DUP3 %set_new_ctx_gas_limit
DUP4 %set_new_ctx_code
%stack (new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) ->
(new_ctx, args_offset, args_size, new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size)
%stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) ->
(new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size)
%copy_mem_to_calldata
// stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
%stack (new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size)
// stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size
%stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size)
-> (new_ctx, kexit_info, ret_offset, ret_size)
%enter_new_ctx
@ -117,7 +124,7 @@ global after_call_instruction:
SWAP3
// stack: kexit_info, leftover_gas, new_ctx, success, ret_offset, ret_size
// Add the leftover gas into the appropriate bits of kexit_info.
SWAP1 %shl_const(192) ADD
SWAP1 %shl_const(192) SWAP1 SUB
// stack: kexit_info, new_ctx, success, ret_offset, ret_size
// The callee's terminal instruction will have populated RETURNDATA.
@ -250,3 +257,78 @@ global after_call_instruction:
%jump(memcpy)
%%after:
%endmacro
// Charge gas for *call opcodes and return the sub-context gas limit.
// Doesn't include memory expansion costs.
%macro call_charge_gas
// Compute C_aaccess
// stack: cold_access, address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size
%mul_const(@GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS)
%add_const(@GAS_WARMACCESS)
// Compute C_xfer
// stack: Caaccess, address, gas, kexit_info, value
DUP5 ISZERO PUSH 1 SUB
// stack: value0, Caaccess, address, gas, kexit_info, value
DUP1
%mul_const(@GAS_CALLVALUE)
// Compute C_new
// stack: Cxfer, value0, Caaccess, address, gas, kexit_info, value
SWAP1
// stack: value0, Cxfer, Caaccess, address, gas, kexit_info, value
DUP4 %is_dead MUL
// stack: is_dead(address) and value0, Cxfer, Caaccess, address, gas, kexit_info, value
%mul_const(@GAS_NEWACCOUNT)
// stack: Cnew, Cxfer, Caaccess, address, gas, kexit_info, value
// Compute C_extra
ADD ADD
// Compute C_gascap
// stack: Cextra, address, gas, kexit_info, value
DUP4 %leftover_gas
// stack: leftover_gas, Cextra, address, gas, kexit_info, value
DUP2 DUP2 LT
// stack: leftover_gas<Cextra, leftover_gas, Cextra, address, gas, kexit_info, value
DUP5 DUP2 MUL
// stack: (leftover_gas<Cextra)*gas, leftover_gas<Cextra, leftover_gas, Cextra, address, gas, kexit_info, value
SWAP1 PUSH 1 SUB
// stack: leftover_gas>=Cextra, (leftover_gas<Cextra)*gas, leftover_gas, Cextra, address, gas, kexit_info, value
DUP4 DUP4 SUB
// stack: leftover_gas - Cextra, leftover_gas>=Cextra, (leftover_gas<Cextra)*gas, leftover_gas, Cextra, address, gas, kexit_info, value
%all_but_one_64th
// stack: L(leftover_gas - Cextra), leftover_gas>=Cextra, (leftover_gas<Cextra)*gas, leftover_gas, Cextra, address, gas, kexit_info, value
DUP7 %min MUL ADD
// stack: Cgascap, leftover_gas, Cextra, address, gas, kexit_info, value
// Compute C_call and charge for it.
%stack (Cgascap, leftover_gas, Cextra) -> (Cextra, Cgascap, Cgascap)
ADD
%stack (C_call, Cgascap, address, gas, kexit_info, value) ->
(C_call, kexit_info, Cgascap, address, gas, value)
%charge_gas
// Compute C_callgas
%stack (kexit_info, Cgascap, address, gas, value) ->
(Cgascap, address, gas, kexit_info, value)
DUP5 ISZERO PUSH 1 SUB
// stack: value!=0, Cgascap, address, gas, kexit_info, value
%mul_const(@GAS_CALLSTIPEND) ADD
%stack (C_callgas, address, gas, kexit_info, value) ->
(kexit_info, C_callgas, address, value)
%endmacro
// Checked memory expansion.
%macro checked_mem_expansion
// stack: size, offset, kexit_info
DUP1 ISZERO %jumpi(%%zero)
ADD // TODO: check for overflow
// stack: expanded_num_bytes, kexit_info
DUP1 %ensure_reasonable_offset
%update_mem_bytes
%jump(%%after)
%%zero:
%pop2
%%after:
%endmacro

View File

@ -66,4 +66,4 @@
DUP1 %is_non_existent
SWAP1 %is_empty
ADD // OR
%endmacro
%endmacro

View File

@ -1,5 +1,6 @@
//! An EVM interpreter for testing and debugging purposes.
use core::cmp::Ordering;
use std::collections::HashMap;
use std::ops::Range;
@ -305,20 +306,20 @@ impl<'a> Interpreter<'a> {
0x02 => self.run_mul(), // "MUL",
0x03 => self.run_sub(), // "SUB",
0x04 => self.run_div(), // "DIV",
0x05 => todo!(), // "SDIV",
0x05 => self.run_sdiv(), // "SDIV",
0x06 => self.run_mod(), // "MOD",
0x07 => todo!(), // "SMOD",
0x07 => self.run_smod(), // "SMOD",
0x08 => self.run_addmod(), // "ADDMOD",
0x09 => self.run_mulmod(), // "MULMOD",
0x0a => self.run_exp(), // "EXP",
0x0b => todo!(), // "SIGNEXTEND",
0x0b => self.run_signextend(), // "SIGNEXTEND",
0x0c => self.run_addfp254(), // "ADDFP254",
0x0d => self.run_mulfp254(), // "MULFP254",
0x0e => self.run_subfp254(), // "SUBFP254",
0x10 => self.run_lt(), // "LT",
0x11 => self.run_gt(), // "GT",
0x12 => todo!(), // "SLT",
0x13 => todo!(), // "SGT",
0x12 => self.run_slt(), // "SLT",
0x13 => self.run_sgt(), // "SGT",
0x14 => self.run_eq(), // "EQ",
0x15 => self.run_iszero(), // "ISZERO",
0x16 => self.run_and(), // "AND",
@ -328,7 +329,7 @@ impl<'a> Interpreter<'a> {
0x1a => self.run_byte(), // "BYTE",
0x1b => self.run_shl(), // "SHL",
0x1c => self.run_shr(), // "SHR",
0x1d => todo!(), // "SAR",
0x1d => self.run_sar(), // "SAR",
0x20 => self.run_keccak256(), // "KECCAK256",
0x21 => self.run_keccak_general(), // "KECCAK_GENERAL",
0x30 => todo!(), // "ADDRESS",
@ -467,12 +468,75 @@ impl<'a> Interpreter<'a> {
self.push(if y.is_zero() { U256::zero() } else { x / y });
}
fn run_sdiv(&mut self) {
let mut x = self.pop();
let mut y = self.pop();
let y_is_zero = y.is_zero();
if y_is_zero {
self.push(U256::zero());
} else if y.eq(&MINUS_ONE) && x.eq(&MIN_VALUE) {
self.push(MIN_VALUE);
} else {
let x_is_pos = x.eq(&(x & SIGN_MASK));
let y_is_pos = y.eq(&(y & SIGN_MASK));
// We compute the absolute quotient first,
// then adapt its sign based on the operands.
if !x_is_pos {
x = two_complement(x);
}
if !y_is_pos {
y = two_complement(y);
}
let div = x / y;
if div.eq(&U256::zero()) {
self.push(U256::zero());
}
self.push(if x_is_pos == y_is_pos {
div
} else {
two_complement(div)
});
}
}
fn run_mod(&mut self) {
let x = self.pop();
let y = self.pop();
self.push(if y.is_zero() { U256::zero() } else { x % y });
}
fn run_smod(&mut self) {
let mut x = self.pop();
let mut y = self.pop();
if y.is_zero() {
self.push(U256::zero());
} else {
let x_is_pos = x.eq(&(x & SIGN_MASK));
let y_is_pos = y.eq(&(y & SIGN_MASK));
// We compute the absolute remainder first,
// then adapt its sign based on the operands.
if !x_is_pos {
x = two_complement(x);
}
if !y_is_pos {
y = two_complement(y);
}
let rem = x % y;
if rem.eq(&U256::zero()) {
self.push(U256::zero());
}
// Remainder always has the same sign as the dividend.
self.push(if x_is_pos { rem } else { two_complement(rem) });
}
}
fn run_addmod(&mut self) {
let x = U512::from(self.pop());
let y = U512::from(self.pop());
@ -513,6 +577,43 @@ impl<'a> Interpreter<'a> {
self.push_bool(x > y);
}
fn run_slt(&mut self) {
let x = self.pop();
let y = self.pop();
self.push_bool(signed_cmp(x, y) == Ordering::Less);
}
fn run_sgt(&mut self) {
let x = self.pop();
let y = self.pop();
self.push_bool(signed_cmp(x, y) == Ordering::Greater);
}
fn run_signextend(&mut self) {
let n = self.pop();
let x = self.pop();
if n > U256::from(31) {
self.push(x);
} else {
let n = n.low_u64() as usize;
let num_bytes_prepend = 31 - n;
let mut x_bytes = [0u8; 32];
x.to_big_endian(&mut x_bytes);
let x_bytes = x_bytes[num_bytes_prepend..].to_vec();
let sign_bit = x_bytes[0] >> 7;
let mut bytes = if sign_bit == 0 {
vec![0; num_bytes_prepend]
} else {
vec![0xff; num_bytes_prepend]
};
bytes.extend_from_slice(&x_bytes);
self.push(U256::from_big_endian(&bytes));
}
}
fn run_eq(&mut self) {
let x = self.pop();
let y = self.pop();
@ -574,6 +675,30 @@ impl<'a> Interpreter<'a> {
self.push(value >> shift);
}
fn run_sar(&mut self) {
let shift = self.pop();
let value = self.pop();
let value_is_neg = !value.eq(&(value & SIGN_MASK));
if shift < U256::from(256usize) {
let shift = shift.low_u64() as usize;
let mask = !(MINUS_ONE >> shift);
let value_shifted = value >> shift;
if value_is_neg {
self.push(value_shifted | mask);
} else {
self.push(value_shifted);
};
} else {
self.push(if value_is_neg {
MINUS_ONE
} else {
U256::zero()
});
}
}
fn run_keccak256(&mut self) {
let offset = self.pop().as_usize();
let size = self.pop().as_usize();
@ -837,6 +962,70 @@ impl<'a> Interpreter<'a> {
}
}
// Computes the two's complement of the given integer.
fn two_complement(x: U256) -> U256 {
let flipped_bits = x ^ MINUS_ONE;
flipped_bits.overflowing_add(U256::one()).0
}
fn signed_cmp(x: U256, y: U256) -> Ordering {
let x_is_zero = x.is_zero();
let y_is_zero = y.is_zero();
if x_is_zero && y_is_zero {
return Ordering::Equal;
}
let x_is_pos = x.eq(&(x & SIGN_MASK));
let y_is_pos = y.eq(&(y & SIGN_MASK));
if x_is_zero {
if y_is_pos {
return Ordering::Less;
} else {
return Ordering::Greater;
}
};
if y_is_zero {
if x_is_pos {
return Ordering::Greater;
} else {
return Ordering::Less;
}
};
match (x_is_pos, y_is_pos) {
(true, true) => x.cmp(&y),
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
(false, false) => x.cmp(&y).reverse(),
}
}
/// -1 in two's complement representation consists in all bits set to 1.
const MINUS_ONE: U256 = U256([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
]);
/// -2^255 in two's complement representation consists in the MSB set to 1.
const MIN_VALUE: U256 = U256([
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
0x8000000000000000,
]);
const SIGN_MASK: U256 = U256([
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
0x7fffffffffffffff,
]);
/// Return the (ordered) JUMPDEST offsets in the code.
fn find_jumpdests(code: &[u8]) -> Vec<usize> {
let mut offset = 0;