diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml new file mode 100644 index 000000000..02a2071f9 --- /dev/null +++ b/.github/workflows/smoke.yml @@ -0,0 +1,99 @@ +name: smoke + +# Validate ALREADY-PUBLISHED base-anvil binaries on each target OS by doing +# exactly what an external developer does: run the public install one-liner, +# then run a real Base precompile test. This is the per-platform coverage a +# single laptop cannot do locally (BOP-385). +# +# v0 scope: Linux gnu (amd64 and arm64, each on new + old glibc) and macOS arm64. +# Deferred to follow-ups: Windows and Alpine/musl (extra shell/dependency +# handling) and Intel macOS (needs the macos-15-intel label; test-or-drop TBD). + +on: + workflow_dispatch: + inputs: + version: + description: "Published release to validate (e.g. v1.1.0). Blank = base-foundryup default." + required: false + default: "" + # Self-test the smoke harness whenever it changes (and on this PR). Scoped to + # smoke files so it does not run on unrelated PRs. workflow_dispatch only + # becomes triggerable once this file is on the default branch. + pull_request: + paths: + - "smoke/**" + - ".github/workflows/smoke.yml" + # Once this is green, enable a daily run against the current default release: + # schedule: + # - cron: "0 12 * * *" + +permissions: {} + +jobs: + smoke: + name: smoke ${{ matrix.label }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 20 + strategy: + fail-fast: false # one platform failing must not hide the others + matrix: + include: + - label: linux-amd64 (glibc, ubuntu 24.04) + runner: ubuntu-24.04 + - label: linux-amd64 (older glibc, ubuntu 22.04) + runner: ubuntu-22.04 + - label: linux-arm64 (glibc, ubuntu 24.04) + runner: ubuntu-24.04-arm + - label: linux-arm64 (older glibc, ubuntu 22.04) + runner: ubuntu-22.04-arm + - label: macos-arm64 + runner: macos-latest + # macos-amd64 (Intel) is deferred. GitHub retired the macos-13 image + # on 2025-12-04, so that label never provisions (jobs queue until they + # time out). Intel x86_64 macOS now runs on the macos-15-intel label, + # supported until ~Aug 2027 when Actions drops Intel macOS entirely. + # Follow-up decision: cover Intel-Mac (uncomment the leg below) or drop + # the darwin_amd64 build from the release pipeline. Don't ship it untested. + # - label: macos-amd64 (intel) + # runner: macos-15-intel + steps: + - name: Check out the proof test + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Install base-anvil via the public flow + shell: bash + env: + VERSION: ${{ inputs.version }} + FOUNDRY_DISABLE_NIGHTLY_WARNING: "1" + run: | + set -euo pipefail + # Pin the install dir: the installer otherwise uses + # ${XDG_CONFIG_HOME:-$HOME}/.foundry, and XDG_CONFIG_HOME is set on the + # Linux runners (but not macOS), which moves the bin dir out from under us. + export FOUNDRY_DIR="$HOME/.foundry" + curl -L https://raw.githubusercontent.com/base/base-anvil/HEAD/foundryup/install | bash + bin="$FOUNDRY_DIR/bin" + echo "$bin" >> "$GITHUB_PATH" + + # Resilient to either install variant: the namespaced base-foundryup + # (current default branch) or the older `foundryup --network base`. + if [ -x "$bin/base-foundryup" ]; then + "$bin/base-foundryup" ${VERSION:+--install "$VERSION"} + echo "FORGE=base-forge" >> "$GITHUB_ENV" + else + "$bin/foundryup" --network base ${VERSION:+--install "$VERSION"} + echo "FORGE=forge" >> "$GITHUB_ENV" + fi + + - name: Run the Base proof test + shell: bash + run: | + set -euo pipefail + cd smoke + if [ "$FORGE" = base-forge ]; then + base-forge test -vvv # base-forge enables Base by default + else + forge test --base -vvv + fi diff --git a/smoke/.gitignore b/smoke/.gitignore new file mode 100644 index 000000000..ccb098322 --- /dev/null +++ b/smoke/.gitignore @@ -0,0 +1,3 @@ +cache/ +out/ +broadcast/ diff --git a/smoke/foundry.toml b/smoke/foundry.toml new file mode 100644 index 000000000..ece2b0479 --- /dev/null +++ b/smoke/foundry.toml @@ -0,0 +1,5 @@ +[profile.default] +test = "test" +# Deliberately dependency-free so the smoke test stays hermetic: no forge-std, +# no `forge install`, no submodules, no network. The proof test inlines the one +# interface it needs. diff --git a/smoke/test/BasePrecompile.t.sol b/smoke/test/BasePrecompile.t.sol new file mode 100644 index 000000000..5243b331b --- /dev/null +++ b/smoke/test/BasePrecompile.t.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IActivationRegistry { + function isActivated(bytes32 feature) external view returns (bool); +} + +/// @notice Proof that a base-anvil binary actually installed and seeded the Base +/// precompiles. Run via `base-forge test` (Base enabled by default) or +/// `forge test --base`. +/// +/// The address and feature ids are the real Base values: +/// - ActivationRegistry address: base-std `StdPrecompiles.sol`. +/// - feature ids: base-std `ActivationRegistryFeatureList.sol`, which match +/// the three features base-anvil seeds active in +/// `crates/evm/networks/src/lib.rs` (`base_activation_seeds()`). +/// +/// WITHOUT --base the address is an empty account, `isActivated()` returns +/// false, and this test FAILS: the false-pass becomes a visible failure. +/// WITH --base the precompile is registered and seeded, so it returns true. +contract BasePrecompileSmokeTest { + IActivationRegistry constant ACTIVATION_REGISTRY = + IActivationRegistry(0x8453000000000000000000000000000000000001); + + // keccak256("base.b20_asset") + bytes32 constant B20_ASSET = 0xcdcc772fe4cbdb1029f822861176d09e646db96723d4c1e82ddfdeb8163ef54c; + // keccak256("base.b20_stablecoin") + bytes32 constant B20_STABLECOIN = 0xecfa0def2c10020caaf65e6155aa69c84b24892aaef76eeac52e0e2b3a0b8601; + // keccak256("base.policy_registry") + bytes32 constant POLICY_REGISTRY = 0xb582ebae03f16fee49a6763f78df482fb11ae73f103ed0d330bbe556aa90a43f; + + function test_basePrecompileSeededActive() external view { + require( + ACTIVATION_REGISTRY.isActivated(B20_ASSET), + "B20_ASSET inactive: --base not applied or precompile missing" + ); + require( + ACTIVATION_REGISTRY.isActivated(B20_STABLECOIN), + "B20_STABLECOIN inactive: --base not applied or precompile missing" + ); + require( + ACTIVATION_REGISTRY.isActivated(POLICY_REGISTRY), + "POLICY_REGISTRY inactive: --base not applied or precompile missing" + ); + } +}