diff --git a/.github/workflows/client_host.yaml b/.github/workflows/client_host.yaml new file mode 100644 index 000000000..f72c3f6f9 --- /dev/null +++ b/.github/workflows/client_host.yaml @@ -0,0 +1,84 @@ +name: Client + Host +on: + push: + branches: [main] + merge_group: + pull_request: +env: + CARGO_TERM_COLOR: always +jobs: + host-client-offline-runs: + name: ${{ matrix.target}} | ${{ matrix.name }} + strategy: + matrix: + target: ["native", "asterisc"] + name: + [ + "OP Mainnet (Ecotone) - Block #122093770", + "OP Sepolia (Fjord) - Block #13992475", + ] + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - uses: taiki-e/install-action@just + - name: Install Rust stable toolchain + uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + prefix-key: ${{ matrix.target }}-${{ matrix.name }} + - uses: rui314/setup-mold@v1 + - name: Clone `asterisc` repository + if: "!contains(matrix.target, 'native')" + run: | + git clone https://github.com/ethereum-optimism/asterisc.git + - name: Setup Go toolchain + if: "!contains(matrix.target, 'native')" + uses: actions/setup-go@v5 + with: + go-version: "1.21.6" + cache-dependency-path: | + asterisc/go.sum + - name: Build `asterisc` + if: "!contains(matrix.target, 'native')" + run: | + cd asterisc && git checkout v1.0.0 && make build-rvgo + mv ./rvgo/bin/asterisc /usr/local/bin/ + - name: Set run environment + run: | + if [[ ${{ contains(matrix.name, 122093770) }} == true ]]; then + BLOCK_NUMBER=122093770 + echo "BLOCK_NUMBER=$BLOCK_NUMBER" >> $GITHUB_ENV + echo "L2_CLAIM=0xf31619aa7cdf03e6e51bb5eda528309eff0354172c4a64186622929c76bd58a9" >> $GITHUB_ENV + echo "L2_OUTPUT_ROOT=0xec52f62123ab0404c1729fbb34716046cffaba6c47836b353184e231e290c560" >> $GITHUB_ENV + echo "L2_HEAD=0xd20e2cad399a964c7bb656c8d32a6e5356a831167cc58a00e60285abf7e94df6" >> $GITHUB_ENV + echo "L1_HEAD=0xed32f3f6cdf80c1d755851ad065f4436ff17ff35b7910262f0d22c1e0919a315" >> $GITHUB_ENV + echo "L2_CHAIN_ID=10" >> $GITHUB_ENV + echo "WITNESS_TAR_NAME=ecotone-op-mainnet-$BLOCK_NUMBER-witness.tar.zst" >> $GITHUB_ENV + elif [[ ${{ contains(matrix.name, 13992475) }} == true ]]; then + BLOCK_NUMBER=13992475 + echo "BLOCK_NUMBER=$BLOCK_NUMBER" >> $GITHUB_ENV + echo "L2_CLAIM=0xba854e0a3e1e65f8e4b05f39a5ad28c13eac0d5444c7c336b6f7ebabb0f20229" >> $GITHUB_ENV + echo "L2_OUTPUT_ROOT=0x59cfca741f06a0e5607e3526e4f9931b8a6a2313b48d8bd0c2242bace6d930f1" >> $GITHUB_ENV + echo "L2_HEAD=0x90ec80845a776021ce56b3e87072e8e30d2931c9c67ccaf2a305bd08268178ee" >> $GITHUB_ENV + echo "L1_HEAD=0x607b4a4d2bd7e7bced989e4bc471a5ce61335b63881c624c8522f63113a5af86" >> $GITHUB_ENV + echo "L2_CHAIN_ID=11155420" >> $GITHUB_ENV + echo "WITNESS_TAR_NAME=fjord-op-sepolia-$BLOCK_NUMBER-witness.tar.zst" >> $GITHUB_ENV + fi + - name: Decompress witness data directory + run: | + tar --zstd -xvf ./bin/client/testdata/$WITNESS_TAR_NAME -C . + - name: Run host + client offline + working-directory: ./bin/client + run: | + mkdir -p ../../target + just run-client-${{ matrix.target }}-offline \ + $BLOCK_NUMBER \ + $L2_CLAIM \ + $L2_OUTPUT_ROOT \ + $L2_HEAD \ + $L1_HEAD \ + $L2_CHAIN_ID \ + -vv diff --git a/bin/client/justfile b/bin/client/justfile index f549cf370..4611ccc3c 100644 --- a/bin/client/justfile +++ b/bin/client/justfile @@ -71,10 +71,7 @@ run-client-native block_number l1_rpc l1_beacon_rpc l2_rpc rollup_node_rpc verbo L1_BEACON_ADDRESS="{{l1_beacon_rpc}}" L2_NODE_ADDRESS="{{l2_rpc}}" OP_NODE_ADDRESS="{{rollup_node_rpc}}" - - HOST_BIN_PATH="./target/release/kona-host" CLIENT_BIN_PATH="./target/riscv64gc-unknown-none-elf/release-client-lto/kona" - STATE_PATH="./state.json" L2_BLOCK_NUMBER={{block_number}} echo "Fetching configuration for block #$L2_BLOCK_NUMBER..." @@ -110,3 +107,78 @@ run-client-native block_number l1_rpc l1_beacon_rpc l2_rpc rollup_node_rpc verbo --exec $CLIENT_BIN_PATH \ --data-dir ./data \ {{verbosity}} + +# Run the client program natively with the host program attached, in offline mode. +run-client-native-offline block_number l2_claim l2_output_root l2_head l1_head l2_chain_id verbosity: + #!/usr/bin/env bash + + CLIENT_BIN_PATH="./target/release-client-lto/kona" + + L2_BLOCK_NUMBER={{block_number}} + L2_CLAIM={{l2_claim}} + L2_OUTPUT_ROOT={{l2_output_root}} + L2_HEAD={{l2_head}} + L1_HEAD={{l1_head}} + L2_CHAIN_ID={{l2_chain_id}} + + # Move to the workspace root + cd $(git rev-parse --show-toplevel) + + echo "Building client program..." + cargo build --bin kona --profile release-client-lto --features tracing-subscriber + echo "Running host program with native client program..." + cargo r --bin kona-host --release -- \ + --l1-head $L1_HEAD \ + --l2-head $L2_HEAD \ + --l2-claim $L2_CLAIM \ + --l2-output-root $L2_OUTPUT_ROOT \ + --l2-block-number $L2_BLOCK_NUMBER \ + --l2-chain-id $L2_CHAIN_ID \ + --exec $CLIENT_BIN_PATH \ + --data-dir ./data \ + {{verbosity}} + +# Run the client program on asterisc with the host program detached, in offline mode. +run-client-asterisc-offline block_number l2_claim l2_output_root l2_head l1_head l2_chain_id verbosity: + #!/usr/bin/env bash + + HOST_BIN_PATH="./target/release/kona-host" + CLIENT_BIN_PATH="./target/riscv64gc-unknown-none-elf/release-client-lto/kona" + STATE_PATH="./state.json" + + L2_BLOCK_NUMBER={{block_number}} + L2_CLAIM={{l2_claim}} + L2_OUTPUT_ROOT={{l2_output_root}} + L2_HEAD={{l2_head}} + L1_HEAD={{l1_head}} + L2_CHAIN_ID={{l2_chain_id}} + + # Move to the workspace root + cd $(git rev-parse --show-toplevel) + + echo "Building client program for RISC-V target..." + just build-asterisc --bin kona --profile release-client-lto + + echo "Loading client program into Asterisc state format..." + asterisc load-elf --path=$CLIENT_BIN_PATH + + echo "Building host program for native target..." + cargo build --bin kona-host --release + + echo "Running asterisc" + asterisc run \ + --info-at '%10000000' \ + --proof-at never \ + --input $STATE_PATH \ + -- \ + $HOST_BIN_PATH \ + --l1-head $L1_HEAD \ + --l2-head $L2_HEAD \ + --l2-claim $L2_CLAIM \ + --l2-output-root $L2_OUTPUT_ROOT \ + --l2-block-number $L2_BLOCK_NUMBER \ + --l2-chain-id $L2_CHAIN_ID \ + --server \ + --data-dir ./data \ + --exec "" \ + {{verbosity}} diff --git a/bin/client/testdata/ecotone-op-mainnet-122093770-witness.tar.zst b/bin/client/testdata/ecotone-op-mainnet-122093770-witness.tar.zst new file mode 100644 index 000000000..11f496546 Binary files /dev/null and b/bin/client/testdata/ecotone-op-mainnet-122093770-witness.tar.zst differ diff --git a/bin/client/testdata/fjord-op-sepolia-13992475-witness.tar.zst b/bin/client/testdata/fjord-op-sepolia-13992475-witness.tar.zst new file mode 100644 index 000000000..f02645a6a Binary files /dev/null and b/bin/client/testdata/fjord-op-sepolia-13992475-witness.tar.zst differ diff --git a/bin/host/src/main.rs b/bin/host/src/main.rs index 496879e16..f806820dd 100644 --- a/bin/host/src/main.rs +++ b/bin/host/src/main.rs @@ -60,26 +60,27 @@ async fn start_server(cfg: HostCli) -> Result<()> { let kv_store = cfg.construct_kv_store(); - let beacon_client = OnlineBeaconClient::new_http( - cfg.l1_beacon_address.clone().expect("Beacon API URL must be set"), - ); - let mut blob_provider = OnlineBlobProvider::new(beacon_client, None, None); - blob_provider - .load_configs() - .await - .map_err(|e| anyhow!("Failed to load blob provider configuration: {e}"))?; - - let fetcher = (!cfg.is_offline()).then(|| { + let fetcher = if !cfg.is_offline() { + let beacon_client = OnlineBeaconClient::new_http( + cfg.l1_beacon_address.clone().expect("Beacon API URL must be set"), + ); + let mut blob_provider = OnlineBlobProvider::new(beacon_client, None, None); + blob_provider + .load_configs() + .await + .map_err(|e| anyhow!("Failed to load blob provider configuration: {e}"))?; let l1_provider = util::http_provider(&cfg.l1_node_address.expect("Provider must be set")); let l2_provider = util::http_provider(&cfg.l2_node_address.expect("Provider must be set")); - Arc::new(RwLock::new(Fetcher::new( + Some(Arc::new(RwLock::new(Fetcher::new( kv_store.clone(), l1_provider, blob_provider, l2_provider, cfg.l2_head, - ))) - }); + )))) + } else { + None + }; // Start the server and wait for it to complete. info!("Starting preimage server."); @@ -96,28 +97,29 @@ async fn start_server_and_native_client(cfg: HostCli) -> Result<()> { let (preimage_pipe, hint_pipe, files) = util::create_native_pipes()?; let kv_store = cfg.construct_kv_store(); - let beacon_client = OnlineBeaconClient::new_http( - cfg.l1_beacon_address.clone().expect("Beacon API URL must be set"), - ); - let mut blob_provider = OnlineBlobProvider::new(beacon_client, None, None); - blob_provider - .load_configs() - .await - .map_err(|e| anyhow!("Failed to load blob provider configuration: {e}"))?; - - let fetcher = (!cfg.is_offline()).then(|| { + let fetcher = if !cfg.is_offline() { + let beacon_client = OnlineBeaconClient::new_http( + cfg.l1_beacon_address.clone().expect("Beacon API URL must be set"), + ); + let mut blob_provider = OnlineBlobProvider::new(beacon_client, None, None); + blob_provider + .load_configs() + .await + .map_err(|e| anyhow!("Failed to load blob provider configuration: {e}"))?; let l1_provider = util::http_provider(cfg.l1_node_address.as_ref().expect("Provider must be set")); let l2_provider = util::http_provider(cfg.l2_node_address.as_ref().expect("Provider must be set")); - Arc::new(RwLock::new(Fetcher::new( + Some(Arc::new(RwLock::new(Fetcher::new( kv_store.clone(), l1_provider, blob_provider, l2_provider, cfg.l2_head, - ))) - }); + )))) + } else { + None + }; // Create the server and start it. let server_task =