From 7a018268428e925572a36f1a9b63047e6a645551 Mon Sep 17 00:00:00 2001 From: Marko Atanasievski Date: Wed, 26 Jun 2024 12:59:19 +0200 Subject: [PATCH 1/5] feat: add jerigon test workflow (#303) * feat: add jerigon workflow * fix: update 1 * fix: update 2 * fix: login * fix: docker compose * feat: add jerigon proof verification * fix: return github user to actor * fix: paths * fix: play nice, shutdown network after * fix: output execution to terminal for debug * fix: environment and script * fix: smaller proof * chore: add verification * fix: show smart contract deployment log * fix: name --- .github/workflows/ci.yml | 2 +- .github/workflows/jerigon.yml | 85 +++++++++++++++++++++++++++++++++++ zero_bin/tools/prove_rpc.sh | 76 ++++++++++++++++++++++--------- 3 files changed, 140 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/jerigon.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 628d15ad7..7c55c157d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: Continuous Integration on: push: - branches: [main] + branches: [develop, main] pull_request: branches: - "**" diff --git a/.github/workflows/jerigon.yml b/.github/workflows/jerigon.yml new file mode 100644 index 000000000..273eff91e --- /dev/null +++ b/.github/workflows/jerigon.yml @@ -0,0 +1,85 @@ +name: Jerigon Integration + +on: + push: + branches: [develop, main] + pull_request: + branches: + - "**" + workflow_dispatch: + branches: + - "**" + + +env: + CARGO_TERM_COLOR: always + REGISTRY: ghcr.io + +jobs: + test_jerigon_input_proving: + name: Test proof generation with jerigon input + runs-on: zero-ci + timeout-minutes: 40 + if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Checkout test-jerigon-network sources + uses: actions/checkout@v4 + with: + repository: 0xPolygonZero/jerigon-test-network + path: test-jerigon-network + + - name: Install nightly toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Run jerigon test network with docker compose + run: | + cd test-jerigon-network + docker-compose -f docker-compose.yml up -d + docker logs -f smart-contracts + echo "Jerigon network is up and running, ready for testing" + + - name: Rpc test with curl + run: | + curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id":83}' localhost:8545 + env: + RUST_LOG: info + + - name: Run prove blocks in test_only mode + run: | + cd zero_bin/tools + OUTPUT_TO_TERMINAL=true ./prove_rpc.sh 0x2 0x3 http://localhost:8546 jerigon true 0 0 test_only + echo "Proving blocks in test_only mode finished" + + + - name: Run prove blocks in real mode + run: | + cd zero_bin/tools + rm -rf proofs/* circuits/* ./proofs.json test.out verify.out leader.out + OUTPUT_TO_TERMINAL=true RUN_VERIFICATION=true ./prove_rpc.sh 0x4 0x5 http://localhost:8546 jerigon true + echo "Proving blocks in real mode finished" + + - name: Shut down network + run: | + cd test-jerigon-network + docker-compose -f docker-compose.yml down -v + + + diff --git a/zero_bin/tools/prove_rpc.sh b/zero_bin/tools/prove_rpc.sh index 43be954b5..971e3b41a 100755 --- a/zero_bin/tools/prove_rpc.sh +++ b/zero_bin/tools/prove_rpc.sh @@ -50,6 +50,10 @@ IGNORE_PREVIOUS_PROOFS=$5 BACKOFF=${6:-0} RETRIES=${7:-0} +# Sometimes we need to override file logging, e.g. in the CI run +OUTPUT_TO_TERMINAL="${OUTPUT_TO_TERMINAL:-false}" +# Only generate proof by default +RUN_VERIFICATION="${RUN_VERIFICATION:-false}" mkdir -p $PROOF_OUTPUT_DIR @@ -87,38 +91,66 @@ fi if [[ $8 == "test_only" ]]; then # test only run echo "Proving blocks ${BLOCK_INTERVAL} in a test_only mode now... (Total: ${TOT_BLOCKS})" - cargo r --release --features test_only --bin leader -- --runtime in-memory --load-strategy on-demand rpc --rpc-type "$NODE_RPC_TYPE" --rpc-url "$NODE_RPC_URL" --block-interval $BLOCK_INTERVAL --proof-output-dir $PROOF_OUTPUT_DIR $PREV_PROOF_EXTRA_ARG --backoff "$BACKOFF" --max-retries "$RETRIES" > $OUT_LOG_PATH 2>&1 - if grep -q 'All proof witnesses have been generated successfully.' $OUT_LOG_PATH; then - echo -e "Success - Note this was just a test, not a proof" - # Remove the log on success if we don't want to keep it. - if [ $ALWAYS_WRITE_LOGS -ne 1 ]; then - rm $OUT_LOG_PATH - fi - exit + command='cargo r --release --features test_only --bin leader -- --runtime in-memory --load-strategy on-demand rpc --rpc-type "$NODE_RPC_TYPE" --rpc-url "$NODE_RPC_URL" --block-interval $BLOCK_INTERVAL --proof-output-dir $PROOF_OUTPUT_DIR $PREV_PROOF_EXTRA_ARG --backoff "$BACKOFF" --max-retries "$RETRIES" ' + if [ "$OUTPUT_TO_TERMINAL" = true ]; then + eval $command + retVal=$? + echo -e "Proof witness generation finished with result: $retVal" + exit $retVal else - echo "Failed to create proof witnesses. See ${OUT_LOG_PATH} for more details." - exit 1 + eval $command > $OUT_LOG_PATH 2>&1 + if grep -q 'All proof witnesses have been generated successfully.' $OUT_LOG_PATH; then + echo -e "Success - Note this was just a test, not a proof" + # Remove the log on success if we don't want to keep it. + if [ $ALWAYS_WRITE_LOGS -ne 1 ]; then + rm $OUT_LOG_PATH + fi + exit + else + echo "Failed to create proof witnesses. See ${OUT_LOG_PATH} for more details." + exit 1 + fi fi else # normal run echo "Proving blocks ${BLOCK_INTERVAL} now... (Total: ${TOT_BLOCKS})" - cargo r --release --bin leader -- --runtime in-memory --load-strategy on-demand rpc --rpc-type "$NODE_RPC_TYPE" --rpc-url "$3" --block-interval $BLOCK_INTERVAL --proof-output-dir $PROOF_OUTPUT_DIR $PREV_PROOF_EXTRA_ARG --backoff "$BACKOFF" --max-retries "$RETRIES" > $OUT_LOG_PATH 2>&1 - - retVal=$? - if [ $retVal -ne 0 ]; then - # Some error occurred. - echo "Block ${i} errored. See ${OUT_LOG_PATH} for more details." - exit $retVal + command='cargo r --release --bin leader -- --runtime in-memory --load-strategy on-demand rpc --rpc-type "$NODE_RPC_TYPE" --rpc-url "$3" --block-interval $BLOCK_INTERVAL --proof-output-dir $PROOF_OUTPUT_DIR $PREV_PROOF_EXTRA_ARG --backoff "$BACKOFF" --max-retries "$RETRIES" ' + if [ "$OUTPUT_TO_TERMINAL" = true ]; then + eval $command + echo -e "Proof generation finished with result: $?" else - # Remove the log on success if we don't want to keep it. - if [ $ALWAYS_WRITE_LOGS -ne 1 ]; then - rm $OUT_LOG_PATH + eval $command > $OUT_LOG_PATH 2>&1 + retVal=$? + if [ $retVal -ne 0 ]; then + # Some error occurred. + echo "Block ${i} errored. See ${OUT_LOG_PATH} for more details." + exit $retVal + else + # Remove the log on success if we don't want to keep it. + if [ $ALWAYS_WRITE_LOGS -ne 1 ]; then + rm $OUT_LOG_PATH + fi fi + echo "Successfully generated ${TOT_BLOCKS} proofs!" fi - - echo "Successfully generated ${TOT_BLOCKS} proofs!" fi +# If we're running the verification, we'll do it here. +if [ "$RUN_VERIFICATION" = true ]; then + echo "Running the verification" + proof_file_name=$PROOF_OUTPUT_DIR/b$END_BLOCK.zkproof + echo "Verifying the proof of the latest block in the interval:" $proof_file_name + echo [ > $PROOF_OUTPUT_DIR/proofs.json && cat $proof_file_name >> $PROOF_OUTPUT_DIR/proofs.json && echo ] >> $PROOF_OUTPUT_DIR/proofs.json + cargo r --release --bin verifier -- -f $PROOF_OUTPUT_DIR/proofs.json > $PROOF_OUTPUT_DIR/verify.out 2>&1 + if grep -q 'All proofs verified successfully!' $PROOF_OUTPUT_DIR/verify.out; then + echo "All proofs verified successfully!"; + else + echo "there was an issue with proof verification"; + exit 1 + fi +else + echo "Skipping verification..." +fi From 4e80f849014341010af16829982b0b521f8dbedf Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:38:24 -0400 Subject: [PATCH 2/5] fix: do not add selfdestruct journal entry for empty accounts (#328) * fix: Do not add journal entry for skipped accounts * Tweak to check with is_non_existent * Reduce overhead --- .../src/cpu/kernel/asm/core/terminate.asm | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm b/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm index 8572f34f2..778cd15c2 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm @@ -110,9 +110,36 @@ global sys_selfdestruct: %add_eth sys_selfdestruct_journal_add: + // stack: address, recipient, balance, kexit_info + DUP3 ISZERO + + // If balance is 0, we didn't perform any transfer. Hence, the recipient + // may not exist, and we need to verify this before adding a new journal entry. + + // stack: balance=0, address, recipient, balance, kexit_info + %jumpi(skip_journal_entry_if_nonexistent) + +sys_selfdestruct_journal_add_after_check: // stack: address, recipient, balance, kexit_info %journal_add_account_destroyed + %jump(sys_selfdestruct_exit) + +skip_journal_entry_if_nonexistent: + // stack: address, recipient, balance, kexit_info + DUP2 %is_non_existent + + // If the account doesn't exist, there is no need to add a journal entry. + // stack: recipient_is_non_existent, address, recipient, balance, kexit_info + %jumpi(skip_journal_entry) + + // stack: address, recipient, balance, kexit_info + %jump(sys_selfdestruct_journal_add_after_check) + +skip_journal_entry: + // stack: address, recipient, balance, kexit_info + %pop3 +sys_selfdestruct_exit: // stack: kexit_info %leftover_gas // stack: leftover_gas From a3bd131cddbca9cf1c27d70cb02421f5d1c94624 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:36:02 -0400 Subject: [PATCH 3/5] ci: add PR check job (#332) --- .github/pr_checking.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/pr_checking.yml diff --git a/.github/pr_checking.yml b/.github/pr_checking.yml new file mode 100644 index 000000000..5bed881a3 --- /dev/null +++ b/.github/pr_checking.yml @@ -0,0 +1,22 @@ +name: PR check + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + title: + name: Validate PR + runs-on: ubuntu-latest + if: ${{ + github.event.pull_request.author_association != 'CONTRIBUTOR' && + github.event.pull_request.author_association != 'MEMBER' && + ( + contains(fromJSON(secrets.RESTRICTED_KEYWORDS), github.event.pull_request.title) || + contains(fromJSON(secrets.RESTRICTED_KEYWORDS), github.event.pull_request.description + ) }} + steps: + - run: gh pr close From 28ae2c752d44717e2c92cd4117a321b804ed2385 Mon Sep 17 00:00:00 2001 From: Linda Guiga <101227802+LindaGuiga@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:20:46 +0100 Subject: [PATCH 4/5] Constrain FP254 operations and SUBMOD to be kernel-only (#333) * Constraint FP254 operations and SUBMOD to be kernel-only * Apply comments --- evm_arithmetization/src/cpu/decode.rs | 36 +++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/evm_arithmetization/src/cpu/decode.rs b/evm_arithmetization/src/cpu/decode.rs index 540a4d66d..b54a0c397 100644 --- a/evm_arithmetization/src/cpu/decode.rs +++ b/evm_arithmetization/src/cpu/decode.rs @@ -148,9 +148,16 @@ pub(crate) fn eval_packed_generic( .sum() }; + // Manually check that the fp254 operations are kernel-only instructions. + yield_constr.constraint((kernel_mode - P::ONES) * lv.op.fp254_op); + + // Manually check that SUBMOD is a kernel-only instruction. SUBMOD is + // differentiated by its second bit set to 1. + yield_constr.constraint(lv.op.ternary_op * lv.opcode_bits[1] * (kernel_mode - P::ONES)); + // Manually check lv.op.m_op_constr let opcode = opcode_high_bits(8); - yield_constr.constraint((P::ONES - kernel_mode) * lv.op.m_op_general); + yield_constr.constraint((kernel_mode - P::ONES) * lv.op.m_op_general); let m_op_constr = (opcode - P::Scalar::from_canonical_usize(0xfb_usize)) * (opcode - P::Scalar::from_canonical_usize(0xfc_usize)) @@ -161,7 +168,7 @@ pub(crate) fn eval_packed_generic( // KECCAK_GENERAL is a kernel-only instruction, but not JUMPDEST. // JUMPDEST is differentiated from KECCAK_GENERAL by its second bit set to 1. yield_constr.constraint( - (P::ONES - kernel_mode) * lv.op.jumpdest_keccak_general * (P::ONES - lv.opcode_bits[1]), + (kernel_mode - P::ONES) * lv.op.jumpdest_keccak_general * (P::ONES - lv.opcode_bits[1]), ); // Check the JUMPDEST and KERNEL_GENERAL opcodes. @@ -190,7 +197,7 @@ pub(crate) fn eval_packed_generic( // Manually check lv.op.m_op_32bytes. // Both are kernel-only. - yield_constr.constraint((P::ONES - kernel_mode) * lv.op.m_op_32bytes); + yield_constr.constraint((kernel_mode - P::ONES) * lv.op.m_op_32bytes); // Check the MSTORE_32BYTES and MLOAD-32BYTES opcodes. let opcode_high_three = opcode_high_bits(3); @@ -206,7 +213,7 @@ pub(crate) fn eval_packed_generic( * lv.op.push_prover_input; yield_constr.constraint(push_prover_input_constr); let prover_input_constr = - lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES) * (P::ONES - kernel_mode); + lv.op.push_prover_input * (lv.opcode_bits[5] - P::ONES) * (kernel_mode - P::ONES); yield_constr.constraint(prover_input_constr); } @@ -304,15 +311,23 @@ pub(crate) fn eval_ext_circuit, const D: usize>( yield_constr.constraint(builder, constr); } + // Manually check that the fp254 operations are kernel-only instructions. + let constr = builder.mul_sub_extension(kernel_mode, lv.op.fp254_op, lv.op.fp254_op); + yield_constr.constraint(builder, constr); + + // Manually check that SUBMOD is a kernel-only instruction. SUBMOD is + // differentiated by its second bit set to 1. + let submod_op = builder.mul_extension(lv.op.ternary_op, lv.opcode_bits[1]); + let constr = builder.mul_sub_extension(kernel_mode, submod_op, submod_op); + yield_constr.constraint(builder, constr); + // Manually check lv.op.m_op_constr let opcode = opcode_high_bits_circuit(builder, lv, 8); let mload_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0xfb_usize)); let mstore_opcode = builder.constant_extension(F::Extension::from_canonical_usize(0xfc_usize)); - let one_extension = builder.constant_extension(F::Extension::ONE); - let is_not_kernel_mode = builder.sub_extension(one_extension, kernel_mode); - let constr = builder.mul_extension(is_not_kernel_mode, lv.op.m_op_general); + let constr = builder.mul_sub_extension(kernel_mode, lv.op.m_op_general, lv.op.m_op_general); yield_constr.constraint(builder, constr); let mload_constr = builder.sub_extension(opcode, mload_opcode); @@ -334,7 +349,8 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let mut kernel_general_filter = builder.sub_extension(one, lv.opcode_bits[1]); kernel_general_filter = builder.mul_extension(lv.op.jumpdest_keccak_general, kernel_general_filter); - let constr = builder.mul_extension(is_not_kernel_mode, kernel_general_filter); + let constr = + builder.mul_sub_extension(kernel_mode, kernel_general_filter, kernel_general_filter); yield_constr.constraint(builder, constr); // Check the JUMPDEST and KERNEL_GENERAL opcodes. @@ -375,7 +391,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( // Manually check lv.op.m_op_32bytes. // Both are kernel-only. - let constr = builder.mul_extension(is_not_kernel_mode, lv.op.m_op_32bytes); + let constr = builder.mul_sub_extension(kernel_mode, lv.op.m_op_32bytes, lv.op.m_op_32bytes); yield_constr.constraint(builder, constr); // Check the MSTORE_32BYTES and MLOAD-32BYTES opcodes. @@ -407,6 +423,6 @@ pub(crate) fn eval_ext_circuit, const D: usize>( lv.opcode_bits[5], lv.op.push_prover_input, ); - let constr = builder.mul_extension(prover_input_filter, is_not_kernel_mode); + let constr = builder.mul_sub_extension(kernel_mode, prover_input_filter, prover_input_filter); yield_constr.constraint(builder, constr); } From 8154e1512d0de7b05a541239c618b0a37a0d5f6f Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 26 Jun 2024 15:05:23 -0400 Subject: [PATCH 5/5] fix: Move PR check file into proper folder --- .github/{ => workflows}/pr_checking.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/pr_checking.yml (100%) diff --git a/.github/pr_checking.yml b/.github/workflows/pr_checking.yml similarity index 100% rename from .github/pr_checking.yml rename to .github/workflows/pr_checking.yml