Skip to content

Integrate L1SLOAD to the devnet script#61

Open
jmadibekov wants to merge 12 commits intofeat/shastafrom
jmadibekov/l1sload
Open

Integrate L1SLOAD to the devnet script#61
jmadibekov wants to merge 12 commits intofeat/shastafrom
jmadibekov/l1sload

Conversation

@jmadibekov
Copy link
Copy Markdown

@jmadibekov jmadibekov commented Mar 5, 2026

Integrates L1SLOAD (RIP-7728) into the devnet deployment scripts.

  • Pin images: NMC (master-334488b), taiko-client (surge-shasta-0933c0f9), raiko (sha-2c34626)
  • Append rip7728TransitionTimestamp = "0x0" to generated chainspec (enables L1SLOAD from genesis)
  • Pass --Surge.L1EthApiEndpoint + --add-host to genesis hash container so NMC can initialize L1SLOAD

E2E verification on devnet

Both direct and indirect L1SLOAD calls were tested on fresh devnets and proved successfully.

Test scripts

Direct L1SLOAD call (EOA -> precompile)
if [ -f .env ]; then
  set -a; source .env; set +a; echo "Loaded .env"
elif [ -f .env.devnet ]; then
  set -a; source .env.devnet; set +a; echo "Loaded .env.devnet"
else
  echo "ERROR: no .env or .env.devnet found"; exit 1
fi

L1_BLOCK=$(cast block-number --rpc-url http://localhost:32003)
TAIKO_INBOX=$SHASTA_SURGE_INBOX
EXPECTED_VALUE=$(cast storage $TAIKO_INBOX 0x0 --rpc-url http://localhost:32003 --block $L1_BLOCK)
echo "EXPECTED storage value (slot 0, block $L1_BLOCK): $EXPECTED_VALUE"

ADDR=$(echo $TAIKO_INBOX | sed 's/0x//')
KEY="0000000000000000000000000000000000000000000000000000000000000000"
BLOCK_HEX=$(printf "%064x" $L1_BLOCK)
CALLDATA="0x${ADDR}${KEY}${BLOCK_HEX}"

L1SLOAD_ADDR="0x0000000000000000000000000000000000010001"
L1SLOAD_RESULT=$(cast call $L1SLOAD_ADDR --data $CALLDATA --rpc-url http://localhost:8547)
echo "L1SLOAD returned: $L1SLOAD_RESULT"
if [ "$L1SLOAD_RESULT" = "$EXPECTED_VALUE" ]; then echo '[PASS]'; else echo '[FAIL]'; fi

cast send $L1SLOAD_ADDR $CALLDATA \
  --rpc-url http://localhost:8547 --private-key $PRIVATE_KEY --gas-limit 100000
Indirect L1SLOAD call (EOA -> L1Reader contract -> precompile)
contract L1Reader {
    function readL1Storage(address contractAddr, bytes32 slot, uint256 blockNumber)
        external view returns (bytes32 value)
    {
        bytes memory input = abi.encodePacked(contractAddr, slot, blockNumber);
        (bool success, bytes memory result) = address(0x10001).staticcall(input);
        require(success, "L1SLOAD failed");
        require(result.length == 32, "Invalid L1SLOAD response length");
        value = abi.decode(result, (bytes32));
    }

    function readAndEmit(address contractAddr, bytes32 slot, uint256 blockNumber)
        external returns (bytes32 value)
    {
        value = this.readL1Storage(contractAddr, slot, blockNumber);
        emit L1StorageRead(contractAddr, slot, blockNumber, value);
    }

    event L1StorageRead(address contractAddr, bytes32 slot, uint256 blockNumber, bytes32 value);
}
forge create L1Reader.sol:L1Reader \
  --rpc-url http://localhost:8547 --private-key $PRIVATE_KEY --broadcast --evm-version paris --legacy
L1_READER=<deployed address>

L1_BLOCK=$(cast block-number --rpc-url http://localhost:32003)
EXPECTED_VALUE=$(cast storage $TAIKO_INBOX 0x0 --rpc-url http://localhost:32003 --block $L1_BLOCK)

RESULT=$(cast call $L1_READER \
  "readL1Storage(address,bytes32,uint256)(bytes32)" \
  $TAIKO_INBOX 0x0000000000000000000000000000000000000000000000000000000000000000 $L1_BLOCK \
  --rpc-url http://localhost:8547)
echo "L1Reader returned: $RESULT"
if [ "$RESULT" = "$EXPECTED_VALUE" ]; then echo '[PASS]'; else echo '[FAIL]'; fi

cast send $L1_READER "readAndEmit(address,bytes32,uint256)" \
  $TAIKO_INBOX 0x0000000000000000000000000000000000000000000000000000000000000000 $L1_BLOCK \
  --rpc-url http://localhost:8547 --private-key $PRIVATE_KEY --gas-limit 200000

Cross references

@jmadibekov jmadibekov self-assigned this Mar 5, 2026
@jmadibekov jmadibekov changed the title Add updates Integrate L1SLOAD to the devnet script Mar 6, 2026
@jmadibekov jmadibekov marked this pull request as ready for review March 6, 2026 12:22
@jmadibekov jmadibekov marked this pull request as draft March 6, 2026 12:47
@smartprogrammer93
Copy link
Copy Markdown

@claude please review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR integrates L1SLOAD (RIP-7728) support into the Surge devnet deployment. L1SLOAD is a precompile that allows L2 contracts to read L1 storage, and this change configures all necessary components (Nethermind, Raiko, chainspec) to support it. The PR description includes thorough E2E verification results demonstrating both direct and indirect L1SLOAD calls working end-to-end.

Changes:

  • Switches Nethermind and Raiko images to local builds (nethermind-local:latest, raiko-local:latest) that include L1SLOAD support, with corresponding pull_policy: never and enhanced RUST_LOG for L1SLOAD-related crates
  • Adds rip7728TransitionTimestamp = "0x0" to the chainspec generation pipeline and passes --Surge.L1EthApiEndpoint to Nethermind for L1SLOAD precompile initialization
  • Sets PROOF_POLLING_INTERVAL=3s in devnet config for faster proof polling

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
.env.devnet Switches to local Docker images for Nethermind and Raiko; sets proof polling interval
docker-compose.yml Sets pull_policy: never for local images; adds L1SLOAD crate log targets to Raiko
deploy-surge-full.sh Adds RIP-7728 transition timestamp to chainspec; passes L1 endpoint to genesis hash extraction container
script/start-nethermind.sh Passes --Surge.L1EthApiEndpoint flag for L1SLOAD support

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…load

# Conflicts:
#	.env.devnet
#	deploy-surge-full.sh
#	script/start-nethermind.sh
@jmadibekov jmadibekov marked this pull request as ready for review March 10, 2026 08:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@jmadibekov
Copy link
Copy Markdown
Author

@claude review and be concise!

@smartprogrammer93
Copy link
Copy Markdown

@claude review

@claude
Copy link
Copy Markdown

claude bot commented Mar 18, 2026

Claude finished @smartprogrammer93's task in 1m 13s —— View job


Code Review

  • Gather context
  • Read changed files
  • Provide review feedback

The PR integrates L1SLOAD (RIP-7728) support into the devnet deployment. The changes are focused and correct. A few observations:

.env.devnet

  • Images updated to pinned remote SHAs (nethermindeth/nethermind:master-334488b, nethermind/surge-raiko-zk:sha-2c34626) — good, this is the right pattern for reproducibility.
  • PROOF_POLLING_INTERVAL= is blank (line 246). Was this intentional? The PR description mentions PROOF_POLLING_INTERVAL=3s was added for faster proof polling, but the current value in the file is empty. Fix this →

deploy-surge-full.sh

  • generate_l2_genesis (line ~1543): The chainspec pipeline now appends .params.rip7728TransitionTimestamp = "0x0" via a final jq — clean and non-invasive addition.
  • generate_l2_genesis (line 1554): l1_rpc_docker is declared twice — once in generate_prover_chain_spec (line 1121) and again here (line 1554). Both use the same fallback. Minor but worth noting — if L1_ENDPOINT_HTTP_DOCKER is unexpectedly unset at genesis-hash time, the fallback hardcodes host.docker.internal:32003 which is fine for the common case.
  • generate_prover_chain_spec (lines ~1105–1112): Null-sanitizing verifier addresses before passing to raiko config is a good defensive fix for mock mode.
  • The --add-host=host.docker.internal:host-gateway addition to the genesis-hash container is necessary and correct for Linux Docker where this mapping isn't automatic.

remove-surge-full.sh

This is a significant refactor of teardown logic:

  • force_remove_containers is a well-structured fallback for when docker compose down fails (e.g., missing .env). Clear intent, good defensive approach.
  • readonly L2_CONTAINERS=(...) / readonly RELAYER_CONTAINERS=(...): The copilot comment flagged this as invalid, but readonly arr=(...) is indeed valid in bash 4+ (readonly accepts assignment syntax). No issue here.
  • remove_dir_force: Uses an Alpine container to handle root-owned files — practical approach for Docker-written data volumes. The abs_dir computation assumes the parent exists, which is safe since it's only called when [[ -d "$dir" ]].
  • The --force flag defaulting logic (lines ~700–712) is cleaner than the previous pattern.
  • Loss of debug/silence mode distinction for remove_l2_stack and remove_relayers — the mode_choice parameter is now accepted but ignored (always silent). This is a regression in UX for --mode debug. Consider either removing the unused parameter or restoring output routing.

docker-compose.yml

  • No changes in this diff (the diff is empty against origin/feat/shasta).

Overall: The L1SLOAD-specific changes (rip7728TransitionTimestamp, --Surge.L1EthApiEndpoint, updated images) are correct and minimal. The remove-surge-full.sh refactor is the bulk of the change and is a net improvement in reliability, with one minor regression noted above.


The refactor accidentally dropped the mode_choice conditional, causing
docker compose output to always be suppressed even in debug mode.
@jmadibekov
Copy link
Copy Markdown
Author

@claude review again and be concise!

@claude
Copy link
Copy Markdown

claude bot commented Mar 19, 2026

Claude finished @jmadibekov's task in 49s —— View job


Code Review

  • Read changed files
  • Provide review

.env.devnet — Image pins updated to specific SHAs (NMC master-334488b, taiko-client surge-shasta-0933c0f9, raiko sha-2c34626). Clean.

deploy-surge-full.sh

  • Null-sanitizing verifier addresses before raiko config (lines 1106–1110): good defensive fix for mock mode.
  • rip7728TransitionTimestamp = "0x0" appended via jq at the end of the chainspec pipeline (line 1543): minimal and correct.
  • --add-host=host.docker.internal:host-gateway on genesis-hash container (line 1554): necessary on Linux where this isn't auto-mapped.

remove-surge-full.sh

  • force_remove_containers + static container lists as readonly arrays: solid fallback when .env is absent and docker compose down can't resolve services.
  • Debug/silence distinction is preserved in remove_l2_stack / remove_relayers (debug: stdout shown; silent: redirected to /dev/null). Previous review note about this being a regression was stale — the latest fix commit (3643c29) restored it.
  • remove_dir_force with Alpine Docker fallback for root-owned volumes: practical and works well.
  • --force flag now defaults all component flags to true (except remove_env) and skips mode prompt — cleaner than before.

Overall: Changes are focused, correct, and minimal. The remove-surge-full.sh refactor is a net improvement. No blocking issues.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants