Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 93bd836

Browse files
committed
build: building docker image fulfiling fuzz-proto spec
Standard packaging included as described here: https://github.com/davxy/jam-conformance/tree/main/fuzz-proto#standard-target-packaging
1 parent 8434b28 commit 93bd836

7 files changed

Lines changed: 222 additions & 4 deletions

File tree

Dockerfile

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
############################
4+
# Stage 1: builder
5+
############################
6+
FROM --platform=linux/amd64 golang:1.25.5-bookworm AS builder
7+
8+
ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo
9+
ENV PATH=/usr/local/cargo/bin:$PATH
10+
RUN apt-get update && apt-get install -y --no-install-recommends \
11+
build-essential \
12+
ca-certificates \
13+
curl \
14+
make \
15+
pkg-config \
16+
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
17+
| sh -s -- -y --default-toolchain stable --profile minimal \
18+
&& rm -rf /var/lib/apt/lists/*
19+
20+
WORKDIR /src
21+
22+
COPY go.mod go.sum ./
23+
RUN go mod download
24+
25+
COPY . .
26+
27+
# Validator node binary + both conformance binaries (tiny and full specs).
28+
RUN make build \
29+
&& make build-conformance \
30+
&& make build-conformance-full
31+
32+
# Dev-only ed25519 key pair for the baked test_validators.json (normal mode).
33+
RUN mkdir -p /out && go run docker/gen-validators.go > /out/test_validators.json
34+
35+
############################
36+
# Stage 2: runtime
37+
############################
38+
FROM --platform=linux/amd64 debian:bookworm-slim
39+
40+
RUN apt-get update && apt-get install -y --no-install-recommends \
41+
ca-certificates \
42+
&& rm -rf /var/lib/apt/lists/* \
43+
&& useradd --create-home --uid 1000 strawberry
44+
45+
WORKDIR /app
46+
47+
COPY --from=builder /src/strawberry /app/strawberry
48+
COPY --from=builder /src/pkg/conformance/bin/strawberry /app/strawberry-conformance-tiny
49+
COPY --from=builder /src/pkg/conformance/bin/strawberry-full /app/strawberry-conformance-full
50+
COPY --from=builder /src/appconfig.json /app/appconfig.json
51+
COPY --from=builder /out/test_validators.json /app/test_validators.json
52+
COPY --from=builder /src/docker/entrypoint.sh /app/entrypoint.sh
53+
54+
RUN sed -i 's/"validatorIndex": *[0-9]*/"validatorIndex": 0/' /app/appconfig.json \
55+
&& chmod +x /app/entrypoint.sh \
56+
&& chown -R strawberry:strawberry /app
57+
58+
USER strawberry
59+
EXPOSE 30000/udp
60+
61+
ENTRYPOINT ["/app/entrypoint.sh"]

Makefile

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ endif
2020
BANDERSNATCH_LIB = libbandersnatch.$(LIB_EXT)
2121
ERASURECODING_LIB = liberasurecoding.$(LIB_EXT)
2222

23+
# Strip debug symbols and DWARF tables from release binaries.
24+
# Override with `make build LDFLAGS=...` if you need symbols for debugging.
25+
LDFLAGS ?= -s -w
26+
2327
all: help
2428

2529
.PHONY: help
@@ -80,13 +84,19 @@ install-hooks:
8084

8185
.PHONY: build
8286
build: build-bandersnatch build-erasurecoding
83-
GOOS=${GOOS} GOARCH=${GOARCH} go build -o strawberry ./cmd/strawberry
87+
GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags="$(LDFLAGS)" -o strawberry ./cmd/strawberry
8488

8589
.PHONY: build-conformance
86-
## build-conformance: Builds the conformance tool
90+
## build-conformance: Builds the conformance tool with the tiny spec
8791
build-conformance: build-bandersnatch build-erasurecoding
8892
mkdir -p pkg/conformance/bin
89-
go build -tags="tiny" -o pkg/conformance/bin/strawberry ./pkg/conformance/cmd/main.go
93+
go build -tags="tiny" -ldflags="$(LDFLAGS)" -o pkg/conformance/bin/strawberry ./pkg/conformance/cmd/main.go
94+
95+
.PHONY: build-conformance-full
96+
## build-conformance-full: Builds the conformance tool with the full spec
97+
build-conformance-full: build-bandersnatch build-erasurecoding
98+
mkdir -p pkg/conformance/bin
99+
go build -ldflags="$(LDFLAGS)" -o pkg/conformance/bin/strawberry-full ./pkg/conformance/cmd/main.go
90100

91101
.PHONY: test-conformance
92102
## test-conformance: Runs conformance tests

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,50 @@ This returns:
6868
```
6969
Meaning that the block is being validated.
7070
71+
## Docker
72+
73+
A single `linux/amd64` image runs in two modes, selected by the `JAM_FUZZ`
74+
environment variable. The image follows the [JAM standard target packaging
75+
spec](https://github.com/davxy/jam-conformance/tree/main/fuzz-proto#standard-target-packaging).
76+
77+
Build:
78+
79+
```bash
80+
docker build -t strawberry .
81+
```
82+
83+
**Normal mode** — validator node listening on UDP 30000 with baked-in dev configs:
84+
85+
```bash
86+
docker run --rm -p 30000:30000/udp strawberry
87+
```
88+
89+
To use your own keys/config, mount over the baked-in files:
90+
91+
```bash
92+
docker run --rm -p 30000:30000/udp \
93+
-v "$PWD/appconfig.json:/app/appconfig.json:ro" \
94+
-v "$PWD/test_validators.json:/app/test_validators.json:ro" \
95+
strawberry
96+
```
97+
98+
**Fuzz mode** — JAM conformance target speaking the fuzz protocol over a Unix socket:
99+
100+
```bash
101+
docker run --rm \
102+
-e JAM_FUZZ=1 \
103+
-e JAM_FUZZ_SPEC=tiny \
104+
-e JAM_FUZZ_DATA_PATH=/tmp/jam/data \
105+
-e JAM_FUZZ_SOCK_PATH=/tmp/jam/fuzz.sock \
106+
-e JAM_FUZZ_LOG_LEVEL=info \
107+
-v /tmp/jam:/tmp/jam \
108+
strawberry
109+
```
110+
111+
`JAM_FUZZ_SPEC` accepts `tiny` or `full`.
112+
113+
> The image bakes a freshly-generated ed25519 keypair for local dev. **Do not use it in production** — generate your own `test_validators.json`.
114+
71115
## Run tests
72116

73117
### Unit tests

docker/entrypoint.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/sh
2+
# Dispatch between strawberry's normal validator-node mode and the JAM fuzz
3+
# conformance target mode, per the standard target packaging spec:
4+
# https://github.com/davxy/jam-conformance/tree/main/fuzz-proto#standard-target-packaging
5+
6+
set -eu
7+
8+
if [ -z "${JAM_FUZZ:-}" ]; then
9+
# Normal mode: run the validator node from /app using baked configs.
10+
cd /app
11+
exec /app/strawberry "$@"
12+
fi
13+
14+
# Fuzz mode: required env vars per spec.
15+
missing=
16+
for v in JAM_FUZZ_SPEC JAM_FUZZ_DATA_PATH JAM_FUZZ_SOCK_PATH; do
17+
eval "val=\${$v:-}"
18+
if [ -z "$val" ]; then
19+
missing="$missing $v"
20+
fi
21+
done
22+
if [ -n "$missing" ]; then
23+
echo "JAM_FUZZ is set but required variable(s) missing:$missing" >&2
24+
exit 1
25+
fi
26+
27+
case "$JAM_FUZZ_SPEC" in
28+
tiny) bin=/app/strawberry-conformance-tiny ;;
29+
full) bin=/app/strawberry-conformance-full ;;
30+
*)
31+
echo "JAM_FUZZ_SPEC must be 'tiny' or 'full', got: $JAM_FUZZ_SPEC" >&2
32+
exit 1
33+
;;
34+
esac
35+
36+
mkdir -p "$JAM_FUZZ_DATA_PATH"
37+
cd "$JAM_FUZZ_DATA_PATH"
38+
39+
# The conformance binary reads JAM_FUZZ_SOCK_PATH and JAM_FUZZ_LOG_LEVEL from
40+
# its environment; passing them through implicitly.
41+
exec "$bin" "$@"

docker/gen-validators.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build ignore
2+
3+
package main
4+
5+
import (
6+
"crypto/ed25519"
7+
"encoding/hex"
8+
"encoding/json"
9+
"fmt"
10+
"os"
11+
)
12+
13+
// Dev-only generator. Emits a single-validator JSON file matching the
14+
// FullValidatorInfo schema consumed by cmd/strawberry/main.go.
15+
// NEVER use the resulting keys in production.
16+
func main() {
17+
pub, prv, err := ed25519.GenerateKey(nil)
18+
if err != nil {
19+
fmt.Fprintln(os.Stderr, err)
20+
os.Exit(1)
21+
}
22+
out := []map[string]any{{
23+
"index": 0,
24+
"address": "::",
25+
"port": 30000,
26+
"ed25519_public_key": hex.EncodeToString(pub),
27+
"ed25519_private_key": hex.EncodeToString(prv),
28+
}}
29+
enc := json.NewEncoder(os.Stdout)
30+
enc.SetIndent("", "\t")
31+
if err := enc.Encode(out); err != nil {
32+
fmt.Fprintln(os.Stderr, err)
33+
os.Exit(1)
34+
}
35+
}

pkg/conformance/cmd/main.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@ import (
55
"log"
66
"net/http"
77
_ "net/http/pprof"
8+
"os"
89

910
"github.com/eigerco/strawberry/pkg/conformance"
11+
pkglog "github.com/eigerco/strawberry/pkg/log"
1012
)
1113

1214
func main() {
13-
socketPath := flag.String("socket", "/tmp/jam_target.sock", "Path to the socket for the fuzzer to connect to")
15+
defaultSocket := "/tmp/jam_target.sock"
16+
if v := os.Getenv("JAM_FUZZ_SOCK_PATH"); v != "" {
17+
defaultSocket = v
18+
}
19+
20+
socketPath := flag.String("socket", defaultSocket, "Path to the socket for the fuzzer to connect to (overrides JAM_FUZZ_SOCK_PATH)")
1421
pprofAddr := flag.String("pprof", "", "Address for pprof HTTP server (e.g., localhost:6060)")
1522
flag.Parse()
1623

@@ -19,6 +26,14 @@ func main() {
1926
log.Fatalf("unexpected arguments: %v", flag.Args())
2027
}
2128

29+
if lvl := os.Getenv("JAM_FUZZ_LOG_LEVEL"); lvl != "" {
30+
parsed, err := pkglog.ParseLogLevel(lvl)
31+
if err != nil {
32+
log.Fatalf("invalid JAM_FUZZ_LOG_LEVEL %q: %v", lvl, err)
33+
}
34+
pkglog.Init(pkglog.Options{LogLevel: parsed})
35+
}
36+
2237
// Start pprof server if address provided
2338
if *pprofAddr != "" {
2439
go func() {

pkg/conformance/node.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ const (
3333
FeatureReserved Features = 2147483648
3434
)
3535

36+
// errCloseSession signals that the connection must be closed without sending
37+
// a response. Used for protocol violations the fuzz spec says should drop the
38+
// session (e.g. a second Initialize within one session).
39+
var errCloseSession = errors.New("close session")
40+
3641
// Node is a conformance testing node complying with fuzzer protocol https://github.com/davxy/jam-stuff/tree/main/fuzz-proto
3742
// opens a connection via a unix socket and listens to fuzzer messages and updates the state accordingly
3843
type Node struct {
@@ -147,6 +152,10 @@ func (n *Node) handleConnection(conn net.Conn) {
147152
}
148153

149154
responseMsg, err := n.messageHandler(msg)
155+
if errors.Is(err, errCloseSession) {
156+
log.Printf("closing session: %v", err)
157+
return
158+
}
150159
if err != nil {
151160
responseMsg = NewMessage(Error{Message: []byte(fmt.Sprintf("Chain error: %s", err.Error()))})
152161
}
@@ -185,6 +194,9 @@ func (n *Node) messageHandler(msg *Message) (*Message, error) {
185194
}
186195
switch choice := msg.Get().(type) {
187196
case Initialize:
197+
if n.mainChainHead != nil {
198+
return nil, fmt.Errorf("%w: second Initialize received within session", errCloseSession)
199+
}
188200
// Initialize state
189201
state, err := deserializeState(choice.State.StateItems)
190202
if err != nil {

0 commit comments

Comments
 (0)