Repository: alpenlabs/strata
Pull request #707: STR-1081-EE Proof: Exclude Stateless Precompiles Accounts in state root construction
Author: login: MdTeach
Created: 2025-03-04T04:45:09Z | Merged: 2025-03-04T10:11:55Z

## Description
*  Exclude Stateless Precompiles Accounts in state root construction
*  Functional tests using using EE Precompiles
<!--
Provide a brief summary of the changes and the motivation behind them.
-->

### Type of Change

<!--
Select the type of change your PR introduces (put an `x` in all that apply):
-->

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature/Enhancement (non-breaking change which adds functionality or enhances an existing one)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [x] Refactor
- [ ] New or updated tests
- [ ] Dependency Update

## Notes to Reviewers

<!--
Anything in particular you want to note that will help reviewers fulfill their role
in reviewing this PR?
-->

## Checklist

<!--
Ensure all the following are checked:
-->

- [ ] I have performed a self-review of my code.
- [ ] I have commented my code where necessary.
- [ ] I have updated the documentation if needed.
- [ ] My changes do not introduce new warnings.
- [ ] I have added (where necessary) tests that prove my changes are effective or that my feature works.
- [ ] New and existing tests pass with my changes.

## Related Issues

<!--
Link any related issues (e.g., `closes #123`, `fixes #456`).
-->


Linked issue #123: Sync manager cannot start because l1_reader starts after sync tasks are spawned and sync tasks don't get any genesis L1 block
This is kind of a deadlock situation.

l1_reader starts after sync manager completes starting and sync manager is expecting genesis block inserted by l1_reader.

Related: #141 
#141 should close this

Diff:
diff --git a/crates/proof-impl/evm-ee-stf/src/processor.rs b/crates/proof-impl/evm-ee-stf/src/processor.rs
index 20412ee59e..366a9515dc 100644
--- a/crates/proof-impl/evm-ee-stf/src/processor.rs
+++ b/crates/proof-impl/evm-ee-stf/src/processor.rs
@@ -29,6 +29,7 @@ use reth_primitives_traits::{constants::MINIMUM_GAS_LIMIT, SignedTransaction};
 use revm::{
     db::{AccountState, InMemoryDB},
     interpreter::Host,
+    precompile::{PrecompileSpecId, Precompiles},
     primitives::{SpecId, TransactTo, TxEnv},
     Database, DatabaseCommit, Evm,
 };
@@ -43,7 +44,7 @@ use strata_reth_evm::{
 
 use crate::{
     mpt::{keccak, RlpBytes, StateAccount},
-    EvmBlockStfInput,
+    EvmBlockStfInput, EVM_CONFIG,
 };
 
 /// The divisor for the gas limit bound.
@@ -273,6 +274,7 @@ impl EvmProcessor<InMemoryDB> {
     /// Process all state changes and finalize the header's state root.
     pub fn finalize(&mut self) {
         let db = self.db.take().expect("DB not initialized");
+        let precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(EVM_CONFIG.spec_id));
 
         let mut state_trie = mem::take(&mut self.input.pre_state_trie);
         for (address, account) in &db.accounts {
@@ -281,6 +283,11 @@ impl EvmProcessor<InMemoryDB> {
                 continue;
             }
 
+            // Ignore Ethereum stateless precompiles
+            if precompiles.contains(address) {
+                continue;
+            }
+
             // Ignore strata statless precompiles
             if address == &BRIDGEOUT_ADDRESS || address == &SCHNORR_ADDRESS {
                 continue;
diff --git a/functional-tests/load/reth/contracts/PrecompileTestContract.sol b/functional-tests/load/reth/contracts/PrecompileTestContract.sol
new file mode 100644
index 0000000000..d8ac7ea71d
--- /dev/null
+++ b/functional-tests/load/reth/contracts/PrecompileTestContract.sol
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: Unlicense
+
+pragma solidity ^0.8.0;
+
+contract PrecompileTestContract {
+    event GasUsed(string testName, uint256 gasUsed);
+
+    function testPrecompiles() public {
+        executePrecompile("testShaPrecompile", testShaPrecompile);
+        executePrecompile("testEcRecoverPrecompile", testEcRecoverPrecompile);
+        executePrecompile("testRIPEMD160Precompile", testRIPEMD160Precompile);
+        executePrecompile("testIdentityPrecompile", testIdentityPrecompile);
+        executePrecompile("testModExpPrecompile", testModExpPrecompile);
+        executePrecompile("testEcAddPrecompile", testEcAddPrecompile);
+        executePrecompile("testEcMulPrecompile", testEcMulPrecompile);
+        executePrecompile("testEcPairingPrecompile", testEcPairingPrecompile);
+    }
+
+    function executePrecompile(
+        string memory precompile,
+        function() internal view f
+    ) internal {
+        uint256 gasBefore = gasleft();
+        f();
+        uint256 gasAfter = gasleft();
+        uint256 gasUsed = gasBefore - gasAfter;
+        emit GasUsed(precompile, gasUsed);
+    }
+
+    function testEcRecoverPrecompile() internal pure {
+        // Message hash that was signed
+        bytes32 messageHash = 0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3;
+
+        // Recovery value + 27
+        uint8 v = 28;
+
+        // ECDSA signature components
+        bytes32 r = 0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608;
+        bytes32 s = 0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada;
+
+        // Recover the signer's address using ecrecover
+        address signer = ecrecover(messageHash, v, r, s);
+
+        // Assert that the recovered signer is the expected signer
+        address expectedSigner = 0x7156526fbD7a3C72969B54f64e42c10fbb768C8a;
+        require(signer == expectedSigner, "Invalid signer");
+    }
+
+    function testShaPrecompile() internal pure {
+        bytes memory message = hex"FF";
+        bytes32 hash = sha256(message);
+        bytes32 expectedHash = 0xa8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89;
+        require(hash == expectedHash, "Invalid hash");
+    }
+
+    function testRIPEMD160Precompile() internal pure {
+        bytes memory message = hex"FF";
+        bytes20 hash = ripemd160(message);
+        bytes20 expectedHash = bytes20(
+            0x2c0C45D3ecab80fE060E5f1d7057cd2F8De5e557
+        );
+        require(hash == expectedHash, "Invalid hash");
+    }
+
+    function testIdentityPrecompile() internal view {
+        bytes memory message = hex"FF";
+        (bool success, bytes memory output) = address(0x04).staticcall(message);
+        require(success, "Identity precompile failed");
+        require(keccak256(output) == keccak256(message), "Invalid output");
+    }
+
+    function testModExpPrecompile() internal view {
+        uint256 base = 8;
+        uint256 exp = 9;
+        uint256 mod = 10;
+        uint256 expected = 8;
+
+        bytes memory precompileData = abi.encode(32, 32, 32, base, exp, mod);
+        (bool success, bytes memory output) = address(0x05).staticcall(
+            precompileData
+        );
+
+        require(success, "ModExp precompile failed");
+        require(abi.decode(output, (uint256)) == expected, "Invalid output");
+    }
+
+    function testEcAddPrecompile() internal view {
+        uint256 x1 = 1;
+        uint256 y1 = 2;
+        uint256 x2 = 1;
+        uint256 y2 = 2;
+        bytes memory precompileData = abi.encode(x1, y1, x2, y2);
+
+        bytes32 expectedX3 = 0x030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3;
+        bytes32 expectedY3 = 0x15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4;
+
+        (bool success, bytes memory output) = address(0x06).staticcall(
+            precompileData
+        );
+        (bytes32 x3, bytes32 y3) = abi.decode(output, (bytes32, bytes32));
+
+        require(success, "EcAdd precompile failed");
+        require(x3 == expectedX3, "Invalid x3");
+        require(y3 == expectedY3, "Invalid y3");
+    }
+
+    function testEcMulPrecompile() internal view {
+        uint256 x1 = 1;
+        uint256 y1 = 2;
+        uint256 scalar = 2;
+        bytes memory precompileData = abi.encode(x1, y1, scalar);
+        (bool success, bytes memory output) = address(0x07).staticcall(
+            precompileData
+        );
+
+        bytes32 expectedX3 = 0x030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3;
+        bytes32 expectedY3 = 0x15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4;
+
+        (bytes32 x3, bytes32 y3) = abi.decode(output, (bytes32, bytes32));
+
+        require(success, "EcMul precompile failed");
+        require(x3 == expectedX3, "Invalid x3");
+        require(y3 == expectedY3, "Invalid y3");
+    }
+
+    function testEcPairingPrecompile() internal view {
+        uint256 aG1_x = 0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da;
+        uint256 aG1_y = 0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6;
+
+        uint256 bG2_x1 = 0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc;
+        uint256 bG2_x2 = 0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9;
+        uint256 bG2_y1 = 0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90;
+        uint256 bG2_y2 = 0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e;
+
+        uint256 cG1_x = 0x0000000000000000000000000000000000000000000000000000000000000001;
+        uint256 cG1_y = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45;
+
+        uint256 dG2_x1 = 0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4;
+        uint256 dG2_x2 = 0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7;
+        uint256 dG2_y1 = 0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2;
+        uint256 dG2_y2 = 0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc;
+
+        bytes memory precompileData = abi.encode(
+            aG1_x,
+            aG1_y,
+            bG2_x1,
+            bG2_x2,
+            bG2_y1,
+            bG2_y2,
+            cG1_x,
+            cG1_y,
+            dG2_x1,
+            dG2_x2,
+            dG2_y1,
+            dG2_y2
+        );
+
+        (bool success, bytes memory output) = address(0x08).staticcall(
+            precompileData
+        );
+        require(success, "EcPairing precompile failed");
+        bool isPairingValid = abi.decode(output, (bool));
+        require(isPairingValid, "EC pairing check failed");
+    }
+}
diff --git a/functional-tests/load/reth/transaction.py b/functional-tests/load/reth/transaction.py
index 4a0ec900f7..5bf3098dca 100644
--- a/functional-tests/load/reth/transaction.py
+++ b/functional-tests/load/reth/transaction.py
@@ -174,13 +174,15 @@ class SmartContracts(TransactionSender):
 
     _smart_contracts_storage = dict()
 
-    def _extract_compiled_contract(self, compiled_sol, contract_name: str):
+    @staticmethod
+    def extract_compiled_contract(compiled_sol, contract_name: str):
         for ct_id, ct_interface in compiled_sol.items():
             if ct_id.endswith(contract_name):
                 return ct_interface["abi"], ct_interface["bin"]
         return None
 
-    def _compile_contract(self, filename, contract_name=None):
+    @staticmethod
+    def compile_contract(filename, contract_name=None):
         if contract_name is None:
             contract_name = filename.split(".")[0]
 
@@ -191,11 +193,11 @@ def _compile_contract(self, filename, contract_name=None):
             output_values=["abi", "bin"],
             solc_version=SmartContracts.SOL_VERSION,
         )
-        return self._extract_compiled_contract(compiled_sol, contract_name)
+        return SmartContracts.extract_compiled_contract(compiled_sol, contract_name)
 
     @tx_caller("DEPLOYING CONTRACT [3]")
     def deploy_contract(self, contract_path, contract_name, contract_id, *args):
-        abi, bytecode = self._compile_contract(contract_path, contract_name)
+        abi, bytecode = SmartContracts.compile_contract(contract_path, contract_name)
         contract = self.w3.eth.contract(abi=abi, bytecode=bytecode)
 
         tx: Tx = TransactionBuilder.new_with_gas(5_000_000)
diff --git a/functional-tests/tests/prover/prover_el_precompiles.py b/functional-tests/tests/prover/prover_el_precompiles.py
new file mode 100644
index 0000000000..be2345938a
--- /dev/null
+++ b/functional-tests/tests/prover/prover_el_precompiles.py
@@ -0,0 +1,69 @@
+import flexitest
+from solcx import install_solc, set_solc_version
+from web3 import Web3
+
+from envs import testenv
+from load.reth.transaction import SmartContracts
+from utils import (
+    el_slot_to_block_id,
+    wait_for_proof_with_time_out,
+    wait_until_with_value,
+)
+
+
+@flexitest.register
+class ElShaPrecompileTest(testenv.StrataTester):
+    def __init__(self, ctx: flexitest.InitContext):
+        install_solc(version="0.8.16")
+        set_solc_version("0.8.16")
+        ctx.set_env("prover")
+
+    def main(self, ctx: flexitest.RunContext):
+        reth = ctx.get_service("reth")
+        reth_rpc = reth.create_rpc()
+
+        prover_client = ctx.get_service("prover_client")
+        prover_client_rpc = prover_client.create_rpc()
+
+        web3: Web3 = reth.create_web3()
+        web3.eth.default_account = web3.address
+
+        # Deploy the contract
+        abi, bytecode = SmartContracts.compile_contract(
+            "PrecompileTestContract.sol", "PrecompileTestContract"
+        )
+        contract = web3.eth.contract(abi=abi, bytecode=bytecode)
+        tx_hash = contract.constructor().transact()
+        tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=30)
+
+        # Call the contract function
+        contract_instance = web3.eth.contract(abi=abi, address=tx_receipt.contractAddress)
+        tx_hash = contract_instance.functions.testPrecompiles().transact()
+        tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=30)
+
+        # Prove the corresponding EE block
+        ee_prover_params = {
+            "start_block": tx_receipt["blockNumber"] - 1,
+            "end_block": tx_receipt["blockNumber"] + 1,
+        }
+
+        # Wait until the end EE block is generated.
+        wait_until_with_value(
+            lambda: web3.eth.get_block("latest")["number"],
+            lambda height: height >= ee_prover_params["end_block"],
+            error_with="EE blocks not generated",
+        )
+
+        start_block_id = el_slot_to_block_id(reth_rpc, ee_prover_params["start_block"])
+        end_block_id = el_slot_to_block_id(reth_rpc, ee_prover_params["end_block"])
+
+        task_ids = prover_client_rpc.dev_strata_proveElBlocks((start_block_id, end_block_id))
+        self.debug(f"Prover task IDs received: {task_ids}")
+
+        if not task_ids:
+            raise Exception("No task IDs received from prover_client_rpc")
+
+        task_id = task_ids[0]
+        self.debug(f"Using task ID: {task_id}")
+
+        wait_for_proof_with_time_out(prover_client_rpc, task_id, time_out=30)


Reviews:
user: login: voidash
body: Bundling all the precompiles in a single function makes it sequential and single failure near top means the precompiles below it are never checked even if they would pass. 
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T07:44:26Z
user: login: voidash
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T08:20:46Z
user: login: prajwolrg
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T10:05:35Z

Review comments:
diff_hunk: @@ -272,7 +273,8 @@ where
 impl EvmProcessor<InMemoryDB> {
     /// Process all state changes and finalize the header's state root.
     pub fn finalize(&mut self) {
-        let db = self.db.take().expect("DB not initialized");
+        let db = self.db.take().expect("DB not initiaed");
path: crates/proof-impl/evm-ee-stf/src/processor.rs
user: login: voidash
body: initiated
created_at: 2025-03-04T07:25:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -191,11 +193,11 @@ def _compile_contract(self, filename, contract_name=None):
             output_values=["abi", "bin"],
             solc_version=SmartContracts.SOL_VERSION,
         )
-        return self._extract_compiled_contract(compiled_sol, contract_name)
+        return SmartContracts.extract_compiled_contract(compiled_sol, contract_name)
 
     @tx_caller("DEPLOYING CONTRACT [3]")
     def deploy_contract(self, contract_path, contract_name, contract_id, *args):
-        abi, bytecode = self._compile_contract(contract_path, contract_name)
+        abi, bytecode = self.compile_contract(contract_path, contract_name)
path: functional-tests/load/reth/transaction.py
user: login: voidash
body: `self.compile_contract` is misleading as this is a `static method`
created_at: 2025-03-04T07:41:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,69 @@
+import flexitest
+from solcx import install_solc, set_solc_version
+from web3 import Web3
+
+from envs import testenv
+from load.reth.transaction import SmartContracts
+from utils import (
+    el_slot_to_block_id,
+    wait_for_proof_with_time_out,
+    wait_until_with_value,
+)
+
+
+@flexitest.register
+class ElShaPrecompileTest(testenv.StrataTester):
+    def __init__(self, ctx: flexitest.InitContext):
+        install_solc(version="0.8.16")
+        set_solc_version("0.8.16")
+        ctx.set_env("prover")
+
+    def main(self, ctx: flexitest.RunContext):
+        reth = ctx.get_service("reth")
+        reth_rpc = reth.create_rpc()
+
+        prover_client = ctx.get_service("prover_client")
+        prover_client_rpc = prover_client.create_rpc()
+
+        web3: Web3 = reth.create_web3()
+        web3.eth.default_account = web3.address
+
+        # Deploy the contract
+        abi, bytecode = SmartContracts.compile_contract(
+            "PrecompileTestContract.sol", "PrecompileTestContract"
+        )
+        contract = web3.eth.contract(abi=abi, bytecode=bytecode)
+        tx_hash = contract.constructor().transact()
+        tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=30)
+
+        # Call the contract function
+        contract_instance = web3.eth.contract(abi=abi, address=tx_receipt.contractAddress)
+        tx_hash = contract_instance.functions.testPrecompiles().transact()
path: functional-tests/tests/prover/prover_el_precompiles.py
user: login: voidash
body: `contract_instance.functions.testSha` and so on... Please look at the comment
https://github.com/alpenlabs/strata/pull/707#discussion_r1978804663
created_at: 2025-03-04T07:41:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,165 @@
+// SPDX-License-Identifier: Unlicense
+
+pragma solidity ^0.8.0;
+
+contract PrecompileTestContract {
+    event GasUsed(string testName, uint256 gasUsed);
+
+    function testPrecompiles() public {
+        executePrecompile("testShaPrecompile", testShaPrecompile);
+        executePrecompile("testEcRecoverPrecompile", testEcRecoverPrecompile);
+        executePrecompile("testRIPEMD160Precompile", testRIPEMD160Precompile);
+        executePrecompile("testIdentityPrecompile", testIdentityPrecompile);
+        executePrecompile("testModExpPrecompile", testModExpPrecompile);
+        executePrecompile("testEcAddPrecompile", testEcAddPrecompile);
+        executePrecompile("testEcMulPrecompile", testEcMulPrecompile);
+        executePrecompile("testEcPairingPrecompile", testEcPairingPrecompile);
path: functional-tests/load/reth/contracts/PrecompileTestContract.sol
user: login: voidash
body: Since all of this is sequential and any of the first precompiles fail, then the whole `testPrecompiles()` fail. I think it would be better to create something like
```
function testShaPrecompile() public {
        executePrecompile("testShaPrecompile", _shaPrecompile);
    }

    function testEcRecoverPrecompile() public {
        executePrecompile("testEcRecoverPrecompile", _ecRecoverPrecompile);
    }
  ```

created_at: 2025-03-04T07:41:51Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-04T04:51:43Z
body: ## [Codecov](https://app.codecov.io/gh/alpenlabs/strata/pull/707?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs) Report
Attention: Patch coverage is `83.33333%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 54.97%. Comparing base [(`de3a4ff`)](https://app.codecov.io/gh/alpenlabs/strata/commit/de3a4ffafe8b679f73de1cfd23b0cebae81549f6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs) to head [(`b6e6292`)](https://app.codecov.io/gh/alpenlabs/strata/commit/b6e6292b6ccb4b4650cc217916239ba6457fdeb1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/alpenlabs/strata/pull/707?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs) | Patch % | Lines |
|---|---|---|
| [crates/proof-impl/evm-ee-stf/src/processor.rs](https://app.codecov.io/gh/alpenlabs/strata/pull/707?src=pr&el=tree&filepath=crates%2Fproof-impl%2Fevm-ee-stf%2Fsrc%2Fprocessor.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs#diff-Y3JhdGVzL3Byb29mLWltcGwvZXZtLWVlLXN0Zi9zcmMvcHJvY2Vzc29yLnJz) | 83.33% | [1 Missing :warning: ](https://app.codecov.io/gh/alpenlabs/strata/pull/707?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs) |

```diff
@@            Coverage Diff             @@
##             main     #707      +/-   ##
==========================================
+ Coverage   54.95%   54.97%   +0.01%     
==========================================
  Files         315      315              
  Lines       34517    34523       +6     
==========================================
+ Hits        18969    18979      +10     
+ Misses      15548    15544       -4     
```

| [Files with missing lines](https://app.codecov.io/gh/alpenlabs/strata/pull/707?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs) | Coverage Δ | |
|---|---|---|
| [crates/proof-impl/evm-ee-stf/src/processor.rs](https://app.codecov.io/gh/alpenlabs/strata/pull/707?src=pr&el=tree&filepath=crates%2Fproof-impl%2Fevm-ee-stf%2Fsrc%2Fprocessor.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs#diff-Y3JhdGVzL3Byb29mLWltcGwvZXZtLWVlLXN0Zi9zcmMvcHJvY2Vzc29yLnJz) | `62.16% <83.33%> (+0.43%)` | :arrow_up: |

... and [4 files with indirect coverage changes](https://app.codecov.io/gh/alpenlabs/strata/pull/707/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alpenlabs)

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-04T05:13:16Z
body: **Commit**: 07ea9cca

**SP1 Performance Test Results**
 
| program           | cycles      | success  |
|-------------------|-------------|----------|
| BTC_BLOCKSPACE    |      59,479 | ✅       |
| EL_BLOCK          |     118,444 | ✅       |
| CL_BLOCK          |      73,165 | ✅       |
| CHECKPOINT        |       2,616 | ✅       |


author_association: CONTRIBUTOR