Merge branch 'main' into bignum-modexp

This commit is contained in:
Nicholas Ward 2023-03-22 09:46:17 -07:00
commit 1fbe305034
20 changed files with 360 additions and 105 deletions

View File

@ -55,15 +55,9 @@ global extcodesize:
// stack: extcodesize(address), retdest
SWAP1 JUMP
%macro codecopy
// stack: dest_offset, offset, size
%address
%extcodecopy
%endmacro
%macro extcodecopy
// stack: address, dest_offset, offset, size
%stack (dest_offset, offset, size) -> (dest_offset, offset, size, %%after)
%stack (address, dest_offset, offset, size) -> (address, dest_offset, offset, size, %%after)
%jump(extcodecopy)
%%after:
%endmacro
@ -145,6 +139,7 @@ global load_code:
JUMP
load_code_ctd:
// stack: codehash, ctx, segment, retdest
DUP1 ISZERO %jumpi(load_code_non_existent_account)
PROVER_INPUT(account_code::length)
// stack: code_size, codehash, ctx, segment, retdest
PUSH 0
@ -177,3 +172,7 @@ load_code_check:
// stack: shouldbecodehash, codehash, retdest, code_size
%assert_eq
JUMP
load_code_non_existent_account:
%stack (codehash, ctx, segment, retdest) -> (retdest, 0)
JUMP

View File

@ -1,3 +1,20 @@
global sys_balance:
// stack: kexit_info, address
// TODO: assuming a cold account access for now.
%charge_gas_const(@GAS_COLDACCOUNTACCESS)
SWAP1
// stack: address, kexit_info
%balance
// stack: balance, kexit_info
SWAP1
EXIT_KERNEL
%macro balance
%stack (address) -> (address, %%after)
%jump(balance)
%%after:
%endmacro
global balance:
// stack: address, retdest
%mpt_read_state_trie
@ -13,11 +30,17 @@ retzero:
%stack (account_ptr, retdest) -> (retdest, 0)
JUMP
global sys_selfbalance:
// stack: kexit_info
%charge_gas_const(@GAS_LOW)
%selfbalance
// stack: balance, kexit_info
SWAP1
EXIT_KERNEL
global selfbalance:
// stack: retdest
%macro selfbalance
PUSH %%after
%address
PUSH balance
// stack: balance, address, retdest
JUMP
%jump(balance)
%%after:
%endmacro

View File

@ -1,3 +1,7 @@
// TODO: This file needs to be cleaned up.
// `create` is no longer being used for contract-creation txns,
// so it can be inlined. Also need to set metadata on new ctx.
// The CREATE syscall.
//
// Pre stack: kexit_info, value, code_offset, code_len
@ -24,8 +28,8 @@ sys_create_finish:
// Note: CODE_ADDR refers to a (context, segment, offset) tuple.
global create:
// stack: sender, endowment, CODE_ADDR, code_len, retdest
// TODO: Charge gas.
DUP1 %get_nonce
DUP1 %nonce
// stack: nonce, sender, endowment, CODE_ADDR, code_len, retdest
// Call get_create_address and have it return to create_inner.
%stack (nonce, sender)
@ -67,12 +71,14 @@ sys_create2_finish:
// Pre stack: address, sender, endowment, CODE_ADDR, code_len, retdest
// Post stack: address
// Note: CODE_ADDR refers to a (context, segment, offset) tuple.
create_inner:
global create_inner:
// stack: address, sender, endowment, CODE_ADDR, code_len, retdest
%stack (address, sender, endowment)
-> (sender, address, endowment, sender, address)
// TODO: Need to handle insufficient balance failure.
%transfer_eth
// stack: transfer_eth_status, sender, address, CODE_ADDR, code_len, retdest
%jumpi(fault_exception)
// stack: sender, address, CODE_ADDR, code_len, retdest
%increment_nonce

View File

@ -5,14 +5,33 @@
// Post stack: address
global get_create_address:
// stack: sender, nonce, retdest
// TODO: Replace with actual implementation.
%pop2
PUSH 123
%alloc_rlp_block
// stack: rlp_start, sender, nonce, retdest
%stack (rlp_start, sender, nonce) -> (rlp_start, sender, nonce, rlp_start)
// stack: rlp_start, sender, nonce, rlp_start, retdest
%encode_rlp_160 // TODO: or encode_rlp_scalar?
// stack: rlp_pos, nonce, rlp_start, retdest
%encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
%prepend_rlp_list_prefix
// stack: rlp_prefix_start, rlp_len, retdest
PUSH @SEGMENT_RLP_RAW
PUSH 0 // context
// stack: RLP_ADDR: 3, rlp_len, retdest
KECCAK_GENERAL
%mod_const(0x10000000000000000000000000000000000000000) // 2^160
// stack: address, retdest
%observe_new_address
SWAP1
JUMP
// Convenience macro to call get_create_address and return where we left off.
%macro get_create_address
%stack (sender, nonce) -> (sender, nonce, %%after)
%jump(get_create_address)
%%after:
%endmacro
// Computes the address for a contract based on the CREATE2 rule, i.e.
// address = KEC(0xff || sender || salt || code_hash)[12:]
//

View File

@ -1,17 +1,21 @@
// Increment the nonce of the given account.
// Get the nonce of the given account.
// Pre stack: address, retdest
// Post stack: (empty)
global get_nonce:
global nonce:
// stack: address, retdest
// TODO: Replace with actual implementation.
POP
JUMP
%mpt_read_state_trie
// stack: account_ptr, retdest
// The nonce is the first account field, so we deref the account pointer itself.
// Note: We don't need to handle account_ptr=0, as trie_data[0] = 0,
// so the deref will give 0 (the default nonce) as desired.
%mload_trie_data
// stack: nonce, retdest
SWAP1 JUMP
// Convenience macro to call get_nonce and return where we left off.
%macro get_nonce
// Convenience macro to call nonce and return where we left off.
%macro nonce
%stack (address) -> (address, %%after)
%jump(get_nonce)
%jump(nonce)
%%after:
%endmacro
@ -20,7 +24,7 @@ global increment_nonce:
// stack: address, retdest
%mpt_read_state_trie
// stack: account_ptr, retdest
DUP1 ISZERO %jumpi(panic)
DUP1 ISZERO %jumpi(increment_nonce_no_such_account)
// stack: nonce_ptr, retdest
DUP1 %mload_trie_data
// stack: nonce, nonce_ptr, retdest
@ -30,6 +34,8 @@ global increment_nonce:
%mstore_trie_data
// stack: retdest
JUMP
global increment_nonce_no_such_account:
PANIC
// Convenience macro to call increment_nonce and return where we left off.
%macro increment_nonce

View File

@ -50,23 +50,76 @@ global process_based_on_type:
global process_contract_creation_txn:
// stack: retdest
PUSH process_contract_creation_txn_after_create
// stack: process_contract_creation_txn_after_create, retdest
%mload_txn_field(@TXN_FIELD_DATA_LEN)
// stack: code_len, process_contract_creation_txn_after_create, retdest
PUSH 0
// stack: code_offset, code_len, process_contract_creation_txn_after_create, retdest
PUSH @SEGMENT_TXN_DATA
// stack: code_segment, code_offset, code_len, process_contract_creation_txn_after_create, retdest
PUSH 0 // context
// stack: CODE_ADDR, code_len, process_contract_creation_txn_after_create, retdest
%mload_txn_field(@TXN_FIELD_ORIGIN)
// stack: origin, retdest
DUP1 %nonce
// stack: origin_nonce, origin, retdest
SWAP1
// stack: origin, origin_nonce, retdest
%get_create_address
// stack: address, retdest
// Deduct value from caller.
%mload_txn_field(@TXN_FIELD_VALUE)
%mload_txn_field(@TXN_FIELD_ORIGIN)
// stack: sender, endowment, CODE_ADDR, code_len, process_contract_creation_txn_after_create, retdest
%jump(create)
%deduct_eth
// stack: deduct_eth_status, address, retdest
%jumpi(panic)
// stack: address, retdest
global process_contract_creation_txn_after_create:
// stack: new_address, retdest
// Write the new account's data to MPT data, and get a pointer to it.
%get_trie_data_size
// stack: account_ptr, address, retdest
PUSH 1 %append_to_trie_data // nonce = 1
// stack: account_ptr, address, retdest
DUP2 %balance %mload_txn_field(@TXN_FIELD_VALUE) ADD %append_to_trie_data // balance = old_balance + txn_value
// stack: account_ptr, address, retdest
PUSH 0 %append_to_trie_data // storage_root = nil
// stack: account_ptr, address, retdest
PUSH @EMPTY_STRING_HASH %append_to_trie_data // code_hash = keccak('')
// stack: account_ptr, address, retdest
DUP2
// stack: address, account_ptr, address, retdest
%mpt_insert_state_trie
// stack: address, retdest
%create_context
// stack: new_ctx, address, retdest
// Copy the code from txdata to the new context's code segment.
PUSH process_contract_creation_txn_after_code_loaded
%mload_txn_field(@TXN_FIELD_DATA_LEN)
PUSH 0 // SRC.offset
PUSH @SEGMENT_TXN_DATA // SRC.segment
PUSH 0 // SRC.context
PUSH 0 // DST.offset
PUSH @SEGMENT_CODE // DST.segment
DUP7 // DST.context = new_ctx
%jump(memcpy)
process_contract_creation_txn_after_code_loaded:
// stack: new_ctx, address, retdest
// Each line in the block below does not change the stack.
DUP2 %set_new_ctx_addr
%mload_txn_field(@TXN_FIELD_ORIGIN) %set_new_ctx_caller
%mload_txn_field(@TXN_FIELD_VALUE) %set_new_ctx_value
%set_new_ctx_parent_ctx
%set_new_ctx_parent_pc(process_contract_creation_txn_after_constructor)
%non_intrinisic_gas %set_new_ctx_gas_limit
// stack: new_ctx, address, retdest
%enter_new_ctx
// (Old context) stack: new_ctx, address, retdest
global process_contract_creation_txn_after_constructor:
// stack: success, leftover_gas, new_ctx, address, retdest
POP // TODO: Success will go into the receipt when we support that.
// stack: leftover_gas, new_ctx, address, retdest
%pay_coinbase_and_refund_sender
// stack: new_ctx, address, retdest
POP
POP
JUMP
@ -105,9 +158,8 @@ global process_message_txn_insufficient_balance:
global process_message_txn_return:
// stack: retdest
%mload_txn_field(@TXN_FIELD_INTRINSIC_GAS)
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
SUB
// Since no code was executed, the leftover gas is the non-intrinsic gas.
%non_intrinisic_gas
// stack: leftover_gas, retdest
%pay_coinbase_and_refund_sender
// stack: retdest
@ -124,18 +176,13 @@ global process_message_txn_code_loaded:
%mload_txn_field(@TXN_FIELD_VALUE) %set_new_ctx_value
%set_new_ctx_parent_ctx
%set_new_ctx_parent_pc(process_message_txn_after_call)
// stack: new_ctx, retdest
// The gas provided to the callee is gas_limit - intrinsic_gas.
%mload_txn_field(@TXN_FIELD_INTRINSIC_GAS)
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
SUB
%set_new_ctx_gas_limit
%non_intrinisic_gas %set_new_ctx_gas_limit
// stack: new_ctx, retdest
// TODO: Copy TXN_DATA to CALLDATA
%enter_new_ctx
// (Old context) stack: new_ctx, retdest
global process_message_txn_after_call:
// stack: success, leftover_gas, new_ctx, retdest
@ -206,3 +253,11 @@ global process_message_txn_after_call:
%mstore_txn_field(@TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS)
// stack: (empty)
%endmacro
%macro non_intrinisic_gas
// stack: (empty)
%mload_txn_field(@TXN_FIELD_INTRINSIC_GAS)
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
SUB
// stack: gas_limit - intrinsic_gas
%endmacro

View File

@ -13,34 +13,11 @@ global sys_sgt:
PANIC
global sys_sar:
PANIC
global sys_balance:
PANIC
global sys_origin:
PANIC
global sys_calldatasize:
PANIC
global sys_calldatacopy:
PANIC
global sys_codecopy:
PANIC
global sys_returndatasize:
PANIC
global sys_returndatacopy:
PANIC
global sys_blockhash:
PANIC
global sys_coinbase:
PANIC
global sys_timestamp:
PANIC
global sys_number:
PANIC
global sys_prevrandao:
// TODO: What semantics will this have for Edge?
PANIC
global sys_gaslimit:
// TODO: Return the block's gas limit.
PANIC
global sys_chainid:
// TODO: Return the block's chain ID instead of the txn's, even though they should match.
// stack: kexit_info
@ -50,10 +27,6 @@ global sys_chainid:
// stack: chain_id, kexit_info
SWAP1
EXIT_KERNEL
global sys_selfbalance:
PANIC
global sys_basefee:
PANIC
global sys_log0:
PANIC
global sys_log1:

View File

@ -97,6 +97,15 @@ global terminate_common:
%shr_const(192)
// stack: gas_used
%mload_context_metadata(@CTX_METADATA_GAS_LIMIT)
// stack: gas_limit, gas_used
SWAP1
// stack: gas_used, gas_limit
DUP2 DUP2 LT
// stack: gas_used < gas_limit, gas_used, gas_limit
SWAP2
// stack: gas_limit, gas_used, gas_used < gas_limit
SUB
// stack: leftover_gas
// stack: gas_limit - gas_used, gas_used < gas_limit
MUL
// stack: leftover_gas = (gas_limit - gas_used) * (gas_used < gas_limit)
%endmacro

View File

@ -104,6 +104,97 @@ global sys_msize:
SWAP1
EXIT_KERNEL
%macro calldatasize
%mload_context_metadata(@CTX_METADATA_CALLDATA_SIZE)
%endmacro
global sys_calldatasize:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%calldatasize
// stack: calldatasize, kexit_info
SWAP1
EXIT_KERNEL
%macro returndatasize
%mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE)
%endmacro
global sys_returndatasize:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%returndatasize
// stack: returndatasize, kexit_info
SWAP1
EXIT_KERNEL
%macro coinbase
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_BENEFICIARY)
%endmacro
global sys_coinbase:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%coinbase
// stack: coinbase, kexit_info
SWAP1
EXIT_KERNEL
%macro timestamp
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_TIMESTAMP)
%endmacro
global sys_timestamp:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%timestamp
// stack: timestamp, kexit_info
SWAP1
EXIT_KERNEL
%macro blocknumber
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_NUMBER)
%endmacro
global sys_number:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%blocknumber
// stack: blocknumber, kexit_info
SWAP1
EXIT_KERNEL
%macro blockgaslimit
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_LIMIT)
%endmacro
global sys_gaslimit:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%blockgaslimit
// stack: blockgaslimit, kexit_info
SWAP1
EXIT_KERNEL
%macro basefee
%mload_global_metadata(@GLOBAL_METADATA_BLOCK_BASE_FEE)
%endmacro
global sys_basefee:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%basefee
// stack: basefee, kexit_info
SWAP1
EXIT_KERNEL
%macro update_mem_words
// stack: num_words, kexit_info
%mem_words

View File

@ -118,3 +118,32 @@ sys_calldataload_after_mload_packing:
SWAP1
EXIT_KERNEL
PANIC
// Macro for {CALLDATA,CODE,RETURNDATA}COPY (W_copy in Yellow Paper).
%macro wcopy(segment)
// stack: kexit_info, dest_offset, offset, size
DUP4 %num_bytes_to_num_words %mul_const(@GAS_COPY) %add_const(@GAS_VERYLOW) %charge_gas
%stack (kexit_info, dest_offset, offset, size) -> (dest_offset, size, dest_offset, offset, size, kexit_info)
ADD // TODO: check for overflow, see discussion here https://github.com/mir-protocol/plonky2/pull/930/files/a4ea0965d79561c345e2f77836c07949c7e0bc69#r1143630253
// stack: expanded_num_bytes, dest_offset, offset, size, kexit_info
DUP1 %ensure_reasonable_offset
%update_mem_bytes
GET_CONTEXT
%stack (context, dest_offset, offset, size, kexit_info) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, context, $segment, offset, size, %%after, kexit_info)
%jump(memcpy)
%%after:
// stack: kexit_info
EXIT_KERNEL
%endmacro
global sys_calldatacopy:
%wcopy(@SEGMENT_CALLDATA)
global sys_codecopy:
%wcopy(@SEGMENT_CODE)
global sys_returndatacopy:
%wcopy(@SEGMENT_RETURNDATA)

View File

@ -15,3 +15,16 @@
%mstore_kernel(@SEGMENT_NORMALIZED_TXN)
// stack: (empty)
%endmacro
%macro origin
%mload_txn_field(@TXN_FIELD_ORIGIN)
%endmacro
global sys_origin:
// stack: kexit_info
%charge_gas_const(@GAS_BASE)
// stack: kexit_info
%origin
// stack: origin, kexit_info
SWAP1
EXIT_KERNEL

View File

@ -15,3 +15,9 @@ mpt_insert_state_trie_save:
// stack: updated_node_ptr, retdest
%mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT)
JUMP
%macro mpt_insert_state_trie
%stack (key, value_ptr) -> (key, value_ptr, %%after)
%jump(mpt_insert_state_trie)
%%after:
%endmacro

View File

@ -37,4 +37,7 @@ global mpt_load_receipt_trie_value:
global mpt_load_storage_trie_value:
// stack: retdest
PANIC // TODO
PROVER_INPUT(mpt)
%append_to_trie_data
// stack: retdest
JUMP

View File

@ -102,7 +102,7 @@ type_0_compute_signed_data:
// stack: rlp_pos, rlp_start, retdest
%mload_txn_field(@TXN_FIELD_TO)
SWAP1 %encode_rlp_160
SWAP1 %encode_rlp_scalar
// stack: rlp_pos, rlp_start, retdest
%mload_txn_field(@TXN_FIELD_VALUE)

View File

@ -1,4 +1,6 @@
use anyhow::Result;
use ethereum_types::U256;
use hex_literal::hex;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::interpreter::Interpreter;
@ -7,11 +9,11 @@ use crate::cpu::kernel::interpreter::Interpreter;
fn test_get_create_address() -> Result<()> {
let get_create_address = KERNEL.global_labels["get_create_address"];
// TODO: Replace with real data once we have a real implementation.
// This is copied from OpenEthereum's `test_contract_address`.
let retaddr = 0xdeadbeefu32.into();
let nonce = 5.into();
let sender = 0.into();
let expected_addr = 123.into();
let nonce = 88.into();
let sender = U256::from_big_endian(&hex!("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"));
let expected_addr = U256::from_big_endian(&hex!("3f09c73a5ed19289fb9bdc72f1742566df146f56"));
let initial_stack = vec![retaddr, nonce, sender];
let mut interpreter = Interpreter::new_with_kernel(get_create_address, initial_stack);

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use ethereum_types::{Address, H160, H256, U256};
use ethereum_types::{Address, BigEndianHash, H160, H256, U256};
use keccak_hash::keccak;
use plonky2::field::types::Field;
@ -79,10 +79,10 @@ impl<F: Field> GenerationState<F> {
pub fn jump_to(&mut self, dst: usize) {
self.registers.program_counter = dst;
if dst == KERNEL.global_labels["observe_new_address"] {
let address = stack_peek(self, 0).expect("Empty stack");
let mut address_bytes = [0; 20];
address.to_big_endian(&mut address_bytes);
self.observe_address(H160(address_bytes));
let tip_u256 = stack_peek(self, 0).expect("Empty stack");
let tip_h256 = H256::from_uint(&tip_u256);
let tip_h160 = H160::from(tip_h256);
self.observe_address(tip_h160);
}
}

View File

@ -1,3 +1,5 @@
use ethereum_types::U256;
#[allow(dead_code)]
#[derive(Debug)]
pub enum ProgramError {
@ -8,4 +10,13 @@ pub enum ProgramError {
InvalidJumpiDestination,
StackOverflow,
KernelPanic,
MemoryError(MemoryError),
}
#[allow(clippy::enum_variant_names)]
#[derive(Debug)]
pub enum MemoryError {
ContextTooLarge { context: U256 },
SegmentTooLarge { segment: U256 },
VirtTooLarge { virt: U256 },
}

View File

@ -12,6 +12,9 @@ use MemoryChannel::{Code, GeneralPurpose};
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
use crate::memory::segments::Segment;
use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge};
use crate::witness::errors::ProgramError;
use crate::witness::errors::ProgramError::MemoryError;
impl MemoryChannel {
pub fn index(&self) -> usize {
@ -41,19 +44,25 @@ impl MemoryAddress {
}
}
pub(crate) fn new_u256s(context: U256, segment: U256, virt: U256) -> Self {
assert!(context.bits() <= 32, "context too large: {}", context);
assert!(
segment < Segment::COUNT.into(),
"segment too large: {}",
segment
);
assert!(virt.bits() <= 32, "virt too large: {}", virt);
Self {
pub(crate) fn new_u256s(
context: U256,
segment: U256,
virt: U256,
) -> Result<Self, ProgramError> {
if context.bits() > 32 {
return Err(MemoryError(ContextTooLarge { context }));
}
if segment >= Segment::COUNT.into() {
return Err(MemoryError(SegmentTooLarge { segment }));
}
if virt.bits() > 32 {
return Err(MemoryError(VirtTooLarge { virt }));
}
Ok(Self {
context: context.as_usize(),
segment: segment.as_usize(),
virt: virt.as_usize(),
}
})
}
pub(crate) fn increment(&mut self) {

View File

@ -111,7 +111,7 @@ pub(crate) fn generate_keccak_general<F: Field>(
stack_pop_with_log_and_fill::<4, _>(state, &mut row)?;
let len = len.as_usize();
let base_address = MemoryAddress::new_u256s(context, segment, base_virt);
let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?;
let input = (0..len)
.map(|i| {
let address = MemoryAddress {
@ -608,7 +608,7 @@ pub(crate) fn generate_mload_general<F: Field>(
let (val, log_read) = mem_read_gp_with_log_and_fill(
3,
MemoryAddress::new_u256s(context, segment, virt),
MemoryAddress::new_u256s(context, segment, virt)?,
state,
&mut row,
);

View File

@ -108,6 +108,7 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
(0x57, _) => Ok(Operation::Jumpi),
(0x58, _) => Ok(Operation::Pc),
(0x59, _) => Ok(Operation::Syscall(opcode)),
(0x5a, _) => Ok(Operation::Syscall(opcode)),
(0x5b, _) => Ok(Operation::Jumpdest),
(0x60..=0x7f, _) => Ok(Operation::Push(opcode & 0x1f)),
(0x80..=0x8f, _) => Ok(Operation::Dup(opcode & 0xf)),