Skip to content

Commit 3909909

Browse files
authored
Merge branch 'main' into docs/spec-deviations-page
2 parents af6e5df + 948353f commit 3909909

61 files changed

Lines changed: 5660 additions & 1191 deletions

Some content is hidden

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

.claude/skills/devnet-log-review/SKILL.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ grep "signature verification failed" lantern_0.log
145145

146146
### Finalization Debugging
147147

148-
Finalization should advance every 6-12 slots. If it stalls, investigate:
148+
On a healthy devnet finalization advances every few slots. If it stalls, investigate:
149149

150150
```bash
151151
# Check finalization progress
@@ -154,9 +154,11 @@ grep "finalized_slot=" ethlambda_0.log | tail -20
154154
# If finalized_slot stays same for 50+ slots → finalization stalled
155155
```
156156

157-
**Finalization requires >2/3 supermajority:**
158-
- 6 validators → need 5 votes minimum
159-
- 9 validators → need 7 votes minimum
157+
**Justification requires a ≥2/3 supermajority** (`3 * votes >= 2 * validator_count`,
158+
i.e. `ceil(2N/3)` — see `crates/blockchain/state_transition/src/lib.rs`):
159+
- 6 validators → need 4 votes minimum
160+
- 9 validators → need 6 votes minimum
161+
- 16 validators → need 11 votes minimum
160162

161163
**See [references/FINALIZATION_DEBUG.md](references/FINALIZATION_DEBUG.md) for:**
162164
- Common causes of finalization stalls
@@ -184,15 +186,15 @@ Different clients have different log formats and key patterns.
184186

185187
## Block Proposal Flow (ethlambda)
186188

187-
A healthy block proposal follows this sequence:
189+
Since the pre-build change (#445), the proposer builds at the *previous* slot's
190+
interval 4 and publishes aligned to the slot boundary. A healthy block proposal
191+
follows this sequence:
188192

189-
1. `We are the proposer for this slot` - Node detects it's the proposer
190-
2. `TODO precompute poseidons in parallel + SIMD` - XMSS aggregate proof starts
191-
3. `packed_pcs_commit` - Proof commitment
192-
4. `Logup data` - Logup protocol data
193-
5. `AIR proof{table=poseidon16}` / `AIR proof{table=poseidon24}` - AIR proofs
194-
6. `Published block` - Block successfully built and published
195-
7. `Published block to gossipsub` - Block broadcast to network
193+
1. `We are the proposer for this slot` - Node detects it's the proposer (fires one slot early)
194+
2. leanVM proving output (`packed_pcs_commit`, `Logup data`, `AIR proof{table=poseidon16|poseidon24}`) - XMSS aggregate proof
195+
3. `Finished building block` - Build complete (#474; this is where the build-time metric stops)
196+
4. `Published block` - Published at the slot boundary (the gap to step 3 is idle wait, not build cost)
197+
5. `Published block to gossipsub` - Block broadcast to network
196198

197199
## Summary Report Format
198200

.claude/skills/test-pr-devnet/SKILL.md

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Test ethlambda branch changes in a multi-client local devnet with zeam (Zig), re
5353
**Success criteria:**
5454
- ✅ No errors in ethlambda logs
5555
- ✅ All 4 nodes at same head slot
56-
- ✅ Finalization advancing (every 6-12 slots)
56+
- ✅ Finalization advancing (every few slots on a healthy devnet)
5757
- ✅ Each validator produces blocks for their slots
5858

5959
### Sync Recovery (~90-120s)
@@ -139,25 +139,13 @@ sleep 10 # Wait for sync
139139
# Quick status
140140
.claude/skills/test-pr-devnet/scripts/check-status.sh
141141
142-
# Detailed analysis (use devnet-log-review skill in lean-quickstart)
143-
cd $LEAN_QUICKSTART
142+
# Detailed analysis (use this repo's devnet-log-review skill; dump logs first)
143+
for node in zeam_0 ream_0 qlean_0 ethlambda_0; do
144+
docker logs "$node" > "${node}.log" 2>&1
145+
done
144146
.claude/skills/devnet-log-review/scripts/analyze-logs.sh
145147
```
146148
147-
## Protocol Compatibility
148-
149-
| Client | Status | Gossipsub | BlocksByRoot |
150-
|--------|--------|-----------|--------------|
151-
| ream | ✅ Full | ✅ Full | ✅ Full |
152-
| zeam | ✅ Full | ✅ Full | ⚠️ Limited |
153-
| qlean | ✅ Full | ✅ Full | ⚠️ Limited |
154-
| ethlambda | ✅ Full | ✅ Full | ✅ Full |
155-
156-
**Notes:**
157-
- zeam/qlean BlocksByRoot errors are expected (not a blocker)
158-
- ream ↔ ethlambda BlocksByRoot should work perfectly
159-
- All clients use Gossipsub for block propagation
160-
161149
## Verification Checklist
162150
163151
| Check | Command | Expected |
@@ -256,5 +244,5 @@ docker logs ethlambda_0 2>&1 | grep -i "peer\|connection" | head -20
256244
257245
## References
258246
259-
- **[ethlambda CLAUDE.md](../../CLAUDE.md)** - Development workflow, detailed debugging commands
260-
- **[lean-quickstart devnet-log-review](../../../lean-quickstart/.claude/skills/devnet-log-review/SKILL.md)** - Comprehensive log analysis
247+
- **[ethlambda CLAUDE.md](../../../CLAUDE.md)** - Development workflow, detailed debugging commands
248+
- **[devnet-log-review](../devnet-log-review/SKILL.md)** - Comprehensive log analysis (in this repo)

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@
2323

2424
- [ ] Ran `make fmt` — clean
2525
- [ ] Ran `make lint` (clippy with `-D warnings`) — clean
26-
- [ ] Ran `cargo test --workspace --release` — all passing
26+
- [ ] Ran `make test` (`cargo test --workspace --profile release-fast`) — all passing

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

Lines changed: 38 additions & 7 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
@@ -73,7 +104,7 @@ runs:
73104
- name: Setup Rust
74105
uses: dtolnay/rust-toolchain@master
75106
with:
76-
toolchain: "1.92.0"
107+
toolchain: "1.97.1"
77108

78109
- name: Setup cache
79110
uses: Swatinem/rust-cache@v2

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- name: Setup Rust
2727
uses: dtolnay/rust-toolchain@master
2828
with:
29-
toolchain: "1.92.0"
29+
toolchain: "1.97.1"
3030
components: rustfmt, clippy
3131

3232
- name: Setup cache

.github/workflows/daily_loc_report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: Setup Rust
4040
uses: dtolnay/rust-toolchain@master
4141
with:
42-
toolchain: "1.92.0"
42+
toolchain: "1.97.1"
4343

4444
- name: Setup cache
4545
uses: Swatinem/rust-cache@v2

.github/workflows/docker_publish.yaml

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,33 @@ env:
2727

2828
jobs:
2929
build-image:
30-
name: Build Docker image (${{ matrix.arch }})
30+
name: Build Docker image (${{ matrix.arch.name }}, ${{ matrix.variant.name }})
3131
strategy:
3232
matrix:
33-
include:
33+
arch:
3434
- runner: ubuntu-latest
35-
arch: amd64
35+
name: amd64
3636
- runner: ubuntu-22.04-arm
37-
arch: arm64
38-
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 }}
3957

4058
steps:
4159
- name: Checkout repository
@@ -53,11 +71,14 @@ jobs:
5371

5472
- name: Prepare tags
5573
id: prep
74+
env:
75+
SUFFIX: ${{ matrix.variant.suffix }}
76+
ARCH: ${{ matrix.arch.name }}
5677
run: |
5778
TAGS=""
5879
IFS=',' read -ra TAG_ARRAY <<< "${TAGS_INPUT}"
5980
for t in "${TAG_ARRAY[@]}"; do
60-
TAGS="${TAGS}${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}-${{ matrix.arch }},"
81+
TAGS="${TAGS}${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}${SUFFIX}-${ARCH},"
6182
done
6283
TAGS="${TAGS%,}" # Remove trailing comma
6384
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
@@ -70,11 +91,12 @@ jobs:
7091
build-args: |
7192
GIT_COMMIT=${{ github.sha }}
7293
GIT_BRANCH=${{ github.ref_name }}
94+
${{ matrix.variant.build_args }}
7395
push: true
7496
tags: ${{ steps.prep.outputs.tags }}
75-
platforms: linux/${{ matrix.arch }}
76-
cache-from: type=gha,scope=${{ matrix.arch }}
77-
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
78100

79101
publish-manifest:
80102
name: Create and push multi-arch manifest
@@ -95,18 +117,24 @@ jobs:
95117
run: |
96118
IFS=',' read -ra TAG_ARRAY <<< "${TAGS_INPUT}"
97119
FIRST_TAG="${TAG_ARRAY[0]}"
120+
SHORT="${SHORT_SHA::7}"
98121
99-
# Create manifest for first tag with SHA tag
100-
docker buildx imagetools create \
101-
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG} \
102-
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${SHORT_SHA::7} \
103-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}-amd64 \
104-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${FIRST_TAG}-arm64
105-
106-
# Create manifests for remaining tags
107-
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.
108127
docker buildx imagetools create \
109-
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t} \
110-
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${t}-amd64 \
111-
${{ 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
112140
done

0 commit comments

Comments
 (0)