Skip to content

Commit 0eed6b7

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/lean-api-events
# Conflicts: # bin/ethlambda/src/main.rs # crates/blockchain/src/lib.rs # crates/blockchain/src/store.rs # crates/net/rpc/src/lib.rs
2 parents 3e6b737 + c8f0973 commit 0eed6b7

42 files changed

Lines changed: 3044 additions & 486 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/run-fixture-tests/action.yml

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,43 @@ runs:
1010
- name: Get leanSpec fixtures release info
1111
id: fixtures-release
1212
shell: bash
13+
env:
14+
# Authenticate the API call: unauthenticated requests share the runner
15+
# IP's low rate-limit pool, which is the usual cause of transient failures.
16+
GH_TOKEN: ${{ github.token }}
1317
run: |
18+
set -euo pipefail
1419
api_url="https://api.github.com/repos/leanEthereum/leanSpec/releases/latest"
15-
json=$(curl -sL "$api_url")
16-
fixtures_url=$(echo "$json" | python3 -c "import sys,json; j=json.load(sys.stdin); print(next(a.get('browser_download_url') for a in j.get('assets',[]) if a.get('name')=='fixtures-prod-scheme.tar.gz'))")
17-
sha_url=$(echo "$json" | python3 -c "import sys,json; j=json.load(sys.stdin); print(next(a.get('browser_download_url') for a in j.get('assets',[]) if a.get('name')=='fixtures-prod-scheme.tar.gz.sha256'))")
18-
sha=$(curl -sL "$sha_url" | cut -d' ' -f1)
20+
21+
# -f fails on HTTP errors (so an error body is never captured as data) and
22+
# --retry rides out transient network/5xx hiccups. Fail loudly, since a
23+
# command substitution in an assignment does not trip `set -e` on its own.
24+
json=$(curl -sSL -f --retry 5 --retry-all-errors \
25+
-H "Accept: application/vnd.github+json" \
26+
-H "Authorization: Bearer $GH_TOKEN" \
27+
"$api_url") \
28+
|| { echo "::error::Failed to fetch leanSpec latest release metadata from $api_url"; exit 1; }
29+
30+
asset_url() {
31+
echo "$json" | python3 -c \
32+
"import sys,json; name=sys.argv[1]; j=json.load(sys.stdin); print(next(a['browser_download_url'] for a in j.get('assets',[]) if a.get('name')==name))" \
33+
"$1"
34+
}
35+
fixtures_url=$(asset_url "fixtures-prod-scheme.tar.gz") \
36+
|| { echo "::error::Release is missing the fixtures-prod-scheme.tar.gz asset"; exit 1; }
37+
sha_url=$(asset_url "fixtures-prod-scheme.tar.gz.sha256") \
38+
|| { echo "::error::Release is missing the fixtures-prod-scheme.tar.gz.sha256 asset"; exit 1; }
39+
40+
sha=$(curl -sSL -f --retry 5 --retry-all-errors "$sha_url" | cut -d' ' -f1) \
41+
|| { echo "::error::Failed to download the fixtures checksum from $sha_url"; exit 1; }
42+
43+
# Validate before writing: a transient HTML error page captured as $sha
44+
# previously corrupted $GITHUB_OUTPUT ("Invalid format '<!--'").
45+
if [[ ! "$sha" =~ ^[0-9a-fA-F]{64}$ ]]; then
46+
echo "::error::Expected a 64-char hex SHA256 for the fixtures archive, got: '$sha'"
47+
exit 1
48+
fi
49+
1950
{
2051
echo "url=$fixtures_url"
2152
echo "sha_url=$sha_url"
@@ -39,8 +70,8 @@ runs:
3970
fixtures_url="${{ steps.fixtures-release.outputs.url }}"
4071
sha_url="${{ steps.fixtures-release.outputs.sha_url }}"
4172
echo "Downloading fixtures from $fixtures_url"
42-
curl -L -f -o "$tmpdir/fixtures-prod-scheme.tar.gz" "$fixtures_url"
43-
curl -L -f -o "$tmpdir/fixtures-prod-scheme.tar.gz.sha256" "$sha_url"
73+
curl -L -f --retry 5 --retry-all-errors -o "$tmpdir/fixtures-prod-scheme.tar.gz" "$fixtures_url"
74+
curl -L -f --retry 5 --retry-all-errors -o "$tmpdir/fixtures-prod-scheme.tar.gz.sha256" "$sha_url"
4475
expected=$(cut -d' ' -f1 "$tmpdir/fixtures-prod-scheme.tar.gz.sha256")
4576
actual=$(sha256sum "$tmpdir/fixtures-prod-scheme.tar.gz" | awk '{print $1}')
4677
if [ "$expected" != "$actual" ]; then

.github/workflows/docker_publish.yaml

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
name: Publish Docker Image
22

33
on:
4+
push:
5+
branches: [main]
46
workflow_dispatch:
57
inputs:
68
tags:
@@ -12,21 +14,46 @@ permissions:
1214
contents: read
1315
packages: write
1416

17+
concurrency:
18+
group: ${{ github.workflow }}-${{ github.ref }}
19+
cancel-in-progress: false
20+
1521
env:
1622
REGISTRY: ghcr.io
1723
IMAGE_NAME: ${{ github.repository }}
24+
# On push to main, inputs.tags is empty, so fall back to "unstable" so it
25+
# tracks main. Manual dispatch still honors custom tags.
26+
TAGS_INPUT: ${{ inputs.tags || 'unstable' }}
1827

1928
jobs:
2029
build-image:
21-
name: Build Docker image (${{ matrix.arch }})
30+
name: Build Docker image (${{ matrix.arch.name }}, ${{ matrix.variant.name }})
2231
strategy:
2332
matrix:
24-
include:
33+
arch:
2534
- runner: ubuntu-latest
26-
arch: amd64
35+
name: amd64
2736
- runner: ubuntu-22.04-arm
28-
arch: arm64
29-
runs-on: ${{ matrix.runner }}
37+
name: arm64
38+
# Each tag is published twice: once as the regular image and once as a
39+
# Shadow-simulator-compatible image (tag suffixed "-shadow"). The shadow
40+
# build args mirror the `shadow-docker-build` Makefile target. LOCKED=
41+
# builds the shadow variant unlocked (the quinn-udp [patch] is absent
42+
# from Cargo.lock; see Dockerfile), so -shadow images are not
43+
# reproducible and should not be treated as equivalent-security
44+
# artifacts to the regular tags.
45+
variant:
46+
- name: default
47+
suffix: ""
48+
build_args: ""
49+
- name: shadow
50+
suffix: "-shadow"
51+
build_args: |
52+
SHADOW=1
53+
FEATURES=shadow-integration
54+
NO_DEFAULT_FEATURES=--no-default-features
55+
LOCKED=
56+
runs-on: ${{ matrix.arch.runner }}
3057

3158
steps:
3259
- name: Checkout repository
@@ -44,11 +71,14 @@ jobs:
4471

4572
- name: Prepare tags
4673
id: prep
74+
env:
75+
SUFFIX: ${{ matrix.variant.suffix }}
76+
ARCH: ${{ matrix.arch.name }}
4777
run: |
4878
TAGS=""
49-
IFS=',' read -ra TAG_ARRAY <<< "${{ inputs.tags }}"
79+
IFS=',' read -ra TAG_ARRAY <<< "${TAGS_INPUT}"
5080
for t in "${TAG_ARRAY[@]}"; do
51-
TAGS="${TAGS}${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}-${{ matrix.arch }},"
81+
TAGS="${TAGS}${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${SUFFIX}-${ARCH},"
5282
done
5383
TAGS="${TAGS%,}" # Remove trailing comma
5484
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
@@ -61,11 +91,12 @@ jobs:
6191
build-args: |
6292
GIT_COMMIT=${{ github.sha }}
6393
GIT_BRANCH=${{ github.ref_name }}
94+
${{ matrix.variant.build_args }}
6495
push: true
6596
tags: ${{ steps.prep.outputs.tags }}
66-
platforms: linux/${{ matrix.arch }}
67-
cache-from: type=gha,scope=${{ matrix.arch }}
68-
cache-to: type=gha,scope=${{ matrix.arch }},mode=max
97+
platforms: linux/${{ matrix.arch.name }}
98+
cache-from: type=gha,scope=${{ matrix.arch.name }}-${{ matrix.variant.name }}
99+
cache-to: type=gha,scope=${{ matrix.arch.name }}-${{ matrix.variant.name }},mode=max
69100

70101
publish-manifest:
71102
name: Create and push multi-arch manifest
@@ -84,20 +115,26 @@ jobs:
84115
env:
85116
SHORT_SHA: ${{ github.sha }}
86117
run: |
87-
IFS=',' read -ra TAG_ARRAY <<< "${{ inputs.tags }}"
118+
IFS=',' read -ra TAG_ARRAY <<< "${TAGS_INPUT}"
88119
FIRST_TAG="${TAG_ARRAY[0]}"
120+
SHORT="${SHORT_SHA::7}"
89121
90-
# Create manifest for first tag with SHA tag
91-
docker buildx imagetools create \
92-
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG} \
93-
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${SHORT_SHA::7} \
94-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}-amd64 \
95-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}-arm64
96-
97-
# Create manifests for remaining tags
98-
for t in "${TAG_ARRAY[@]:1}"; do
122+
# Publish each tag twice: the regular image and its "-shadow" twin.
123+
# These suffixes must stay in sync with the `variant` matrix suffixes
124+
# in the build-image job above: a new variant must be added in both.
125+
for suffix in "" "-shadow"; do
126+
# First tag also gets an immutable sha-<sha> tag.
99127
docker buildx imagetools create \
100-
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t} \
101-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}-amd64 \
102-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}-arm64
128+
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}${suffix} \
129+
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${SHORT}${suffix} \
130+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}${suffix}-amd64 \
131+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}${suffix}-arm64
132+
133+
# Remaining tags.
134+
for t in "${TAG_ARRAY[@]:1}"; do
135+
docker buildx imagetools create \
136+
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${suffix} \
137+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${suffix}-amd64 \
138+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${suffix}-arm64
139+
done
103140
done

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ Docker images are published to `ghcr.io/lambdaclass/ethlambda` with the followin
114114
| Tag | Description |
115115
|-----|-------------|
116116
| `devnetX` | Stable image for a specific devnet (e.g. `devnet4`) |
117-
| `latest` | Alias for the stable image of the currently running devnet |
118-
| `unstable` | Development builds; promoted to `devnetX`/`latest` once tested |
117+
| `latest` | Alias for the latest stable image of the currently running devnet |
118+
| `unstable` | Built from the latest main commit; promoted to `devnetX`/`latest` once tested |
119119
| `sha-XXXXXXX` | Specific commit |
120120
121121
[`RELEASE.md`](./RELEASE.md) has more details on our release process and how to tag new images.

RELEASE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ exact commit it was built from.
2121
On top of that, the workflow accepts a comma-separated list of custom tags as a
2222
parameter (e.g. `latest,devnet2`). We use the following tagging convention:
2323

24-
- `unstable` - the latest image built from the `main` branch, without any devnet-specific features
25-
- `latest` - the latest image built for the current devnet (`devnet3` at the time of writing)
26-
- `devnetX` - the latest image built with `devnetX` support (e.g. `devnet3`, `devnet4`)
24+
- `unstable` - the latest image built from the `main` branch
25+
- `latest` - the latest stable image built for the current devnet (`devnet5` at the time of writing)
26+
- `devnetX` - the latest stable image built with `devnetX` support (e.g. `devnet4`, `devnet5`)
2727

2828
Future devnets will introduce new tags, with previous ones left without updates.
2929

3030
### Pulling an image
3131

3232
```bash
3333
docker pull ghcr.io/lambdaclass/ethlambda:unstable # latest from main
34-
docker pull ghcr.io/lambdaclass/ethlambda:devnet3 # devnet3-compatible
34+
docker pull ghcr.io/lambdaclass/ethlambda:devnet5 # devnet5-compatible
3535
docker pull ghcr.io/lambdaclass/ethlambda:sha-12f8377 # pinned to a specific commit
3636
```
3737

bin/ethlambda/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ jemalloc = ["dep:tikv-jemallocator"]
1616
# Shadow simulator compatibility: single-threaded tokio runtime and no jemalloc.
1717
# The quinn-udp UDP fallback is a Cargo `[patch]` (which cannot be feature-gated),
1818
# injected at build time by `shadow/build.sh` / `make shadow-build`.
19-
shadow-integration = []
19+
shadow-integration = ["ethlambda-crypto/shadow-integration"]
2020

2121
[dependencies]
2222
ethlambda-blockchain.workspace = true
23+
ethlambda-crypto.workspace = true
2324
ethlambda-network-api.workspace = true
2425
ethlambda-p2p.workspace = true
2526
ethlambda-types.workspace = true

bin/ethlambda/src/cli.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,46 @@ pub(crate) struct CliOptions {
107107
/// `on_block`.
108108
#[arg(long, default_value = "3")]
109109
pub(crate) max_attestations_per_block: usize,
110+
/// Shadow-simulator sim-cost + fake-XMSS flags (only under the
111+
/// `shadow-integration` feature).
112+
#[cfg(feature = "shadow-integration")]
113+
#[command(flatten)]
114+
pub(crate) shadow: ShadowOptions,
115+
}
116+
117+
/// Shadow-simulator sim-cost + fake-XMSS flags. Compiled only under the
118+
/// `shadow-integration` feature.
119+
#[cfg(feature = "shadow-integration")]
120+
#[derive(Debug, clap::Args)]
121+
pub(crate) struct ShadowOptions {
122+
/// Shadow sim only: replace the XMSS aggregation prover/verifier with a
123+
/// deterministic stub (no leanVM proving/verifying). Off by default.
124+
#[arg(long, default_value = "false")]
125+
pub(crate) shadow_xmss_fake: bool,
126+
127+
/// Shadow sim only: signatures aggregated per second. Injects a sleep of
128+
/// n/rate seconds into aggregation so its CPU cost shows up on Shadow's
129+
/// virtual clock. Unset or <= 0 disables.
130+
#[arg(long)]
131+
pub(crate) shadow_xmss_aggregate_signatures_rate: Option<f64>,
132+
133+
/// Shadow sim only: signatures verified per aggregate per second; injects
134+
/// a sleep of n/rate seconds into verification. Unset or <= 0 disables.
135+
#[arg(long)]
136+
pub(crate) shadow_xmss_verify_aggregated_signatures_rate: Option<f64>,
137+
138+
/// Shadow sim only: Type-1 components merged into a Type-2 per second;
139+
/// injects a sleep of n/rate seconds into the proposal Type-2 merge.
140+
/// Unset or <= 0 disables.
141+
#[arg(long)]
142+
pub(crate) shadow_xmss_merge_rate: Option<f64>,
143+
144+
/// Shadow sim only: byte length of each fake stub proof. Defaults to 32
145+
/// KiB; capped at the 512 KiB on-wire proof limit.
146+
#[arg(
147+
long,
148+
default_value_t = ethlambda_crypto::shadow_cost::DEFAULT_FAKE_PROOF_SIZE as u64,
149+
value_parser = clap::value_parser!(u64).range(1..=524_288)
150+
)]
151+
pub(crate) shadow_xmss_fake_proof_size: u64,
110152
}

0 commit comments

Comments
 (0)