Skip to content

test: one deterministic Bedrock version - #793

Merged
hansieodendaal merged 9 commits into
logos-blockchain:devfrom
hansieodendaal:ho_cucumber_ci_binary
Sep 10, 2026
Merged

hansieodendaal merged 9 commits into
logos-blockchain:devfrom
hansieodendaal:ho_cucumber_ci_binary

Conversation

@hansieodendaal

@hansieodendaal hansieodendaal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🎯 Purpose

Resolve #773 by making the Logos Blockchain revision resolved by LEZ's Cargo dependencies the single source of truth for Bedrock node provisioning.

Previously, LEZ could use different Bedrock versions depending on the execution path:

  • Rust/TF dependencies were pinned to a logos-blockchain Git revision;
  • TF could build a node from that revision;
  • the Docker setup independently used 0.2.1-lssa.

This PR removes that independent Bedrock version. In CI, the Linux Bedrock node is resolved once and shared across General integration tests, Docker-backed tests, TF consumers, and Cucumber.

Fixes #773.

🔒 Version Contract

The Cargo-resolved Logos revision is authoritative.

flowchart TD
    A["Cargo.toml / Cargo.lock<br/>Logos dependencies"] --> B["cargo metadata --locked<br/>exact Logos commit"]

    B --> C{"Matching published<br/>release asset?"}
    C -->|Yes| D["Download + verify SHA-256"]
    C -->|No| E["Build exact Cargo checkout"]

    D --> F["Platform-specific resolved node"]
    E --> F

    F --> G["Linux CI artifact"]
    G --> H["General / TF"]
    G --> I["Cucumber"]
    G --> J["Docker Bedrock"]

    F --> K["Host-native TF<br/>when requested"]
Loading

Platform-specific binaries may differ, but they must all correspond to the same Cargo-resolved revision.

⚙️ Approach

  • Derive the exact logos-blockchain revision from cargo metadata --locked
  • Reject Cargo graphs containing multiple Logos revisions
  • Use an exact matching release asset when available and verify its SHA-256
  • Otherwise build logos-blockchain-node from the exact Cargo checkout
  • Resolve/build the Linux Bedrock node once per CI run and share it as an artifact
  • Reuse that artifact across TF, Docker-backed tests, integration tests, and Cucumber
  • Make Cucumber a reusable workflow while keeping it separate from nextest
  • Remove the independently versioned 0.2.1-lssa node image
  • Support Docker/Linux and host-native local resolution
  • Exclude integration-test binaries with no runnable nextest tests from the CI matrix
  • Add resolver and target-discovery coverage

Resolver

The shared resolver:

  1. derives the exact Logos revision from Cargo;
  2. checks for a published release resolving to that commit;
  3. selects only the exact expected platform asset;
  4. verifies its published SHA-256;
  5. otherwise builds:
cargo build --locked --release \
  -p logos-blockchain-node \
  --features testing

The source checkout is verified before building.

Outputs:

bedrock/.resolved/
├── logos-blockchain-node
└── metadata.json

Host-native resolution uses:

bedrock/.resolved-host/
├── logos-blockchain-node
└── metadata.json

Metadata records the revision, target platform, resolution method, release information when applicable, and binary SHA-256.

HTTP reads use bounded retries for transient failures; ordinary client errors such as 404 are not retried.

Single CI producer

ci.yml is the top-level CI orchestrator.

The CI image and Bedrock node are each produced once. Unrelated checks remain parallel, while only actual Bedrock consumers wait for the node artifact:

flowchart TD
    A["CI image"] --> B["Bedrock node resolver"]
    A --> C["lint / unit / fmt / other CI"]
    A --> D["integration-tests-prebuild"]

    B --> E["bedrock-node artifact"]
    D --> F["nextest archive"]

    E --> G["Integration-test matrix"]
    F --> G

    E --> H["test_fixtures / Docker"]
    E --> I["TF consumers"]
    E --> J["Reusable Cucumber workflow"]
Loading

TF consumers receive:

LOGOS_BLOCKCHAIN_NODE_BIN=<workspace>/bedrock/.resolved/logos-blockchain-node

This removes the previous need to recreate Cargo Git state or unset CI / GITHUB_ACTIONS to force TF onto its source-build provider.

Cucumber CI

Cucumber no longer resolves or builds another Bedrock node.

The reusable workflow receives the shared CI image, downloads the same bedrock-node artifact, builds the Cucumber target, then runs:

@smoke_ci
    ↓
@auth_transfer_ci
    ↓
@indexer_state_ci
    ↓
@multi_sequencer_ci

Each suite receives the shared node through LOGOS_BLOCKCHAIN_NODE_BIN.

Cucumber remains a separate execution path from nextest.

Integration-test target discovery

The integration-test matrix is now derived from the full nextest test inventory rather than just the binary list.

Only binaries containing at least one runnable test are scheduled. This avoids downloading and extracting the large nextest archive for binaries that would simply report 0 tests run.

tps remains intentionally excluded.

Temporary TF test quarantine

The three tf_app_deployments tests are temporarily ignored because concurrent execution exposed a separate port-allocation race in the upstream Logos testing infrastructure.

The observed AddrInUse node startup failures break the Bedrock network and cause downstream cluster/indexer failures.

Fixing that cross-process port reservation mechanism is outside this PR. Once fixed upstream, these tests should be re-enabled.

Docker-backed Bedrock

The independently versioned 0.2.1-lssa image is removed.

Docker now builds a small runtime image containing the same Cargo-authoritative Linux node used by CI:

Cargo-resolved Logos revision
            │
            ▼
       Linux node
        ┌───┴────┐
        ▼        ▼
       CI      Docker

The existing Bedrock configuration, ports, and launch script remain unchanged.

Local development

Docker-backed Bedrock:

just resolve-bedrock-node
just run-bedrock

Host-native TF:

just resolve-host-bedrock-node

LOGOS_BLOCKCHAIN_NODE_BIN="$PWD/bedrock/.resolved-host/logos-blockchain-node" \
  cargo test -p integration_tests ...

Docker and host-native binaries may differ by platform, but both correspond to the same Cargo-resolved Logos revision.

Docker-backed tests also validate stored resolver metadata against the current Cargo.lock.

Coverage

Resolver tests cover:

  • exact release/commit matching;
  • asset and digest validation;
  • duplicate/unverifiable asset rejection;
  • archive extraction;
  • multiple Cargo revision rejection;
  • source checkout verification;
  • transient HTTP retries;
  • live published-release lookup.

Target-discovery tests cover:

  • excluding binaries with no runnable tests;
  • preserving runnable binaries;
  • tps exclusion;
  • rejecting unsuitable binary-only nextest listings.

🧪 How to Test

Verify that CI demonstrates:

  • one CI-image producer;
  • one Cargo-authoritative Bedrock resolution/build;
  • one bedrock-node artifact;
  • General and Cucumber both consuming that artifact;
  • unrelated CI work remaining parallel with the Bedrock build;
  • no integration-test matrix jobs for binaries without runnable tests;
  • all expected General/TF/Docker-backed tests passing;
  • all four Cucumber suites passing.

Resolver logs should report:

  • Cargo-resolved Logos revision;
  • target platform;
  • release download or source-build resolution;
  • resulting binary SHA-256.

Expected result

                 LEZ Cargo dependencies
                          │
                          ▼
                  exact Logos revision
                          │
              ┌───────────┴───────────┐
              ▼                       ▼
       Linux node binary       Host-native binary
              │                       │
      ┌───────┼────────┐              ▼
      ▼       ▼        ▼         local native TF
   General Cucumber  Docker
     CI              Bedrock

Updating LEZ's Logos dependency is sufficient to change the Bedrock node revision used throughout the project.

If an exact release asset exists, it is downloaded and verified. Otherwise that exact revision is built from source.

🔗 Dependencies

Depends on #779.

🔜 Future Work

This PR deliberately does not:

  • change the authoritative Logos dependency revision;
  • route Cucumber through nextest;
  • share the nextest/Cucumber test build products;
  • change Cucumber scenario concurrency;
  • fix the upstream port-allocation race exposed by tf_app_deployments;
  • introduce workflow_run or cross-workflow-run artifact plumbing.

Once the upstream allocator is fixed, the temporarily ignored tf_app_deployments tests should be re-enabled.

📋 PR Completion Checklist

  • Complete PR description
  • Implement the core functionality
  • Add/update tests
  • Add/update documentation and inline comments

…lves issue logos-blockchain#774.

#### Changes

- Added .github/workflows/cucumber-integration-tests.yml
  - Runs on PRs and pushes to dev/main
  - Uses workflow concurrency cancellation
  - Reuses the existing LEZ CI image
  - Runs the Cucumber suites on a single ubuntu-latest runner
  - Builds the Cucumber test target before scenario execution
  - Executes four sequential suites with scenario concurrency set to 1:
    - @smoke_ci
    - @auth_transfer_ci
    - @indexer_state_ci
    - @multi_sequencer_ci
  - Uploads the Cucumber JUnit report

- Added reusable run-cucumber-suite action using run-in-ci-image
  - Preserves LEZ’s Docker-aware execution model
  - Uploads suite diagnostics on failure
  - Preserves test failures after artifact upload

- Updated run-in-ci-image to persist Cargo git state and Logos blockchain
  build/cache state between disposable CI containers.

- Fixed source-bearing thiserror display messages in
  integration_tests/src/cucumber/error.rs.

- Fixed accidental indentation in block_publisher.rs.
@hansieodendaal hansieodendaal changed the title Ho cucumber ci binary test: one deterministic Bedrock version Aug 27, 2026
…al Docker setup.

Resolve logos-blockchain#773 by making the Logos Blockchain revision resolved by LEZ's Cargo dependencies the single source of truth for Bedrock node provisioning.

Previously, LEZ could use different Bedrock versions depending on the execution path:

- Rust/TF dependencies were pinned to a `logos-blockchain` Git revision;
- TF could build a node from that revision;
- the legacy Docker setup independently used `0.2.1-lssa`.

This PR removes that independent Bedrock version and ties all node binaries back to the exact Logos revision resolved by Cargo.

Fixes logos-blockchain#773.

Depends on logos-blockchain#779.

The Cargo-resolved Logos revision is authoritative.

Platform-specific node binaries may differ, but they must all correspond to that same exact revision:

```mermaid
flowchart TD
    A["Cargo.toml / Cargo.lock<br/>Logos dependencies"] --> B["cargo metadata --locked<br/>resolved Logos commit"]

    B --> C{"Matching published<br/>release asset for target platform?"}

    C -->|Yes| D["Download exact asset<br/>and verify SHA-256"]
    C -->|No| E["Build logos-blockchain-node<br/>from exact Cargo checkout"]

    D --> F["Platform-specific<br/>resolved node"]
    E --> F

    F --> G["Linux CI<br/>TF / Cucumber"]
    F --> H["Linux Docker Bedrock"]
    F --> I["Host-native TF<br/>when requested"]
```

There is no separately maintained Bedrock Docker tag or node revision that can drift from LEZ's Cargo dependency graph.

A shared resolver now:

1. runs `cargo metadata --locked`;
2. determines the exact `logos-blockchain` revision used by LEZ;
3. verifies that all packages sourced from `logos-blockchain/logos-blockchain` resolve to one revision;
4. checks published Logos Blockchain releases for a tag resolving to that exact commit;
5. selects only the exact expected node asset for the requested platform;
6. verifies the published SHA-256 digest;
7. otherwise builds `logos-blockchain-node` from the exact Cargo checkout using:

```bash
cargo build --locked --release \
  -p logos-blockchain-node \
  --features testing
```

The resolver verifies the source checkout revision before building.

For Docker/CI resolution the canonical output is:

```text
bedrock/.resolved/
├── logos-blockchain-node
└── metadata.json
```

For explicitly requested host-native resolution:

```text
bedrock/.resolved-host/
├── logos-blockchain-node
└── metadata.json
```

The metadata records:

- the resolved Logos revision;
- the target platform;
- whether the binary came from a release or source build;
- release information when applicable;
- the resulting binary SHA-256.

A published binary is only accepted when all of the following are true:

```mermaid
flowchart LR
    A["Cargo-resolved SHA"] --> B{"Release tag resolves<br/>to exact SHA?"}
    B -->|No| X["Reject"]
    B -->|Yes| C{"Exact expected<br/>platform asset exists once?"}
    C -->|No| X
    C -->|Yes| D{"Published SHA-256<br/>available and valid?"}
    D -->|No| X
    D -->|Yes| E["Download + verify"]
```

The expected asset name is deterministic:

```text
logos-blockchain-node-<platform>-<release-tag>.tar.gz
```

Missing, duplicate, or unverifiable assets are not selected.

If no suitable release exists, the resolver falls back to building the exact Cargo-resolved revision from source.

General CI resolves one Linux node and uploads it as an artifact for the jobs that need Bedrock:

```mermaid
flowchart TD
    A["ci-image"] --> B["Bedrock node resolver"]
    A --> C["integration-tests-prebuild"]

    B --> D["bedrock-node artifact"]
    C --> E["nextest archive"]

    D --> F["Legacy integration-test matrix"]
    E --> F

    D --> G["test_fixtures / Docker-backed tests"]
    D --> H["TF-based tests"]
```

TF-based tests receive the resolved binary explicitly through:

```text
LOGOS_BLOCKCHAIN_NODE_BIN=<workspace>/bedrock/.resolved/logos-blockchain-node
```

This removes the previous need to recreate Cargo Git state or unset `CI` / `GITHUB_ACTIONS` to force TF onto its source-build provider.

The Cucumber workflow independently resolves the same Cargo-authoritative Linux node:

```mermaid
flowchart TD
    A["ci-image"] --> B["Resolve Cargo-pinned Linux node"]
    B --> C["Build Cucumber target"]
    C --> D["@smoke_ci"]
    D --> E["@auth_transfer_ci"]
    E --> F["@indexer_state_ci"]
    F --> G["@multi_sequencer_ci"]
```

Each suite receives:

```text
LOGOS_BLOCKCHAIN_NODE_BIN=<workspace>/bedrock/.resolved/logos-blockchain-node
```

The temporary `env -u GITHUB_ACTIONS -u CI` workaround is no longer needed.

Cucumber remains separate from nextest.

The legacy independently versioned `0.2.1-lssa` node image is removed.

Instead, Docker builds a small runtime image containing the resolved Linux binary:

```mermaid
flowchart LR
    A["Cargo-resolved Logos revision"]

    A --> B["Resolve Linux node"]

    B --> C["TF / CI direct execution"]
    B --> D["Bedrock runtime image"]

    D --> E["/usr/bin/logos-blockchain-node"]
```

The runtime image uses Debian Trixie so source-built binaries produced in the LEZ CI environment have a compatible runtime.

The existing Bedrock configuration, ports, and launch script remain unchanged.

Docker-backed local Bedrock uses the same Cargo-authoritative resolution path:

```bash
just resolve-bedrock-node
just run-bedrock
```

`just run-bedrock` resolves the Linux binary and rebuilds the runtime image before starting Compose.

The local resolver runs its controlled Linux build environment using the invoking user's UID/GID so generated files and caches remain caller-owned.

For explicitly host-native TF usage:

```bash
just resolve-host-bedrock-node
```

This writes the native binary to:

```text
bedrock/.resolved-host/logos-blockchain-node
```

Docker and host-native binaries may differ by platform, but both correspond to the same Cargo-resolved Logos revision.

Docker-backed tests also validate the stored resolver metadata against the current `Cargo.lock`, preventing a stale binary from silently surviving a Logos dependency update.

The resolver tests cover:

- exact release commit matching;
- rejecting releases for another commit;
- rejecting unverifiable digests;
- rejecting ambiguous duplicate assets;
- archive extraction;
- rejecting multiple Cargo-resolved Logos revisions;
- rejecting source checkouts at the wrong revision;
- matching an actual published Logos release through the live GitHub API.

The live release test uses the workflow's GitHub token.

Verify resolver behavior:

- the current Cargo graph resolves to one Logos revision;
- a matching published release selects only the exact expected platform asset;
- the asset digest is verified before extraction;
- a revision without a matching release falls back to an exact-revision source build;
- resolver metadata records the revision, platform, resolution method, and binary digest.

Verify the normal CI consumers:

- `test_fixtures`;
- legacy integration-test matrix;
- TF-based integration tests;
- Docker-backed Bedrock tests;
- Cucumber suites:
  - `@smoke_ci`
  - `@auth_transfer_ci`
  - `@indexer_state_ci`
  - `@multi_sequencer_ci`

Verify that CI logs report:

- the Cargo-resolved Logos revision;
- the target platform;
- whether the binary was downloaded or source-built;
- the resulting binary SHA-256.

After this change:

```text
             LEZ Cargo dependencies
                     │
                     ▼
             exact Logos revision
                     │
          ┌──────────┴──────────┐
          ▼                     ▼
    Linux node binary      Host-native binary
          │                     │
      ┌───┴────┐                ▼
      ▼        ▼           local native TF
     CI      Docker
   TF/Cucumber Bedrock
```

Updating LEZ's Logos dependency is sufficient to change the Bedrock node revision used throughout the project.

If an exact release asset exists for the requested platform, it is downloaded and verified.

If not, that exact Cargo-resolved revision is built from source.

This PR deliberately does not:

- change the authoritative Logos dependency revision itself;
- combine the General and Cucumber top-level workflows;
- route Cucumber through nextest;
- implement the separate nextest/Cucumber pre-build sharing optimization discussed in logos-blockchain#779;
- change Cucumber scenario concurrency;
- introduce cross-workflow build-artifact sharing.

remove flaky concurrent tf tests

single path bedrock binary resolution

@moudyellaz moudyellaz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm, thanks. Left some minor comments!

Comment thread .github/scripts/resolve_bedrock_node.py Outdated
Comment thread .github/scripts/resolve_bedrock_node.py Outdated
Comment thread integration_tests/tests/tf_app_deployments.rs Outdated

@Arjentix Arjentix left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, thanks. Tbh I'm a bit worried how complex this became and already forgot why we can't just always use pinned docker version

Comment thread Justfile Outdated
Comment thread test_fixtures/src/setup.rs
Comment thread integration_tests/tests/tf_app_deployments.rs Outdated
Comment thread integration_tests/src/cucumber/steps/transfers/public.rs Outdated
Comment thread .github/workflows/ci.yml
# Conflicts:
#	.github/actions/run-cucumber-suite/action.yml
#	.github/workflows/ci.yml
#	.github/workflows/cucumber-integration-tests.yml
#	integration_tests/src/cucumber/steps/accounts/helpers.rs
@hansieodendaal

Copy link
Copy Markdown
Contributor Author

LGTM, thanks. Tbh I'm a bit worried how complex this became and already forgot why we can't just always use pinned docker version

Please see #793 (comment).

@hansieodendaal

Copy link
Copy Markdown
Contributor Author

@Arjentix and @moudyellaz, I know you have approved, thanks, but I fixed/answered the minor comments. Happy to resolve those?

@hansieodendaal
hansieodendaal merged commit dfff6ac into logos-blockchain:dev Sep 10, 2026
95 of 104 checks passed
@hansieodendaal
hansieodendaal deleted the ho_cucumber_ci_binary branch September 10, 2026 11:51
romanzac added a commit that referenced this pull request Sep 11, 2026
Split sequencer_registration.feature into three feature files, each a
coherent group with its own CI tag, and add one workflow step per group
to cucumber-integration-tests.yml following the run-cucumber-suite
pattern from #779 and #793:

- sequencer_registration.feature, @stake_registration_ci: the amount a
  first Stake moves (P-01, P-02, P-03, P-25, P-23)
- stake_account_validation.feature, @stake_accounts_ci: the pre-state
  account list, its signers, owners and count (P-04, P-13, P-14, P-20)
- stake_instruction_validation.feature, @stake_instruction_ci: the
  instruction bytes and call path (P-18, P-24)

Feature names drop the em-dash tagline so the artifact_subdir path the
suite action uploads on failure is plain ASCII. The non-inclusion
protocol rationale stays in sequencer_registration.feature; the other
two files point at it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create one deterministic Bedrock version for TF tests and the Docker/local setup

3 participants