diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index a0ac3ec727..7c0e479458 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -10,7 +10,7 @@ on: branches: - "**" -jobs: +jobs: test: name: Test Suite runs-on: ubuntu-latest @@ -82,6 +82,57 @@ jobs: CARGO_INCREMENTAL: 1 RUST_BACKTRACE: 1 + wasm32: + name: wasm32 compatibility + runs-on: ubuntu-latest + if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install nightly wasm32 toolchain + id: rustc-toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + target: wasm32-unknown-unknown + default: true + override: true + + - name: rust-cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: rustc-wasm32-${{ steps.rustc-toolchain.outputs.rustc_hash }}-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Check in plonky2 subdirectory + uses: actions-rs/cargo@v1 + with: + command: check + args: --manifest-path plonky2/Cargo.toml --target wasm32-unknown-unknown --no-default-features + env: + RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 + RUST_LOG: 1 + CARGO_INCREMENTAL: 1 + RUST_BACKTRACE: 1 + + - name: Check in starky subdirectory + uses: actions-rs/cargo@v1 + with: + command: check + args: --manifest-path starky/Cargo.toml --target wasm32-unknown-unknown --no-default-features + env: + RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 + RUST_LOG: 1 + CARGO_INCREMENTAL: 1 + RUST_BACKTRACE: 1 + lints: name: Formatting and Clippy runs-on: ubuntu-latest diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 21dcf91985..14544654eb 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -63,6 +63,12 @@ pub(crate) fn ctl_arithmetic_rows() -> TableWithColumns { // If an arithmetic operation is happening on the CPU side, // the CTL will enforce that the reconstructed opcode value // from the opcode bits matches. + // These opcodes are missing the syscall and prover_input opcodes, + // since `IS_RANGE_CHECK` can be associated to multiple opcodes. + // For `IS_RANGE_CHECK`, the opcodes are written in OPCODE_COL, + // and we use that column for scaling and the CTL checks. + // Note that we ensure in the STARK's constraints that the + // value in `OPCODE_COL` is 0 if `IS_RANGE_CHECK` = 0. const COMBINED_OPS: [(usize, u8); 16] = [ (columns::IS_ADD, 0x01), (columns::IS_MUL, 0x02), @@ -89,8 +95,13 @@ pub(crate) fn ctl_arithmetic_rows() -> TableWithColumns { columns::OUTPUT_REGISTER, ]; - let filter_column = Some(Column::sum(COMBINED_OPS.iter().map(|(c, _v)| *c))); + let mut filter_cols = COMBINED_OPS.to_vec(); + filter_cols.push((columns::IS_RANGE_CHECK, 0x01)); + let filter_column = Some(Column::sum(filter_cols.iter().map(|(c, _v)| *c))); + + let mut all_combined_cols = COMBINED_OPS.to_vec(); + all_combined_cols.push((columns::OPCODE_COL, 0x01)); // Create the Arithmetic Table whose columns are those of the // operations listed in `ops` whose inputs and outputs are given // by `regs`, where each element of `regs` is a range of columns @@ -98,7 +109,7 @@ pub(crate) fn ctl_arithmetic_rows() -> TableWithColumns { // is used as the operation filter). TableWithColumns::new( Table::Arithmetic, - cpu_arith_data_link(&COMBINED_OPS, ®ISTER_MAP), + cpu_arith_data_link(&all_combined_cols, ®ISTER_MAP), filter_column, ) } @@ -109,7 +120,7 @@ pub struct ArithmeticStark { pub f: PhantomData, } -const RANGE_MAX: usize = 1usize << 16; // Range check strict upper bound +pub(crate) const RANGE_MAX: usize = 1usize << 16; // Range check strict upper bound impl ArithmeticStark { /// Expects input in *column*-major layout @@ -195,6 +206,10 @@ impl, const D: usize> Stark for ArithmeticSta let lv: &[P; NUM_ARITH_COLUMNS] = vars.get_local_values().try_into().unwrap(); let nv: &[P; NUM_ARITH_COLUMNS] = vars.get_next_values().try_into().unwrap(); + // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. + let opcode_constraint = (P::ONES - lv[columns::IS_RANGE_CHECK]) * lv[columns::OPCODE_COL]; + yield_constr.constraint(opcode_constraint); + // Check the range column: First value must be 0, last row // must be 2^16-1, and intermediate rows must increment by 0 // or 1. @@ -231,6 +246,16 @@ impl, const D: usize> Stark for ArithmeticSta let nv: &[ExtensionTarget; NUM_ARITH_COLUMNS] = vars.get_next_values().try_into().unwrap(); + // Check that `OPCODE_COL` holds 0 if the operation is not a range_check. + let opcode_constraint = builder.arithmetic_extension( + F::NEG_ONE, + F::ONE, + lv[columns::IS_RANGE_CHECK], + lv[columns::OPCODE_COL], + lv[columns::OPCODE_COL], + ); + yield_constr.constraint(builder, opcode_constraint); + // Check the range column: First value must be 0, last row // must be 2^16-1, and intermediate rows must increment by 0 // or 1. diff --git a/evm/src/arithmetic/columns.rs b/evm/src/arithmetic/columns.rs index aa36b3ab71..3736445433 100644 --- a/evm/src/arithmetic/columns.rs +++ b/evm/src/arithmetic/columns.rs @@ -38,8 +38,10 @@ pub(crate) const IS_GT: usize = IS_LT + 1; pub(crate) const IS_BYTE: usize = IS_GT + 1; pub(crate) const IS_SHL: usize = IS_BYTE + 1; pub(crate) const IS_SHR: usize = IS_SHL + 1; - -pub(crate) const START_SHARED_COLS: usize = IS_SHR + 1; +pub(crate) const IS_RANGE_CHECK: usize = IS_SHR + 1; +/// Column that stores the opcode if the operation is a range check. +pub(crate) const OPCODE_COL: usize = IS_RANGE_CHECK + 1; +pub(crate) const START_SHARED_COLS: usize = OPCODE_COL + 1; /// Within the Arithmetic Unit, there are shared columns which can be /// used by any arithmetic circuit, depending on which one is active diff --git a/evm/src/arithmetic/mod.rs b/evm/src/arithmetic/mod.rs index 2699ee51c4..4b84a3510e 100644 --- a/evm/src/arithmetic/mod.rs +++ b/evm/src/arithmetic/mod.rs @@ -1,6 +1,11 @@ use ethereum_types::U256; use plonky2::field::types::PrimeField64; +use self::columns::{ + INPUT_REGISTER_0, INPUT_REGISTER_1, INPUT_REGISTER_2, OPCODE_COL, OUTPUT_REGISTER, +}; +use self::utils::u256_to_array; +use crate::arithmetic::columns::IS_RANGE_CHECK; use crate::extension_tower::BN_BASE; use crate::util::{addmod, mulmod, submod}; @@ -135,6 +140,7 @@ impl TernaryOperator { } /// An enum representing arithmetic operations that can be either binary or ternary. +#[allow(clippy::enum_variant_names)] #[derive(Debug)] pub(crate) enum Operation { BinaryOperation { @@ -150,6 +156,13 @@ pub(crate) enum Operation { input2: U256, result: U256, }, + RangeCheckOperation { + input0: U256, + input1: U256, + input2: U256, + opcode: U256, + result: U256, + }, } impl Operation { @@ -195,11 +208,28 @@ impl Operation { } } + pub(crate) fn range_check( + input0: U256, + input1: U256, + input2: U256, + opcode: U256, + result: U256, + ) -> Self { + Self::RangeCheckOperation { + input0, + input1, + input2, + opcode, + result, + } + } + /// Gets the result of an arithmetic operation. pub(crate) fn result(&self) -> U256 { match self { Operation::BinaryOperation { result, .. } => *result, Operation::TernaryOperation { result, .. } => *result, + _ => panic!("This function should not be called for range checks."), } } @@ -228,6 +258,13 @@ impl Operation { input2, result, } => ternary_op_to_rows(operator.row_filter(), input0, input1, input2, result), + Operation::RangeCheckOperation { + input0, + input1, + input2, + opcode, + result, + } => range_check_to_rows(input0, input1, input2, opcode, result), } } } @@ -293,3 +330,21 @@ fn binary_op_to_rows( } } } + +fn range_check_to_rows( + input0: U256, + input1: U256, + input2: U256, + opcode: U256, + result: U256, +) -> (Vec, Option>) { + let mut row = vec![F::ZERO; columns::NUM_ARITH_COLUMNS]; + row[IS_RANGE_CHECK] = F::ONE; + row[OPCODE_COL] = F::from_canonical_u64(opcode.as_u64()); + u256_to_array(&mut row[INPUT_REGISTER_0], input0); + u256_to_array(&mut row[INPUT_REGISTER_1], input1); + u256_to_array(&mut row[INPUT_REGISTER_2], input2); + u256_to_array(&mut row[OUTPUT_REGISTER], result); + + (row, None) +} diff --git a/evm/src/byte_packing/columns.rs b/evm/src/byte_packing/columns.rs index 16bc4be5fa..d29a1b6f51 100644 --- a/evm/src/byte_packing/columns.rs +++ b/evm/src/byte_packing/columns.rs @@ -31,7 +31,7 @@ pub(crate) const TIMESTAMP: usize = ADDR_VIRTUAL + 1; // 32 byte limbs hold a total of 256 bits. const BYTES_VALUES_START: usize = TIMESTAMP + 1; // There are `NUM_BYTES` columns used to store the values of the bytes -// that are beeing read/written for an (un)packing operation. +// that are being read/written for an (un)packing operation. // If `index_bytes(i) == 1`, then all `value_bytes(j) for j <= i` may be non-zero. pub(crate) const fn value_bytes(i: usize) -> usize { debug_assert!(i < NUM_BYTES); diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 09f6f1fa5b..911da5b8cf 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -74,8 +74,8 @@ fn ctl_data_binops() -> Vec> { /// Creates the vector of `Columns` corresponding to the three inputs and /// one output of a ternary operation. By default, ternary operations use -/// the first three memory channels, and the last one for the result (binary -/// operations do not use the third inputs). +/// the first three memory channels, and the next top of the stack for the +/// result (binary operations do not use the third inputs). fn ctl_data_ternops() -> Vec> { let mut res = Column::singles(COL_MAP.mem_channels[0].value).collect_vec(); res.extend(Column::singles(COL_MAP.mem_channels[1].value)); @@ -115,6 +115,9 @@ pub fn ctl_arithmetic_base_rows() -> TableWithColumns { COL_MAP.op.fp254_op, COL_MAP.op.ternary_op, COL_MAP.op.shift, + COL_MAP.op.prover_input, + COL_MAP.op.syscall, + COL_MAP.op.exception, ])), ) } diff --git a/evm/src/cpu/docs/out-of-gas.md b/evm/src/cpu/docs/out-of-gas.md index 733384b27e..55d24f62c3 100644 --- a/evm/src/cpu/docs/out-of-gas.md +++ b/evm/src/cpu/docs/out-of-gas.md @@ -8,7 +8,7 @@ When a native instruction (one that is not a syscall) is executed, a constraint If everything goes smoothly and we have not run out of gas, `gas` should be no more than the gas allowance at the point that we `STOP`, `REVERT`, stack overflow, or whatever. Indeed, because we assume that the gas overflow handler is invoked _as soon as_ we've run out of gas, all these termination methods must verify that `gas` <= allowance, and `PANIC` if this is not the case. This is also true for the out-of-gas handler, which should check that (a) we have not yet run out of gas and (b) we are about to run out of gas, `PANIC`king if either of those does not hold. -When we do run out of gas, however, this event must be handled. Syscalls are responsible for checking that their execution would not cause the transaction to run out of gas. If the syscall detects that it would need to charge more gas than available, it must abort the transaction by jumping to `exc_out_of_gas`, which in turn verifies that the out-of-gas hasn't _already_ occured. +When we do run out of gas, however, this event must be handled. Syscalls are responsible for checking that their execution would not cause the transaction to run out of gas. If the syscall detects that it would need to charge more gas than available, it must abort the transaction by jumping to `exc_out_of_gas`, which in turn verifies that the out-of-gas hasn't _already_ occurred. Native instructions do this differently. If the prover notices that execution of the instruction would cause an out-of-gas error, it must jump to the appropriate handler instead of executing the instruction. (The handler contains special code that `PANIC`s if the prover invoked it incorrectly.) diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index bd06bb6194..e63030994f 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -43,6 +43,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/core/log.asm"), include_str!("asm/core/selfdestruct_list.asm"), include_str!("asm/core/touched_addresses.asm"), + include_str!("asm/core/withdrawals.asm"), include_str!("asm/core/precompiles/main.asm"), include_str!("asm/core/precompiles/ecrec.asm"), include_str!("asm/core/precompiles/sha256.asm"), diff --git a/evm/src/cpu/kernel/asm/core/create_contract_account.asm b/evm/src/cpu/kernel/asm/core/create_contract_account.asm index ca74dcfe67..cb04f0a436 100644 --- a/evm/src/cpu/kernel/asm/core/create_contract_account.asm +++ b/evm/src/cpu/kernel/asm/core/create_contract_account.asm @@ -22,8 +22,18 @@ DUP1 %mload_trie_data // codehash = account[3] %eq_const(@EMPTY_STRING_HASH) ISZERO %jumpi(%%error_collision) // stack: existing_codehash_ptr, address - %sub_const(2) %mload_trie_data // balance = account[1] - %jump(%%do_insert) + %sub_const(1) + %stack (storage_root_ptr, address) -> (storage_root_ptr, 0, storage_root_ptr, 2, address) + // Set the storage root to 0. + %mstore_trie_data + // stack: storage_root_ptr, 2, address + SUB + // stack: nonce_ptr, address + PUSH 1 SWAP1 %mstore_trie_data + // stack: address + PUSH 0 SWAP1 %journal_add_nonce_change + PUSH 0 // success + %jump(%%end) %%add_account: // stack: existing_balance, address @@ -34,6 +44,7 @@ %get_trie_data_size // stack: account_ptr, new_acct_value, address PUSH 0 DUP4 %journal_add_nonce_change + PUSH 0 %append_to_trie_data // key placeholder PUSH 1 %append_to_trie_data // nonce = 1 // stack: account_ptr, new_acct_value, address SWAP1 %append_to_trie_data // balance = new_acct_value diff --git a/evm/src/cpu/kernel/asm/core/exception.asm b/evm/src/cpu/kernel/asm/core/exception.asm index 49ccf6dcc1..64477c052b 100644 --- a/evm/src/cpu/kernel/asm/core/exception.asm +++ b/evm/src/cpu/kernel/asm/core/exception.asm @@ -1,4 +1,4 @@ -// These exception codes are arbitary and assigned by us. +// These exception codes are arbitrary and assigned by us. global exception_jumptable: // exception 0: out of gas JUMPTABLE exc_out_of_gas diff --git a/evm/src/cpu/kernel/asm/core/withdrawals.asm b/evm/src/cpu/kernel/asm/core/withdrawals.asm new file mode 100644 index 0000000000..3be05d880c --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/withdrawals.asm @@ -0,0 +1,25 @@ +%macro withdrawals + // stack: (empty) + PUSH %%after + %jump(withdrawals) +%%after: + // stack: (empty) +%endmacro + +global withdrawals: + // stack: retdest + PROVER_INPUT(withdrawal) + // stack: address, retdest + PROVER_INPUT(withdrawal) + // stack: amount, address, retdest + DUP2 %eq_const(@U256_MAX) %jumpi(withdrawals_end) + SWAP1 + // stack: address, amount, retdest + %add_eth + // stack: retdest + %jump(withdrawals) + +withdrawals_end: + // stack: amount, address, retdest + %pop2 + JUMP diff --git a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm index 947c972a32..28e3b5d20e 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/field_arithmetic/inverse.asm @@ -1,4 +1,4 @@ -// Returns reverse order divison y/x, modulo N +// Returns reverse order division y/x, modulo N %macro divr_fp254 // stack: x , y %inv_fp254 diff --git a/evm/src/cpu/kernel/asm/journal/storage_change.asm b/evm/src/cpu/kernel/asm/journal/storage_change.asm index 1a5d7e44c1..58ca422ab1 100644 --- a/evm/src/cpu/kernel/asm/journal/storage_change.asm +++ b/evm/src/cpu/kernel/asm/journal/storage_change.asm @@ -13,19 +13,20 @@ global revert_storage_change: // stack: address, slot, prev_value, retdest SWAP1 %slot_to_storage_key // stack: storage_key, address, prev_value, retdest - PUSH 64 // storage_key has 64 nibbles - // stack: 64, storage_key, address, prev_value, retdest - DUP3 %smt_read_state + DUP2 %smt_read_state DUP1 ISZERO %jumpi(panic) - // stack: account_ptr, 64, storage_key, address, prev_value, retdest + // stack: account_ptr, storage_key, address, prev_value, retdest %add_const(2) - // stack: storage_root_ptr_ptr, 64, storage_key, address, prev_value, retdest + // stack: storage_root_ptr_ptr, storage_key, address, prev_value, retdest %mload_trie_data + // stack: storage_root_ptr, storage_key, address, prev_value, retdest %get_trie_data_size - DUP6 %append_to_trie_data - %stack (prev_value_ptr, storage_root_ptr, num_nibbles, storage_key, address, prev_value, retdest) -> - (storage_root_ptr, num_nibbles, storage_key, prev_value_ptr, new_storage_root, address, retdest) - %jump(mpt_insert) + // stack: prev_value_ptr, storage_root_ptr, storage_key, address, prev_value, retdest + PUSH 0 %append_to_trie_data + DUP5 %append_to_trie_data + %stack (prev_value_ptr, storage_root_ptr, storage_key, address, prev_value, retdest) -> + (storage_root_ptr, storage_key, prev_value_ptr, new_storage_root, address, retdest) + %jump(smt_insert) delete: // stack: address, slot, prev_value, retdest diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index 24ed0a43d9..733f2adbc2 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -32,7 +32,7 @@ global start_txns: txn_loop: // If the prover has no more txns for us to process, halt. PROVER_INPUT(end_of_txns) - %jumpi(hash_final_tries) + %jumpi(execute_withdrawals) // Call route_txn. When we return, continue the txn loop. PUSH txn_loop_after @@ -48,6 +48,9 @@ global txn_loop_after: SWAP3 %increment SWAP3 %jump(txn_loop) +global execute_withdrawals: + // stack: cum_gas, txn_counter, num_nibbles, txn_nb + %withdrawals global hash_final_tries: // stack: cum_gas, txn_counter, num_nibbles, txn_nb // Check that we end up with the correct `cum_gas`, `txn_nb` and bloom filter. diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 4209f06c75..88f5bce4ef 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -1,7 +1,7 @@ // Computes the Merkle root of the given trie node. // // encode_value is a function which should take as input -// - the position withing @SEGMENT_RLP_RAW to write to, +// - the position within @SEGMENT_RLP_RAW to write to, // - the offset of a value within @SEGMENT_TRIE_DATA, and // - a return address. // It should serialize the value, write it to @SEGMENT_RLP_RAW starting at the diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index 767927fbc6..e5734f8046 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -99,7 +99,7 @@ global encode_account: global encode_txn: // stack: rlp_pos, value_ptr, retdest - // Load the txn_rlp_len which is at the beginnig of value_ptr + // Load the txn_rlp_len which is at the beginning of value_ptr DUP2 %mload_trie_data // stack: txn_rlp_len, rlp_pos, value_ptr, retdest SWAP2 %increment diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm index f5b9610cc5..0f1bd050b4 100644 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm +++ b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm @@ -89,12 +89,15 @@ sstore_after_refund: EQ %jumpi(sstore_noop) // stack: current_value, slot, value, kexit_info - DUP2 %address %journal_add_storage_change - // stack: slot, value, kexit_info + DUP1 DUP3 %address %journal_add_storage_change + // stack: current_value, slot, value, kexit_info // If the value is zero, delete the slot from the storage SMT. - // stack: slot, value, kexit_info - DUP2 ISZERO %jumpi(sstore_delete) + DUP3 ISZERO %jumpi(sstore_delete) + + // stack: current_value, slot, value, kexit_info + %jumpi(existing_slot) + // First we write the value to SMT data, and get a pointer to it. %get_trie_data_size @@ -126,6 +129,20 @@ after_storage_insert: // stack: kexit_info EXIT_KERNEL +existing_slot: + // stack: slot, value, kexit_info + %slot_to_storage_key + // stack: storage_key, value, kexit_info + %current_storage_smt + // stack: storage_root_ptr, storage_key, value, kexit_info + %stack (storage_root_ptr, storage_key) -> (storage_root_ptr, storage_key, existing_slot_after_read) + %jump(smt_read) +existing_slot_after_read: + // stack: value_ptr, value, kexit_info + %mstore_trie_data + // stack: kexit_info + EXIT_KERNEL + sstore_noop: // stack: current_value, slot, value, kexit_info %pop3 @@ -133,8 +150,7 @@ sstore_noop: // Delete the slot from the storage SMT. sstore_delete: - // stack: slot, value, kexit_info - SWAP1 POP + %stack (current_value, slot, value, kexit_info) -> (slot, kexit_info) PUSH after_storage_insert SWAP1 // stack: slot, after_storage_insert, kexit_info %slot_to_storage_key diff --git a/evm/src/cpu/kernel/asm/smt/insert.asm b/evm/src/cpu/kernel/asm/smt/insert.asm index cc8f58405b..f2e6752f7c 100644 --- a/evm/src/cpu/kernel/asm/smt/insert.asm +++ b/evm/src/cpu/kernel/asm/smt/insert.asm @@ -112,4 +112,10 @@ after_second_leaf: smt_insert_leaf_same_key: // stack: node_payload_ptr, key, value_ptr, retdest - PANIC // Not sure if this should happen. + %stack (node_payload_ptr, key, value_ptr) -> (value_ptr, key, node_payload_ptr, value_ptr, node_payload_ptr, 1) + %mstore_trie_data // Store key + %mstore_trie_data // Store new value pointer + // stack: node_payload_ptr, 1, retdest + SUB + // stack: node_ptr, retdest + SWAP1 JUMP diff --git a/evm/src/cpu/kernel/tests/smt/delete.rs b/evm/src/cpu/kernel/tests/smt/delete.rs index 494f1dc8ed..2f4f63371e 100644 --- a/evm/src/cpu/kernel/tests/smt/delete.rs +++ b/evm/src/cpu/kernel/tests/smt/delete.rs @@ -26,7 +26,6 @@ fn test_state_smt(smt: Smt, k: U256, account: Account) -> Result<()> { state_smt: smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let smt_insert_state = KERNEL.global_labels["smt_insert_state"]; diff --git a/evm/src/cpu/kernel/tests/smt/hash.rs b/evm/src/cpu/kernel/tests/smt/hash.rs index b38caecc59..cdc472b99e 100644 --- a/evm/src/cpu/kernel/tests/smt/hash.rs +++ b/evm/src/cpu/kernel/tests/smt/hash.rs @@ -32,7 +32,6 @@ fn test_state_smt(state_smt: Smt) -> Result<()> { state_smt: state_smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; let smt_hash_state = KERNEL.global_labels["smt_hash_state"]; diff --git a/evm/src/cpu/kernel/tests/smt/insert.rs b/evm/src/cpu/kernel/tests/smt/insert.rs index a785e2c08c..b3817c5279 100644 --- a/evm/src/cpu/kernel/tests/smt/insert.rs +++ b/evm/src/cpu/kernel/tests/smt/insert.rs @@ -34,7 +34,6 @@ fn test_state_smt(mut state_smt: Smt, new_key: U256, new_account: Account) -> Re state_smt: state_smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; diff --git a/evm/src/cpu/modfp254.rs b/evm/src/cpu/modfp254.rs index c5b130fa04..82d270c9e6 100644 --- a/evm/src/cpu/modfp254.rs +++ b/evm/src/cpu/modfp254.rs @@ -15,7 +15,7 @@ const P_LIMBS: [u32; 8] = [ 0xd87cfd47, 0x3c208c16, 0x6871ca8d, 0x97816a91, 0x8181585d, 0xb85045b6, 0xe131a029, 0x30644e72, ]; -/// Evaluates constriants to check the modulus in mem_channel[2]. +/// Evaluates constraints to check the modulus in mem_channel[2]. pub fn eval_packed( lv: &CpuColumnsView

, yield_constr: &mut ConstraintConsumer

, @@ -33,7 +33,7 @@ pub fn eval_packed( } /// Circuit version of `eval_packed`. -/// Evaluates constriants to check the modulus in mem_channel[2]. +/// Evaluates constraints to check the modulus in mem_channel[2]. pub fn eval_ext_circuit, const D: usize>( builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, lv: &CpuColumnsView>, diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 4cebf62b2c..68500a9269 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -75,7 +75,7 @@ pub(crate) const JUMPDEST_OP: StackBehavior = StackBehavior { // AUDITORS: If the value below is `None`, then the operation must be manually checked to ensure // that every general-purpose memory channel is either disabled or has its read flag and address -// propertly constrained. The same applies when `disable_other_channels` is set to `false`, +// properly constrained. The same applies when `disable_other_channels` is set to `false`, // except the first `num_pops` and the last `pushes as usize` channels have their read flag and // address constrained automatically in this file. pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsColumnsView { @@ -96,8 +96,12 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsCol pushes: true, disable_other_channels: true, }), - prover_input: None, // TODO - jumps: None, // Depends on whether it's a JUMP or a JUMPI. + prover_input: Some(StackBehavior { + num_pops: 0, + pushes: true, + disable_other_channels: true, + }), + jumps: None, // Depends on whether it's a JUMP or a JUMPI. pc_push0: Some(StackBehavior { num_pops: 0, pushes: true, diff --git a/evm/src/curve_pairings.rs b/evm/src/curve_pairings.rs index d789051a2f..a9b6f14abc 100644 --- a/evm/src/curve_pairings.rs +++ b/evm/src/curve_pairings.rs @@ -370,7 +370,7 @@ const BN_EXP: [bool; 253] = [ false, ]; -// The folowing constants are defined above get_custom_powers +// The following constants are defined above get_custom_powers const BN_EXPS4: [(bool, bool, bool); 64] = [ (true, true, false), diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index d3b1b0c7b2..d88dc08b53 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -48,6 +48,8 @@ pub struct GenerationInputs { pub block_bloom_after: [U256; 8], pub signed_txns: Vec>, + // Withdrawal pairs `(addr, amount)`. At the end of the txs, `amount` is added to `addr`'s balance. See EIP-4895. + pub withdrawals: Vec<(Address, U256)>, pub tries: TrieInputs, /// Expected trie roots after the transactions are executed. pub trie_roots_after: TrieRoots, @@ -83,10 +85,6 @@ pub struct TrieInputs { /// A partial version of the receipt trie prior to these transactions. It should include all nodes /// that will be accessed by these transactions. pub receipts_trie: HashedPartialTrie, - - /// A partial version of each storage trie prior to these transactions. It should include all - /// storage tries, and nodes therein, that will be accessed by these transactions. - pub storage_tries: Vec<(H256, HashedPartialTrie)>, } impl Default for TrieInputs { @@ -96,7 +94,6 @@ impl Default for TrieInputs { state_smt: vec![U256::zero(); 4], transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], } } } diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs index 726c20225b..35d4a396e8 100644 --- a/evm/src/generation/mpt.rs +++ b/evm/src/generation/mpt.rs @@ -46,6 +46,17 @@ pub struct LegacyReceiptRlp { pub logs: Vec, } +impl LegacyReceiptRlp { + // RLP encode the receipt and prepend the tx type. + pub fn encode(&self, tx_type: u8) -> Vec { + let mut bytes = rlp::encode(self).to_vec(); + if tx_type != 0 { + bytes.insert(0, tx_type); + } + bytes + } +} + pub(crate) fn state_smt_prover_inputs_reversed(trie_inputs: &TrieInputs) -> Vec { let mut inputs = state_smt_prover_inputs(trie_inputs); inputs.reverse(); diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index cf618d1717..013f41a354 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -45,6 +45,7 @@ impl GenerationState { "current_hash" => self.run_current_hash(), "account_code" => self.run_account_code(input_fn), "bignum_modmul" => self.run_bignum_modmul(), + "withdrawal" => self.run_withdrawal(), _ => Err(ProgramError::ProverInputError(InvalidFunction)), } } @@ -233,6 +234,13 @@ impl GenerationState { (biguint_to_mem_vec(rem), biguint_to_mem_vec(quo)) } + + /// Withdrawal data. + fn run_withdrawal(&mut self) -> Result { + self.withdrawal_prover_inputs + .pop() + .ok_or(ProgramError::ProverInputError(OutOfWithdrawalData)) + } } enum EvmField { diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 79e0cfdb85..c834c1f144 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -43,6 +43,8 @@ pub(crate) struct GenerationState { /// via `pop()`. pub(crate) rlp_prover_inputs: Vec, + pub(crate) withdrawal_prover_inputs: Vec, + /// The state trie only stores state keys, which are hashes of addresses, but sometimes it is /// useful to see the actual addresses for debugging. Here we store the mapping for all known /// addresses. @@ -63,11 +65,11 @@ impl GenerationState { &inputs.tries.transactions_trie ); log::debug!("Input receipts_trie: {:?}", &inputs.tries.receipts_trie); - log::debug!("Input storage_tries: {:?}", &inputs.tries.storage_tries); log::debug!("Input contract_code: {:?}", &inputs.contract_code); let state_smt_prover_inputs = state_smt_prover_inputs_reversed(&inputs.tries); let mpt_prover_inputs = all_mpt_prover_inputs_reversed(&inputs.tries)?; let rlp_prover_inputs = all_rlp_prover_inputs_reversed(&inputs.signed_txns); + let withdrawal_prover_inputs = all_withdrawals_prover_inputs_reversed(&inputs.withdrawals); let bignum_modmul_result_limbs = Vec::new(); Ok(Self { @@ -79,6 +81,7 @@ impl GenerationState { mpt_prover_inputs, state_smt_prover_inputs, rlp_prover_inputs, + withdrawal_prover_inputs, state_key_to_address: HashMap::new(), bignum_modmul_result_limbs, }) @@ -154,3 +157,16 @@ impl GenerationState { .collect() } } + +/// Withdrawals prover input array is of the form `[addr0, amount0, ..., addrN, amountN, U256::MAX, U256::MAX]`. +/// Returns the reversed array. +fn all_withdrawals_prover_inputs_reversed(withdrawals: &[(Address, U256)]) -> Vec { + let mut withdrawal_prover_inputs = withdrawals + .iter() + .flat_map(|w| [U256::from((w.0).0.as_slice()), w.1]) + .collect::>(); + withdrawal_prover_inputs.push(U256::MAX); + withdrawal_prover_inputs.push(U256::MAX); + withdrawal_prover_inputs.reverse(); + withdrawal_prover_inputs +} diff --git a/evm/src/keccak_sponge/columns.rs b/evm/src/keccak_sponge/columns.rs index f10dfbfd9a..c7f54bb2c1 100644 --- a/evm/src/keccak_sponge/columns.rs +++ b/evm/src/keccak_sponge/columns.rs @@ -41,9 +41,6 @@ pub(crate) struct KeccakSpongeColumnsView { /// The timestamp at which inputs should be read from memory. pub timestamp: T, - /// The length of the original input, in bytes. - pub len: T, - /// The number of input bytes that have already been absorbed prior to this block. pub already_absorbed_bytes: T, diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 03c1f811a0..1e8a0f025c 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -1,5 +1,5 @@ use std::borrow::Borrow; -use std::iter::{once, repeat}; +use std::iter::{self, once, repeat}; use std::marker::PhantomData; use std::mem::size_of; @@ -41,15 +41,23 @@ pub(crate) fn ctl_looked_data() -> Vec> { outputs.push(cur_col); } - Column::singles([ - cols.context, - cols.segment, - cols.virt, - cols.len, - cols.timestamp, - ]) - .chain(outputs) - .collect() + // The length of the inputs is `already_absorbed_bytes + is_final_input_len`. + let len_col = Column::linear_combination( + iter::once((cols.already_absorbed_bytes, F::ONE)).chain( + cols.is_final_input_len + .iter() + .enumerate() + .map(|(i, &elt)| (elt, F::from_canonical_usize(i))), + ), + ); + + let mut res: Vec> = + Column::singles([cols.context, cols.segment, cols.virt]).collect(); + res.push(len_col); + res.push(Column::single(cols.timestamp)); + res.extend(outputs); + + res } /// Creates the vector of `Columns` corresponding to the inputs of the Keccak sponge. @@ -397,7 +405,6 @@ impl, const D: usize> KeccakSpongeStark { row.segment = F::from_canonical_usize(op.base_address.segment); row.virt = F::from_canonical_usize(op.base_address.virt); row.timestamp = F::from_canonical_usize(op.timestamp); - row.len = F::from_canonical_usize(op.input.len()); row.already_absorbed_bytes = F::from_canonical_usize(already_absorbed_bytes); row.original_rate_u32s = sponge_state[..KECCAK_RATE_U32S] @@ -584,13 +591,6 @@ impl, const D: usize> Stark for KeccakSpongeS yield_constr.constraint_transition( is_dummy * (next_values.is_full_input_block + next_is_final_block), ); - - // If this is a final block, is_final_input_len implies `len - already_absorbed == i`. - let offset = local_values.len - already_absorbed_bytes; - for (i, &is_final_len) in local_values.is_final_input_len.iter().enumerate() { - let entry_match = offset - P::from(FE::from_canonical_usize(i)); - yield_constr.constraint(is_final_len * entry_match); - } } fn eval_ext_circuit( @@ -728,16 +728,6 @@ impl, const D: usize> Stark for KeccakSpongeS builder.mul_extension(is_dummy, tmp) }; yield_constr.constraint_transition(builder, constraint); - - // If this is a final block, is_final_input_len implies `len - already_absorbed == i`. - let offset = builder.sub_extension(local_values.len, already_absorbed_bytes); - for (i, &is_final_len) in local_values.is_final_input_len.iter().enumerate() { - let index = builder.constant_extension(F::from_canonical_usize(i).into()); - let entry_match = builder.sub_extension(offset, index); - - let constraint = builder.mul_extension(is_final_len, entry_match); - yield_constr.constraint(builder, constraint); - } } fn constraint_degree(&self) -> usize { diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 74aa8787d7..725ae79d4c 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -324,10 +324,14 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark( let pc = state.registers.program_counter; let input_fn = &KERNEL.prover_inputs[&pc]; let input = state.prover_input(input_fn)?; + let opcode = 0x49.into(); + // `ArithmeticStark` range checks `mem_channels[0]`, which contains + // the top of the stack, `mem_channels[1]`, `mem_channels[2]` and + // next_row's `mem_channels[0]` which contains the next top of the stack. + // Our goal here is to range-check the input, in the next stack top. + let range_check_op = arithmetic::Operation::range_check( + state.registers.stack_top, + U256::from(0), + U256::from(0), + opcode, + input, + ); + push_with_write(state, &mut row, input)?; + + state.traces.push_arithmetic(range_check_op); state.traces.push_cpu(row); Ok(()) } @@ -772,10 +787,24 @@ pub(crate) fn generate_syscall( let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2; let new_program_counter = u256_to_usize(handler_addr)?; + let gas = U256::from(state.registers.gas_used); + let syscall_info = U256::from(state.registers.program_counter + 1) + (U256::from(u64::from(state.registers.is_kernel)) << 32) - + (U256::from(state.registers.gas_used) << 192); - + + (gas << 192); + + // `ArithmeticStark` range checks `mem_channels[0]`, which contains + // the top of the stack, `mem_channels[1]`, `mem_channels[2]` and + // next_row's `mem_channels[0]` which contains the next top of the stack. + // Our goal here is to range-check the gas, contained in syscall_info, + // stored in the next stack top. + let range_check_op = arithmetic::Operation::range_check( + state.registers.stack_top, + handler_addr0, + handler_addr1, + U256::from(opcode), + syscall_info, + ); // Set registers before pushing to the stack; in particular, we need to set kernel mode so we // can't incorrectly trigger a stack overflow. However, note that we have to do it _after_ we // make `syscall_info`, which should contain the old values. @@ -787,6 +816,7 @@ pub(crate) fn generate_syscall( log::debug!("Syscall to {}", KERNEL.offset_name(new_program_counter)); + state.traces.push_arithmetic(range_check_op); state.traces.push_memory(log_in0); state.traces.push_memory(log_in1); state.traces.push_memory(log_in2); @@ -1055,9 +1085,27 @@ pub(crate) fn generate_exception( let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2; let new_program_counter = u256_to_usize(handler_addr)?; - let exc_info = - U256::from(state.registers.program_counter) + (U256::from(state.registers.gas_used) << 192); + let gas = U256::from(state.registers.gas_used); + + let exc_info = U256::from(state.registers.program_counter) + (gas << 192); + // Get the opcode so we can provide it to the range_check operation. + let code_context = state.registers.code_context(); + let address = MemoryAddress::new(code_context, Segment::Code, state.registers.program_counter); + let opcode = state.memory.get(address); + + // `ArithmeticStark` range checks `mem_channels[0]`, which contains + // the top of the stack, `mem_channels[1]`, `mem_channels[2]` and + // next_row's `mem_channels[0]` which contains the next top of the stack. + // Our goal here is to range-check the gas, contained in syscall_info, + // stored in the next stack top. + let range_check_op = arithmetic::Operation::range_check( + state.registers.stack_top, + handler_addr0, + handler_addr1, + opcode, + exc_info, + ); // Set registers before pushing to the stack; in particular, we need to set kernel mode so we // can't incorrectly trigger a stack overflow. However, note that we have to do it _after_ we // make `exc_info`, which should contain the old values. @@ -1068,7 +1116,7 @@ pub(crate) fn generate_exception( push_with_write(state, &mut row, exc_info)?; log::debug!("Exception to {}", KERNEL.offset_name(new_program_counter)); - + state.traces.push_arithmetic(range_check_op); state.traces.push_memory(log_in0); state.traces.push_memory(log_in1); state.traces.push_memory(log_in2); diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs index 3944c89d55..9457750e27 100644 --- a/evm/src/witness/traces.rs +++ b/evm/src/witness/traces.rs @@ -70,6 +70,7 @@ impl Traces { BinaryOperator::Div | BinaryOperator::Mod => 2, _ => 1, }, + Operation::RangeCheckOperation { .. } => 1, }) .sum(), byte_packing_len: self.byte_packing_ops.iter().map(|op| op.bytes.len()).sum(), diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index f75ea18024..e1a1c292fb 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -439,10 +439,11 @@ pub(crate) fn transition(state: &mut GenerationState) -> anyhow let offset_name = KERNEL.offset_name(state.registers.program_counter); bail!( "{:?} in kernel at pc={}, stack={:?}, memory={:?}", + // "{:?} in kernel at pc={}, stack={:?}", e, offset_name, state.stack(), - state.memory.contexts[0].segments[Segment::KernelGeneral as usize].content, + state.memory.contexts[0].segments[Segment::TrieData as usize].content, ); } state.rollback(checkpoint); diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs index ef38fb5c68..4405a9697d 100644 --- a/evm/tests/add11_yml.rs +++ b/evm/tests/add11_yml.rs @@ -78,7 +78,6 @@ fn add11_yml() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![(to_hashed, Node::Empty.into())], }; let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); @@ -155,6 +154,7 @@ fn add11_yml() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, diff --git a/evm/tests/basic_smart_contract.rs b/evm/tests/basic_smart_contract.rs index a4a58901ff..f64010f415 100644 --- a/evm/tests/basic_smart_contract.rs +++ b/evm/tests/basic_smart_contract.rs @@ -80,7 +80,6 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![], }; let txdata_gas = 2 * 16; @@ -153,6 +152,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, diff --git a/evm/tests/empty_txn_list.rs b/evm/tests/empty_txn_list.rs index 36c19bc4ca..68a71b24bd 100644 --- a/evm/tests/empty_txn_list.rs +++ b/evm/tests/empty_txn_list.rs @@ -37,7 +37,6 @@ fn test_empty_txn_list() -> anyhow::Result<()> { let state_smt = Smt::empty(); let transactions_trie = HashedPartialTrie::from(Node::Empty); let receipts_trie = HashedPartialTrie::from(Node::Empty); - let storage_tries = vec![]; let mut contract_code = HashMap::new(); contract_code.insert(keccak(vec![]), vec![]); @@ -50,11 +49,11 @@ fn test_empty_txn_list() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![], + withdrawals: vec![], tries: TrieInputs { state_smt: state_smt.serialize(), transactions_trie, receipts_trie, - storage_tries, }, trie_roots_after, contract_code, diff --git a/evm/tests/erc20.rs b/evm/tests/erc20.rs new file mode 100644 index 0000000000..dbac04a7b4 --- /dev/null +++ b/evm/tests/erc20.rs @@ -0,0 +1,295 @@ +use std::str::FromStr; +use std::time::Duration; + +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; +use eth_trie_utils::nibbles::Nibbles; +use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; +use hex_literal::hex; +use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::plonk::config::KeccakGoldilocksConfig; +use plonky2::util::timing::TimingTree; +use plonky2_evm::all_stark::AllStark; +use plonky2_evm::config::StarkConfig; +use plonky2_evm::generation::mpt::{LegacyReceiptRlp, LogRlp}; +use plonky2_evm::generation::{GenerationInputs, TrieInputs}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; +use plonky2_evm::prover::prove; +use plonky2_evm::verifier::verify_proof; +use plonky2_evm::Node; +use smt_utils::account::Account; +use smt_utils::smt::Smt; + +type F = GoldilocksField; +const D: usize = 2; +type C = KeccakGoldilocksConfig; + +/// Test a simple ERC20 transfer. +/// Used the following Solidity code: +/// ```solidity +/// pragma solidity ^0.8.13; +/// import "../lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; +/// contract Token is ERC20 { +/// constructor() ERC20("Token", "TKN") { +/// _mint(msg.sender, 1_000_000 ether); +/// } +/// } +/// contract Giver { +/// Token public token; +/// constructor(address _token) { +/// token = Token(_token); +/// } +/// function send(uint256 amount) public { +/// token.transfer(0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326, amount); +/// } +/// } +/// ``` +#[test] +fn test_erc20() -> anyhow::Result<()> { + init_logger(); + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + let sender = hex!("70997970C51812dc3A010C7d01b50e0d17dc79C8"); + let giver = hex!("e7f1725E7734CE288F8367e1Bb143E90bb3F0512"); + let token = hex!("5FbDB2315678afecb367f032d93F642f64180aa3"); + + let sender_state_key = keccak(sender); + let giver_state_key = keccak(giver); + let token_state_key = keccak(token); + + let sender_bits = sender_state_key.into(); + let giver_bits = giver_state_key.into(); + let token_bits = token_state_key.into(); + + let mut state_smt_before = Smt::empty(); + state_smt_before + .insert(sender_bits, sender_account().into()) + .unwrap(); + state_smt_before + .insert(giver_bits, giver_account().into()) + .unwrap(); + state_smt_before + .insert(token_bits, token_account().into()) + .unwrap(); + + let tries_before = TrieInputs { + state_smt: state_smt_before.serialize(), + transactions_trie: HashedPartialTrie::from(Node::Empty), + receipts_trie: HashedPartialTrie::from(Node::Empty), + }; + + let txn = signed_tx(); + + let gas_used = 56_499.into(); + let bloom = bloom(); + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: gas_used, + block_bloom: bloom, + }; + + let contract_code = [giver_bytecode(), token_bytecode(), vec![]] + .map(|v| (keccak(v.clone()), v)) + .into(); + + let expected_state_trie_after = { + let mut state_smt_after = Smt::empty(); + let sender_account = sender_account(); + let sender_account_after = Account { + nonce: sender_account.nonce + 1, + balance: sender_account.balance - gas_used * 0xa, + ..sender_account + }; + state_smt_after + .insert(sender_bits, sender_account_after.into()) + .unwrap(); + state_smt_after + .insert(giver_bits, giver_account().into()) + .unwrap(); + let token_account_after = Account { + storage_smt: token_storage_after(), + ..token_account() + }; + state_smt_after + .insert(token_bits, token_account_after.into()) + .unwrap(); + + state_smt_after + }; + + let receipt_0 = LegacyReceiptRlp { + status: true, + cum_gas_used: gas_used, + bloom: bloom_bytes().to_vec().into(), + logs: vec![LogRlp { + address: H160::from_str("0x5fbdb2315678afecb367f032d93f642f64180aa3").unwrap(), + topics: vec![ + H256::from_str( + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + ) + .unwrap(), + H256::from_str( + "0x000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f0512", + ) + .unwrap(), + H256::from_str( + "0x0000000000000000000000001f9090aae28b8a3dceadf281b0f12828e676c326", + ) + .unwrap(), + ], + data: hex!("0000000000000000000000000000000000000000000000056bc75e2d63100000") + .to_vec() + .into(), + }], + }; + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + receipts_trie.insert(Nibbles::from_str("0x80").unwrap(), receipt_0.encode(2)); + let transactions_trie: HashedPartialTrie = Node::Leaf { + nibbles: Nibbles::from_str("0x80").unwrap(), + value: txn.to_vec(), + } + .into(); + + let trie_roots_after = TrieRoots { + state_root: expected_state_trie_after.root, + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + let inputs = GenerationInputs { + signed_txns: vec![txn.to_vec()], + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: gas_used, + block_bloom_before: [0.into(); 8], + block_bloom_after: bloom, + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + addresses: vec![], + }; + + let mut timing = TimingTree::new("prove", log::Level::Debug); + let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + verify_proof(&all_stark, proof, &config) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); +} + +fn giver_bytecode() -> Vec { + hex!("608060405234801561001057600080fd5b50600436106100365760003560e01c8063a52c101e1461003b578063fc0c546a14610050575b600080fd5b61004e61004936600461010c565b61007f565b005b600054610063906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b60005460405163a9059cbb60e01b8152731f9090aae28b8a3dceadf281b0f12828e676c3266004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af11580156100e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101089190610125565b5050565b60006020828403121561011e57600080fd5b5035919050565b60006020828403121561013757600080fd5b8151801515811461014757600080fd5b939250505056fea264697066735822122050741efdbac11eb0bbb776ce3ac6004e596b7d7559658a12506164388c371cfd64736f6c63430008140033").into() +} + +fn token_bytecode() -> Vec { + hex!("608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce567146100fe57806370a082311461010d57806395d89b4114610136578063a9059cbb1461013e578063dd62ed3e1461015157600080fd5b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100d957806323b872dd146100eb575b600080fd5b6100a061018a565b6040516100ad919061056a565b60405180910390f35b6100c96100c43660046105d4565b61021c565b60405190151581526020016100ad565b6002545b6040519081526020016100ad565b6100c96100f93660046105fe565b610236565b604051601281526020016100ad565b6100dd61011b36600461063a565b6001600160a01b031660009081526020819052604090205490565b6100a061025a565b6100c961014c3660046105d4565b610269565b6100dd61015f36600461065c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101999061068f565b80601f01602080910402602001604051908101604052809291908181526020018280546101c59061068f565b80156102125780601f106101e757610100808354040283529160200191610212565b820191906000526020600020905b8154815290600101906020018083116101f557829003601f168201915b5050505050905090565b60003361022a818585610277565b60019150505b92915050565b600033610244858285610289565b61024f85858561030c565b506001949350505050565b6060600480546101999061068f565b60003361022a81858561030c565b610284838383600161036b565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461030657818110156102f757604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b6103068484848403600061036b565b50505050565b6001600160a01b03831661033657604051634b637e8f60e11b8152600060048201526024016102ee565b6001600160a01b0382166103605760405163ec442f0560e01b8152600060048201526024016102ee565b610284838383610440565b6001600160a01b0384166103955760405163e602df0560e01b8152600060048201526024016102ee565b6001600160a01b0383166103bf57604051634a1406b160e11b8152600060048201526024016102ee565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561030657826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161043291815260200190565b60405180910390a350505050565b6001600160a01b03831661046b57806002600082825461046091906106c9565b909155506104dd9050565b6001600160a01b038316600090815260208190526040902054818110156104be5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102ee565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b0382166104f957600280548290039055610518565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161055d91815260200190565b60405180910390a3505050565b600060208083528351808285015260005b818110156105975785810183015185820160400152820161057b565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146105cf57600080fd5b919050565b600080604083850312156105e757600080fd5b6105f0836105b8565b946020939093013593505050565b60008060006060848603121561061357600080fd5b61061c846105b8565b925061062a602085016105b8565b9150604084013590509250925092565b60006020828403121561064c57600080fd5b610655826105b8565b9392505050565b6000806040838503121561066f57600080fd5b610678836105b8565b9150610686602084016105b8565b90509250929050565b600181811c908216806106a357607f821691505b6020821081036106c357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561023057634e487b7160e01b600052601160045260246000fdfea2646970667358221220266a323ae4a816f6c6342a5be431fedcc0d45c44b02ea75f5474eb450b5d45b364736f6c63430008140033").into() +} + +fn insert_storage(smt: &mut Smt, slot: U256, value: U256) { + let mut bytes = [0; 32]; + slot.to_big_endian(&mut bytes); + let key = keccak(bytes); + let bits = key.into(); + smt.insert(bits, value.into()).unwrap(); +} + +fn sd2u(s: &str) -> U256 { + U256::from_dec_str(s).unwrap() +} + +fn giver_storage() -> Smt { + let mut smt = Smt::empty(); + insert_storage( + &mut smt, + U256::zero(), + sd2u("546584486846459126461364135121053344201067465379"), + ); + smt +} + +fn token_storage() -> Smt { + let mut smt = Smt::empty(); + insert_storage( + &mut smt, + sd2u("82183438603287090451672504949863617512989139203883434767553028632841710582583"), + sd2u("1000000000000000000000"), + ); + smt +} + +fn token_storage_after() -> Smt { + let mut smt = Smt::empty(); + insert_storage( + &mut smt, + sd2u("82183438603287090451672504949863617512989139203883434767553028632841710582583"), + sd2u("900000000000000000000"), + ); + insert_storage( + &mut smt, + sd2u("53006154680716014998529145169423020330606407246856709517064848190396281160729"), + sd2u("100000000000000000000"), + ); + smt +} + +fn giver_account() -> Account { + Account { + nonce: 1, + balance: 0.into(), + code_hash: keccak(giver_bytecode()), + storage_smt: giver_storage(), + } +} + +fn token_account() -> Account { + Account { + nonce: 1, + balance: 0.into(), + code_hash: keccak(token_bytecode()), + storage_smt: token_storage(), + } +} + +fn sender_account() -> Account { + Account { + nonce: 0, + balance: sd2u("10000000000000000000000"), + code_hash: keccak([]), + storage_smt: Smt::empty(), + } +} + +fn signed_tx() -> Vec { + hex!("02f88701800a0a830142c594e7f1725e7734ce288f8367e1bb143e90bb3f051280a4a52c101e0000000000000000000000000000000000000000000000056bc75e2d63100000c001a0303f5591159d7ea303faecb1c8bd8624b55732f769de28b111190dfb9a7c5234a019d5d6d38938dc1c63acbe106cf361672def773ace4ca587860117d057326627").into() +} + +fn bloom_bytes() -> [u8; 256] { + hex!("00000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000002000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000020000000000080000000000000000000000000000000000000000000000000000000000000000") +} +fn bloom() -> [U256; 8] { + let bloom = bloom_bytes() + .chunks_exact(32) + .map(U256::from_big_endian) + .collect::>(); + bloom.try_into().unwrap() +} diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index e9212ede94..73c07f50f8 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -125,7 +125,6 @@ fn test_log_opcodes() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: receipts_trie.clone(), - storage_tries: vec![(to_hashed, Node::Empty.into())], }; // Prove a transaction which carries out two LOG opcodes. @@ -230,6 +229,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { ]; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, @@ -341,7 +341,6 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![], }; let txn = hex!("f85f800a82520894095e7baea6a6c7c4c2dfeb977efac326af552d870a8026a0122f370ed4023a6c253350c6bfb87d7d7eb2cd86447befee99e0a26b70baec20a07100ab1b3977f2b4571202b9f4b68850858caf5469222794600b5ce1cfb348ad"); @@ -429,6 +428,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { let inputs_first = GenerationInputs { signed_txns: vec![txn.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after: tries_after, contract_code, @@ -472,7 +472,6 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { state_smt: state_trie_before.serialize(), transactions_trie: transactions_trie.clone(), receipts_trie: receipts_trie.clone(), - storage_tries: vec![], }; // Prove a transaction which carries out two LOG opcodes. @@ -569,6 +568,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { ]; let inputs = GenerationInputs { signed_txns: vec![txn_2.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, @@ -786,7 +786,6 @@ fn test_two_txn() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![(to_hashed, Node::Empty.into())], }; // Prove two simple transfers. @@ -878,6 +877,7 @@ fn test_two_txn() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![txn_0.to_vec(), txn_1.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, diff --git a/evm/tests/many_transactions.rs b/evm/tests/many_transactions.rs index 7d9bfae572..399d2b80d0 100644 --- a/evm/tests/many_transactions.rs +++ b/evm/tests/many_transactions.rs @@ -78,7 +78,6 @@ fn test_four_transactions() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![], }; // Generated using a little py-evm script. @@ -179,6 +178,7 @@ fn test_four_transactions() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![txn1.to_vec(), txn2.to_vec(), txn3.to_vec(), txn4.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), diff --git a/evm/tests/self_balance_gas_cost.rs b/evm/tests/self_balance_gas_cost.rs index fdcd04b05d..a6a7fb0810 100644 --- a/evm/tests/self_balance_gas_cost.rs +++ b/evm/tests/self_balance_gas_cost.rs @@ -88,7 +88,6 @@ fn self_balance_gas_cost() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![(to_hashed, Node::Empty.into())], }; let txn = hex!("f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"); @@ -169,6 +168,7 @@ fn self_balance_gas_cost() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, diff --git a/evm/tests/selfdestruct.rs b/evm/tests/selfdestruct.rs index 88af15aca7..009971ca14 100644 --- a/evm/tests/selfdestruct.rs +++ b/evm/tests/selfdestruct.rs @@ -72,7 +72,6 @@ fn test_selfdestruct() -> anyhow::Result<()> { state_smt: state_trie_before.serialize(), transactions_trie: HashedPartialTrie::from(Node::Empty), receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], }; // Generated using a little py-evm script. @@ -127,6 +126,7 @@ fn test_selfdestruct() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, diff --git a/evm/tests/simple_transfer.rs b/evm/tests/simple_transfer.rs index b4fc49166e..5a6e099a74 100644 --- a/evm/tests/simple_transfer.rs +++ b/evm/tests/simple_transfer.rs @@ -62,7 +62,6 @@ fn test_simple_transfer() -> anyhow::Result<()> { state_smt: state_smt_before.serialize(), transactions_trie: HashedPartialTrie::from(Node::Empty), receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], }; // Generated using a little py-evm script. @@ -130,6 +129,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { }; let inputs = GenerationInputs { signed_txns: vec![txn.to_vec()], + withdrawals: vec![], tries: tries_before, trie_roots_after, contract_code, diff --git a/evm/tests/withdrawals.rs b/evm/tests/withdrawals.rs new file mode 100644 index 0000000000..23431e45e0 --- /dev/null +++ b/evm/tests/withdrawals.rs @@ -0,0 +1,98 @@ +use std::collections::HashMap; +use std::time::Duration; + +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; +use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie}; +use ethereum_types::{H160, H256, U256}; +use keccak_hash::keccak; +use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::plonk::config::PoseidonGoldilocksConfig; +use plonky2::util::timing::TimingTree; +use plonky2_evm::all_stark::AllStark; +use plonky2_evm::config::StarkConfig; +use plonky2_evm::generation::{GenerationInputs, TrieInputs}; +use plonky2_evm::proof::{BlockHashes, BlockMetadata, TrieRoots}; +use plonky2_evm::prover::prove; +use plonky2_evm::verifier::verify_proof; +use plonky2_evm::Node; +use rand::random; +use smt_utils::account::Account; +use smt_utils::bits::Bits; +use smt_utils::smt::Smt; + +type F = GoldilocksField; +const D: usize = 2; +type C = PoseidonGoldilocksConfig; + +/// Execute 0 txns and 1 withdrawal. +#[test] +fn test_withdrawals() -> anyhow::Result<()> { + init_logger(); + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let block_metadata = BlockMetadata::default(); + + let state_smt_before = Smt::empty(); + let transactions_trie = HashedPartialTrie::from(Node::Empty); + let receipts_trie = HashedPartialTrie::from(Node::Empty); + + let mut contract_code = HashMap::new(); + contract_code.insert(keccak(vec![]), vec![]); + + // Just one withdrawal. + let withdrawals = vec![(H160(random()), U256(random()))]; + + let state_smt_after = { + let mut smt = Smt::empty(); + let addr_state_key = keccak(withdrawals[0].0); + let addr_bits = Bits::from(addr_state_key); + let account = Account { + balance: withdrawals[0].1, + ..Account::default() + }; + smt.insert(addr_bits, account.into()).unwrap(); + smt + }; + + let trie_roots_after = TrieRoots { + state_root: state_smt_after.root, + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + + let inputs = GenerationInputs { + signed_txns: vec![], + withdrawals, + tries: TrieInputs { + state_smt: state_smt_before.serialize(), + transactions_trie, + receipts_trie, + }, + trie_roots_after, + contract_code, + genesis_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 0.into(), + block_bloom_before: [0.into(); 8], + block_bloom_after: [0.into(); 8], + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + addresses: vec![], + }; + + let mut timing = TimingTree::new("prove", log::Level::Debug); + let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + verify_proof(&all_stark, proof, &config) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); +} diff --git a/field/src/arch/x86_64/avx2_goldilocks_field.rs b/field/src/arch/x86_64/avx2_goldilocks_field.rs index ffae8693be..54b69679c8 100644 --- a/field/src/arch/x86_64/avx2_goldilocks_field.rs +++ b/field/src/arch/x86_64/avx2_goldilocks_field.rs @@ -425,7 +425,7 @@ unsafe fn add_small_64s_64_s(x_s: __m256i, y: __m256i) -> __m256i { // 0xffffffff and the addition of the low 32 bits generated a carry. This can never occur if y // <= 0xffffffff00000000: if y >> 32 = 0xffffffff, then no carry can occur. let mask = _mm256_cmpgt_epi32(x_s, res_wrapped_s); // -1 if overflowed else 0. - // The mask contains 0xffffffff in the high 32 bits if wraparound occured and 0 otherwise. + // The mask contains 0xffffffff in the high 32 bits if wraparound occurred and 0 otherwise. let wrapback_amt = _mm256_srli_epi64::<32>(mask); // -FIELD_ORDER if overflowed else 0. let res_s = _mm256_add_epi64(res_wrapped_s, wrapback_amt); res_s @@ -442,7 +442,7 @@ unsafe fn sub_small_64s_64_s(x_s: __m256i, y: __m256i) -> __m256i { // 0xffffffff and the subtraction of the low 32 bits generated a borrow. This can never occur if // y <= 0xffffffff00000000: if y >> 32 = 0xffffffff, then no borrow can occur. let mask = _mm256_cmpgt_epi32(res_wrapped_s, x_s); // -1 if underflowed else 0. - // The mask contains 0xffffffff in the high 32 bits if wraparound occured and 0 otherwise. + // The mask contains 0xffffffff in the high 32 bits if wraparound occurred and 0 otherwise. let wrapback_amt = _mm256_srli_epi64::<32>(mask); // -FIELD_ORDER if underflowed else 0. let res_s = _mm256_sub_epi64(res_wrapped_s, wrapback_amt); res_s diff --git a/plonky2/src/gadgets/arithmetic.rs b/plonky2/src/gadgets/arithmetic.rs index 858a4eaf07..e3af60e9c5 100644 --- a/plonky2/src/gadgets/arithmetic.rs +++ b/plonky2/src/gadgets/arithmetic.rs @@ -1,3 +1,4 @@ +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; use core::borrow::Borrow; diff --git a/plonky2/src/gadgets/arithmetic_extension.rs b/plonky2/src/gadgets/arithmetic_extension.rs index 0fe8083aa3..3c1deac381 100644 --- a/plonky2/src/gadgets/arithmetic_extension.rs +++ b/plonky2/src/gadgets/arithmetic_extension.rs @@ -1,3 +1,4 @@ +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; use core::borrow::Borrow; diff --git a/plonky2/src/gadgets/lookup.rs b/plonky2/src/gadgets/lookup.rs index 826f3e2902..4ab765ba03 100644 --- a/plonky2/src/gadgets/lookup.rs +++ b/plonky2/src/gadgets/lookup.rs @@ -1,3 +1,6 @@ +use alloc::borrow::ToOwned; +use alloc::vec; + use crate::field::extension::Extendable; use crate::gates::lookup::LookupGate; use crate::gates::lookup_table::{LookupTable, LookupTableGate}; diff --git a/plonky2/src/gadgets/range_check.rs b/plonky2/src/gadgets/range_check.rs index bdb35f9edc..41af064aa6 100644 --- a/plonky2/src/gadgets/range_check.rs +++ b/plonky2/src/gadgets/range_check.rs @@ -1,3 +1,4 @@ +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; diff --git a/plonky2/src/gadgets/split_base.rs b/plonky2/src/gadgets/split_base.rs index 0a39b8f00c..1562323c8e 100644 --- a/plonky2/src/gadgets/split_base.rs +++ b/plonky2/src/gadgets/split_base.rs @@ -1,3 +1,4 @@ +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; use core::borrow::Borrow; diff --git a/plonky2/src/gadgets/split_join.rs b/plonky2/src/gadgets/split_join.rs index fb83c3a6cc..6901c8caf2 100644 --- a/plonky2/src/gadgets/split_join.rs +++ b/plonky2/src/gadgets/split_join.rs @@ -1,3 +1,4 @@ +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; diff --git a/plonky2/src/gates/arithmetic_base.rs b/plonky2/src/gates/arithmetic_base.rs index 631b1c3715..dca1c5061a 100644 --- a/plonky2/src/gates/arithmetic_base.rs +++ b/plonky2/src/gates/arithmetic_base.rs @@ -1,5 +1,5 @@ use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use crate::field::extension::Extendable; diff --git a/plonky2/src/gates/arithmetic_extension.rs b/plonky2/src/gates/arithmetic_extension.rs index 294c090274..b43d8d3ad4 100644 --- a/plonky2/src/gates/arithmetic_extension.rs +++ b/plonky2/src/gates/arithmetic_extension.rs @@ -1,5 +1,5 @@ use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::ops::Range; diff --git a/plonky2/src/gates/base_sum.rs b/plonky2/src/gates/base_sum.rs index 181252a2d3..28131c1b19 100644 --- a/plonky2/src/gates/base_sum.rs +++ b/plonky2/src/gates/base_sum.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::ops::Range; diff --git a/plonky2/src/gates/coset_interpolation.rs b/plonky2/src/gates/coset_interpolation.rs index c701b8cf7d..99046ec4a8 100644 --- a/plonky2/src/gates/coset_interpolation.rs +++ b/plonky2/src/gates/coset_interpolation.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::marker::PhantomData; @@ -631,8 +631,6 @@ fn partial_interpolate_ext_algebra_target, const D: #[cfg(test)] mod tests { - use core::iter::repeat_with; - use anyhow::Result; use plonky2_field::polynomial::PolynomialValues; use plonky2_util::log2_strict; @@ -832,7 +830,7 @@ mod tests { // Get a working row for InterpolationGate. let shift = F::rand(); - let values = PolynomialValues::new(repeat_with(FF::rand).take(4).collect()); + let values = PolynomialValues::new(core::iter::repeat_with(FF::rand).take(4).collect()); let eval_point = FF::rand(); let gate = CosetInterpolationGate::::with_max_degree(2, 3); let vars = EvaluationVars { diff --git a/plonky2/src/gates/exponentiation.rs b/plonky2/src/gates/exponentiation.rs index 521520e86a..8e708b9711 100644 --- a/plonky2/src/gates/exponentiation.rs +++ b/plonky2/src/gates/exponentiation.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::marker::PhantomData; diff --git a/plonky2/src/gates/lookup.rs b/plonky2/src/gates/lookup.rs index f682be23f2..3c03f6baa9 100644 --- a/plonky2/src/gates/lookup.rs +++ b/plonky2/src/gates/lookup.rs @@ -1,6 +1,6 @@ -use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; +use alloc::{format, vec}; use core::usize; use itertools::Itertools; diff --git a/plonky2/src/gates/lookup_table.rs b/plonky2/src/gates/lookup_table.rs index 9f9d967ea0..a683a87da9 100644 --- a/plonky2/src/gates/lookup_table.rs +++ b/plonky2/src/gates/lookup_table.rs @@ -1,7 +1,7 @@ -use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; +use alloc::{format, vec}; use core::usize; use itertools::Itertools; diff --git a/plonky2/src/gates/multiplication_extension.rs b/plonky2/src/gates/multiplication_extension.rs index 8f6b27db60..ba578d8be6 100644 --- a/plonky2/src/gates/multiplication_extension.rs +++ b/plonky2/src/gates/multiplication_extension.rs @@ -1,5 +1,5 @@ use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::ops::Range; diff --git a/plonky2/src/gates/poseidon.rs b/plonky2/src/gates/poseidon.rs index f6d0657260..0842930d5f 100644 --- a/plonky2/src/gates/poseidon.rs +++ b/plonky2/src/gates/poseidon.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::marker::PhantomData; diff --git a/plonky2/src/gates/poseidon_mds.rs b/plonky2/src/gates/poseidon_mds.rs index 8e2f4a7664..75f9ae5c94 100644 --- a/plonky2/src/gates/poseidon_mds.rs +++ b/plonky2/src/gates/poseidon_mds.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::marker::PhantomData; diff --git a/plonky2/src/gates/random_access.rs b/plonky2/src/gates/random_access.rs index 9110a59b67..df1bb576e4 100644 --- a/plonky2/src/gates/random_access.rs +++ b/plonky2/src/gates/random_access.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::marker::PhantomData; diff --git a/plonky2/src/gates/reducing.rs b/plonky2/src/gates/reducing.rs index b313efe695..46ff69c8d5 100644 --- a/plonky2/src/gates/reducing.rs +++ b/plonky2/src/gates/reducing.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::ops::Range; diff --git a/plonky2/src/gates/reducing_extension.rs b/plonky2/src/gates/reducing_extension.rs index 5492c50611..77c1f39acc 100644 --- a/plonky2/src/gates/reducing_extension.rs +++ b/plonky2/src/gates/reducing_extension.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::ops::Range; diff --git a/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs b/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs index 10d81f280b..4b1d8dfb8d 100644 --- a/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs +++ b/plonky2/src/hash/arch/aarch64/poseidon_goldilocks_neon.rs @@ -89,7 +89,7 @@ unsafe fn add_with_wraparound(a: u64, b: u64) -> u64 { adj = lateout(reg) adj, options(pure, nomem, nostack), ); - res + adj // adj is EPSILON if wraparound occured and 0 otherwise + res + adj // adj is EPSILON if wraparound occurred and 0 otherwise } /// Subtraction of a and (b >> 32) modulo ORDER accounting for wraparound. @@ -152,7 +152,7 @@ unsafe fn multiply(x: u64, y: u64) -> u64 { // ==================================== STANDALONE CONST LAYER ===================================== /// Standalone const layer. Run only once, at the start of round 1. Remaining const layers are fused -/// with the preceeding MDS matrix multiplication. +/// with the preceding MDS matrix multiplication. /* #[inline(always)] #[unroll_for_loops] diff --git a/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs b/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs index c7a65f9016..e56fea5ea7 100644 --- a/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs +++ b/plonky2/src/hash/arch/x86_64/poseidon_goldilocks_avx2_bmi2.rs @@ -18,7 +18,7 @@ use crate::util::branch_hint; const WIDTH: usize = 12; -// These tranformed round constants are used where the constant layer is fused with the preceeding +// These transformed round constants are used where the constant layer is fused with the preceding // MDS layer. The FUSED_ROUND_CONSTANTS for round i are the ALL_ROUND_CONSTANTS for round i + 1. // The FUSED_ROUND_CONSTANTS for the very last round are 0, as it is not followed by a constant // layer. On top of that, all FUSED_ROUND_CONSTANTS are shifted by 2 ** 63 to save a few XORs per @@ -183,10 +183,10 @@ unsafe fn const_layer( // occur if all round constants are < 0xffffffff00000001 = ORDER: if the high bits are // 0xffffffff, then the low bits are 0, so the carry bit cannot occur. So this trick is valid // as long as all the round constants are in canonical form. - // The mask contains 0xffffffff in the high doubleword if wraparound occured and 0 otherwise. + // The mask contains 0xffffffff in the high doubleword if wraparound occurred and 0 otherwise. // We will ignore the low doubleword. let wraparound_mask = map3!(_mm256_cmpgt_epi32, state_s, res_maybe_wrapped_s); - // wraparound_adjustment contains 0xffffffff = EPSILON if wraparound occured and 0 otherwise. + // wraparound_adjustment contains 0xffffffff = EPSILON if wraparound occurred and 0 otherwise. let wraparound_adjustment = map3!(_mm256_srli_epi64::<32>, wraparound_mask); // XOR commutes with the addition below. Placing it here helps mask latency. let res_maybe_wrapped = map3!(_mm256_xor_si256, res_maybe_wrapped_s, rep sign_bit); @@ -939,7 +939,7 @@ pub unsafe fn poseidon(state: &[GoldilocksField; 12]) -> [GoldilocksField; 12] { let state = load_state(state); // The first constant layer must be done explicitly. The remaining constant layers are fused - // with the preceeding MDS layer. + // with the preceding MDS layer. let state = const_layer(state, &ALL_ROUND_CONSTANTS[0..WIDTH].try_into().unwrap()); let state = half_full_rounds(state, 0); diff --git a/plonky2/src/hash/hashing.rs b/plonky2/src/hash/hashing.rs index 28d3b89f28..f5fe1f1ef6 100644 --- a/plonky2/src/hash/hashing.rs +++ b/plonky2/src/hash/hashing.rs @@ -2,7 +2,6 @@ use alloc::vec::Vec; use core::fmt::Debug; -use std::iter::repeat; use crate::field::extension::Extendable; use crate::field::types::Field; @@ -34,7 +33,7 @@ impl, const D: usize> CircuitBuilder { num_outputs: usize, ) -> Vec { let zero = self.zero(); - let mut state = H::AlgebraicPermutation::new(std::iter::repeat(zero)); + let mut state = H::AlgebraicPermutation::new(core::iter::repeat(zero)); // Absorb all input chunks. for input_chunk in inputs.chunks(H::AlgebraicPermutation::RATE) { @@ -71,7 +70,7 @@ pub trait PlonkyPermutation: /// received; remaining state (if any) initialised with /// `T::default()`. To initialise remaining elements with a /// different value, instead of your original `iter` pass - /// `iter.chain(std::iter::repeat(F::from_canonical_u64(12345)))` + /// `iter.chain(core::iter::repeat(F::from_canonical_u64(12345)))` /// or similar. fn new>(iter: I) -> Self; @@ -103,7 +102,7 @@ pub fn compress>(x: HashOut, y: HashOut) debug_assert_eq!(y.elements.len(), NUM_HASH_OUT_ELTS); debug_assert!(P::RATE >= NUM_HASH_OUT_ELTS); - let mut perm = P::new(repeat(F::ZERO)); + let mut perm = P::new(core::iter::repeat(F::ZERO)); perm.set_from_slice(&x.elements, 0); perm.set_from_slice(&y.elements, NUM_HASH_OUT_ELTS); @@ -120,7 +119,7 @@ pub fn hash_n_to_m_no_pad>( inputs: &[F], num_outputs: usize, ) -> Vec { - let mut perm = P::new(repeat(F::ZERO)); + let mut perm = P::new(core::iter::repeat(F::ZERO)); // Absorb all input chunks. for input_chunk in inputs.chunks(P::RATE) { diff --git a/plonky2/src/hash/keccak.rs b/plonky2/src/hash/keccak.rs index 43b02db42c..281220f309 100644 --- a/plonky2/src/hash/keccak.rs +++ b/plonky2/src/hash/keccak.rs @@ -1,6 +1,5 @@ use alloc::vec; use alloc::vec::Vec; -use core::iter; use core::mem::size_of; use itertools::Itertools; @@ -68,7 +67,7 @@ impl PlonkyPermutation for KeccakPermutation { .copy_from_slice(&self.state[i].to_canonical_u64().to_le_bytes()); } - let hash_onion = iter::repeat_with(|| { + let hash_onion = core::iter::repeat_with(|| { let output = keccak(state_bytes.clone()).to_fixed_bytes(); state_bytes = output.to_vec(); output diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 14eb3a1cb3..c848f66ed0 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -132,7 +132,7 @@ impl, const D: usize> CircuitBuilder { perm_inputs.set_from_slice(&state.elements, 0); perm_inputs.set_from_slice(&sibling.elements, NUM_HASH_OUT_ELTS); // Ensure the rest of the state, if any, is zero: - perm_inputs.set_from_iter(std::iter::repeat(zero), 2 * NUM_HASH_OUT_ELTS); + perm_inputs.set_from_iter(core::iter::repeat(zero), 2 * NUM_HASH_OUT_ELTS); let perm_outs = self.permute_swapped::(perm_inputs, bit); let hash_outs = perm_outs.squeeze()[0..NUM_HASH_OUT_ELTS] .try_into() diff --git a/plonky2/src/hash/poseidon.rs b/plonky2/src/hash/poseidon.rs index e6e4116ef9..e50572b7ae 100644 --- a/plonky2/src/hash/poseidon.rs +++ b/plonky2/src/hash/poseidon.rs @@ -3,7 +3,7 @@ use alloc::vec; use alloc::vec::Vec; -use std::fmt::Debug; +use core::fmt::Debug; use plonky2_field::packed::PackedField; use unroll::unroll_for_loops; diff --git a/plonky2/src/iop/challenger.rs b/plonky2/src/iop/challenger.rs index 9df49996c5..d5de2831a3 100644 --- a/plonky2/src/iop/challenger.rs +++ b/plonky2/src/iop/challenger.rs @@ -30,7 +30,7 @@ pub struct Challenger> { impl> Challenger { pub fn new() -> Challenger { Challenger { - sponge_state: H::Permutation::new(std::iter::repeat(F::ZERO)), + sponge_state: H::Permutation::new(core::iter::repeat(F::ZERO)), input_buffer: Vec::with_capacity(H::Permutation::RATE), output_buffer: Vec::with_capacity(H::Permutation::RATE), } @@ -175,7 +175,7 @@ impl, H: AlgebraicHasher, const D: usize> pub fn new(builder: &mut CircuitBuilder) -> Self { let zero = builder.zero(); Self { - sponge_state: H::AlgebraicPermutation::new(std::iter::repeat(zero)), + sponge_state: H::AlgebraicPermutation::new(core::iter::repeat(zero)), input_buffer: Vec::new(), output_buffer: Vec::new(), __: PhantomData, diff --git a/plonky2/src/iop/generator.rs b/plonky2/src/iop/generator.rs index 22d0fe0792..1704b34795 100644 --- a/plonky2/src/iop/generator.rs +++ b/plonky2/src/iop/generator.rs @@ -1,3 +1,5 @@ +use alloc::boxed::Box; +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; use core::fmt::Debug; diff --git a/plonky2/src/lib.rs b/plonky2/src/lib.rs index c2913023f5..b02fc21bfb 100644 --- a/plonky2/src/lib.rs +++ b/plonky2/src/lib.rs @@ -2,7 +2,7 @@ #![allow(clippy::needless_range_loop)] #![cfg_attr(not(feature = "std"), no_std)] -extern crate alloc; +pub extern crate alloc; #[doc(inline)] pub use plonky2_field as field; diff --git a/plonky2/src/recursion/cyclic_recursion.rs b/plonky2/src/recursion/cyclic_recursion.rs index 4d5fc60250..60d5342a90 100644 --- a/plonky2/src/recursion/cyclic_recursion.rs +++ b/plonky2/src/recursion/cyclic_recursion.rs @@ -1,5 +1,7 @@ #![allow(clippy::int_plus_one)] // Makes more sense for some inequalities below. +use alloc::vec::Vec; + use anyhow::{ensure, Result}; use crate::field::extension::Extendable; diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index 620c979f4b..ee73105acc 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -1,3 +1,4 @@ +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; diff --git a/plonky2/src/util/serialization/gate_serialization.rs b/plonky2/src/util/serialization/gate_serialization.rs index 008e29c0bf..d858a05764 100644 --- a/plonky2/src/util/serialization/gate_serialization.rs +++ b/plonky2/src/util/serialization/gate_serialization.rs @@ -1,3 +1,7 @@ +//! A module to help with GateRef serialization + +use alloc::vec::Vec; + use plonky2_field::extension::Extendable; use crate::gates::gate::GateRef; @@ -44,7 +48,11 @@ macro_rules! get_gate_tag_impl { Ok(tag) } else)* { - log::log!(log::Level::Error, "attempted to serialize gate with id `{}` which is unsupported by this gate serializer", $gate.0.id()); + log::log!( + log::Level::Error, + "attempted to serialize gate with id `{}` which is unsupported by this gate serializer", + $gate.0.id() + ); Err($crate::util::serialization::IoError) } }}; @@ -68,7 +76,7 @@ macro_rules! impl_gate_serializer { fn write_gate( &self, - buf: &mut Vec, + buf: &mut $crate::alloc::vec::Vec, gate: &$crate::gates::gate::GateRef, common: &$crate::plonk::circuit_data::CommonCircuitData, ) -> $crate::util::serialization::IoResult<()> { diff --git a/plonky2/src/util/serialization/generator_serialization.rs b/plonky2/src/util/serialization/generator_serialization.rs index 6e00340090..19a22a77c3 100644 --- a/plonky2/src/util/serialization/generator_serialization.rs +++ b/plonky2/src/util/serialization/generator_serialization.rs @@ -1,5 +1,7 @@ //! A module to help with WitnessGeneratorRef serialization +use alloc::vec::Vec; + use plonky2_field::extension::Extendable; use crate::hash::hash_types::RichField; @@ -50,7 +52,11 @@ macro_rules! get_generator_tag_impl { Ok(tag) } else)* { - log::log!(log::Level::Error, "attempted to serialize generator with id {} which is unsupported by this generator serializer", $generator.0.id()); + log::log!( + log::Level::Error, + "attempted to serialize generator with id {} which is unsupported by this generator serializer", + $generator.0.id() + ); Err($crate::util::serialization::IoError) } }}; @@ -74,7 +80,7 @@ macro_rules! impl_generator_serializer { fn write_generator( &self, - buf: &mut Vec, + buf: &mut $crate::alloc::vec::Vec, generator: &$crate::iop::generator::WitnessGeneratorRef, common: &$crate::plonk::circuit_data::CommonCircuitData, ) -> $crate::util::serialization::IoResult<()> { diff --git a/plonky2/src/util/serialization/mod.rs b/plonky2/src/util/serialization/mod.rs index ca95b4a137..1861cba241 100644 --- a/plonky2/src/util/serialization/mod.rs +++ b/plonky2/src/util/serialization/mod.rs @@ -134,7 +134,7 @@ pub trait Read { /// Reads a `usize` value from `self`. #[inline] fn read_usize(&mut self) -> IoResult { - let mut buf = [0; std::mem::size_of::()]; + let mut buf = [0; core::mem::size_of::()]; self.read_exact(&mut buf)?; Ok(u64::from_le_bytes(buf) as usize) } diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index bd5d2f1916..18db561be2 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use core::iter::once; use anyhow::{ensure, Result};