From 90b53b9e0140b5a2eb4612821f54e5e21447b342 Mon Sep 17 00:00:00 2001 From: "fletcher.fan" Date: Tue, 15 Sep 2026 09:40:49 +0800 Subject: [PATCH 1/6] build(node): add reproducible build target for morphnode `make morphnode` embeds the wall-clock build time, so the same source tree produces a different binary on every build. Add `make morphnode-repro`, which builds the same binary but byte-identically: - BuildTime becomes the constant "reproducible"; a reproducible artifact has no meaningful build time, and reusing the commit time here would misreport what BuildTime means - -buildid= drops the Go build ID, which varies with the build cache state - -trimpath drops absolute checkout paths - -no_uuid drops the random LC_UUID that Apple's linker stamps into every Mach-O binary (Linux's GNU build-id is already content-derived) Without -no_uuid the binaries still differed, in 47 bytes: the UUID itself plus the ad-hoc code signature covering it. `make morphnode` is deliberately left untouched, so its BuildTime keeps reporting when the binary was actually built. Co-authored-by: Cursor --- node/Makefile | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/node/Makefile b/node/Makefile index acd835766..d0c6616df 100644 --- a/node/Makefile +++ b/node/Makefile @@ -3,27 +3,52 @@ GITDATE := $(shell git show -s --format='%ct') VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) +# Set by the morphnode-repro target below. Strips everything that varies between +# two builds of the same source tree, so the binary is byte-identical: +# - the build timestamp, which has no meaning for a reproducible artifact +# - the Go build ID, which depends on the state of the build cache +# - absolute checkout paths, via -trimpath +# - the random LC_UUID that Apple's linker stamps into every Mach-O binary +# (on Linux the equivalent GNU build-id is derived from content already) +ifdef REPRO +BUILD_TIME := reproducible +BUILDFLAGS += -trimpath +LDFLAGSSTRING +=-buildid= +ifeq ($(shell uname -s),Darwin) +LDFLAGSSTRING +=-extldflags=-Wl,-no_uuid +endif +endif + LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT) LDFLAGSSTRING +=-X main.GitDate=$(GITDATE) LDFLAGSSTRING +=-X main.Version=$(VERSION) LDFLAGSSTRING +=-X main.BuildTime=$(BUILD_TIME) LDFLAGS := -ldflags "$(LDFLAGSSTRING)" +BUILDFLAGS += $(LDFLAGS) morphnode: if [ ! -d build/bin ]; then mkdir -p build/bin; fi go mod download - env GO111MODULE=on CGO_ENABLED=1 CGO_LDFLAGS="-ldl" go build -o build/bin/morphnode -v $(LDFLAGS) ./cmd/node + env GO111MODULE=on CGO_ENABLED=1 CGO_LDFLAGS="-ldl" go build -o build/bin/morphnode -v $(BUILDFLAGS) ./cmd/node .PHONY: morphnode tendermint: if [ ! -d build/bin ]; then mkdir -p build/bin; fi go mod download - env GO111MODULE=on CGO_ENABLED=1 go build -o build/bin/tendermint -v $(LDFLAGS) ./cmd/tendermint + env GO111MODULE=on CGO_ENABLED=1 go build -o build/bin/tendermint -v $(BUILDFLAGS) ./cmd/tendermint .PHONY: tendermint build: morphnode tendermint .PHONY: build +# Same binary as `make morphnode`, but byte-identical across builds of an +# identical source tree. This still requires a fixed toolchain: morphnode is +# built with cgo, so the Go version and the C toolchain must match too. Build +# inside ops/docker/Dockerfile.l2-node to guarantee that across machines. +morphnode-repro: + $(MAKE) morphnode REPRO=1 +.PHONY: morphnode-repro + init: if [ -d build/config ]; then exit 0; fi if [ ! -d build ]; then mkdir -p build; fi @@ -94,7 +119,7 @@ testnet-clean: testnet-down install-tendermint: if [ ! -d build/bin ]; then mkdir -p build/bin; fi go mod download - env GO111MODULE=on CGO_ENABLED=1 go install -v $(LDFLAGS) ./cmd/tendermint + env GO111MODULE=on CGO_ENABLED=1 go install -v $(BUILDFLAGS) ./cmd/tendermint .PHONY: tendermint From 3cd3795012747d4ff682d2782168fe3cf83aefb1 Mon Sep 17 00:00:00 2001 From: "marvel.yu" Date: Tue, 15 Sep 2026 17:23:05 +0800 Subject: [PATCH 2/6] add print sha256 --- MakefileEc2.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/MakefileEc2.mk b/MakefileEc2.mk index 92832247d..3d91b862e 100644 --- a/MakefileEc2.mk +++ b/MakefileEc2.mk @@ -11,6 +11,7 @@ build-bk-prod-morph-prod-mainnet-to-morph-node: if [ ! -d dist ]; then mkdir -p dist; fi cd $(PWD)/node && make build cp node/build/bin/morphnode dist/ + sha256sum dist/morphnode cp node/build/bin/tendermint dist/ tar -czvf morph-node.tar.gz dist aws s3 cp morph-node.tar.gz s3://morph-0582-morph-technical-department-mainnet-data/morph-setup/morph-node.tar.gz From 528967113f429ad72a1944d468a6092955c25053 Mon Sep 17 00:00:00 2001 From: "marvel.yu" Date: Tue, 15 Sep 2026 17:40:27 +0800 Subject: [PATCH 3/6] use make morphnode-repro --- MakefileEc2.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MakefileEc2.mk b/MakefileEc2.mk index 3d91b862e..2481e1a73 100644 --- a/MakefileEc2.mk +++ b/MakefileEc2.mk @@ -9,10 +9,9 @@ LDFLAGS := -ldflags "$(LDFLAGSSTRING)" build-bk-prod-morph-prod-mainnet-to-morph-node: if [ ! -d dist ]; then mkdir -p dist; fi - cd $(PWD)/node && make build + cd $(PWD)/node && make morphnode-repro cp node/build/bin/morphnode dist/ sha256sum dist/morphnode - cp node/build/bin/tendermint dist/ tar -czvf morph-node.tar.gz dist aws s3 cp morph-node.tar.gz s3://morph-0582-morph-technical-department-mainnet-data/morph-setup/morph-node.tar.gz From caaf822b9ddcd14cf8791a142cd9a8249a4160cf Mon Sep 17 00:00:00 2001 From: "fletcher.fan" Date: Tue, 15 Sep 2026 18:00:27 +0800 Subject: [PATCH 4/6] build(node): also strip VCS stamps from the reproducible build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `go build` embeds vcs.revision, vcs.time and vcs.modified. They are stable for a fixed commit, but vcs.modified flips with a dirty tree and the whole block is silently omitted when .git is absent — so the same source can still produce different binaries depending on how the build environment is set up. Docker builds that exclude .git hit exactly this. Version and GitCommit already carry the same information, so drop the stamps. Co-authored-by: Cursor --- node/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/node/Makefile b/node/Makefile index d0c6616df..3a8456834 100644 --- a/node/Makefile +++ b/node/Makefile @@ -8,11 +8,13 @@ BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) # - the build timestamp, which has no meaning for a reproducible artifact # - the Go build ID, which depends on the state of the build cache # - absolute checkout paths, via -trimpath +# - the VCS stamps, which depend on whether .git is present and whether the +# tree is dirty; Version and GitCommit already carry that information # - the random LC_UUID that Apple's linker stamps into every Mach-O binary # (on Linux the equivalent GNU build-id is derived from content already) ifdef REPRO BUILD_TIME := reproducible -BUILDFLAGS += -trimpath +BUILDFLAGS += -trimpath -buildvcs=false LDFLAGSSTRING +=-buildid= ifeq ($(shell uname -s),Darwin) LDFLAGSSTRING +=-extldflags=-Wl,-no_uuid From 83e04322f1a6fc7b7ba043dff6a39569f63c2e1c Mon Sep 17 00:00:00 2001 From: "fletcher.fan" Date: Tue, 15 Sep 2026 18:20:32 +0800 Subject: [PATCH 5/6] specified git short as 8 --- node/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Makefile b/node/Makefile index 3a8456834..1756d21a3 100644 --- a/node/Makefile +++ b/node/Makefile @@ -1,4 +1,4 @@ -GITCOMMIT := $(shell git rev-parse --short HEAD) +GITCOMMIT := $(shell git rev-parse --short=8 HEAD) GITDATE := $(shell git show -s --format='%ct') VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) From c9085948ce6638c29504e17965f3dbc9374e4e63 Mon Sep 17 00:00:00 2001 From: "marvel.yu" Date: Tue, 15 Sep 2026 18:31:20 +0800 Subject: [PATCH 6/6] specified git short as 8 --- node/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/Makefile b/node/Makefile index 1756d21a3..3213c4c39 100644 --- a/node/Makefile +++ b/node/Makefile @@ -1,6 +1,6 @@ GITCOMMIT := $(shell git rev-parse --short=8 HEAD) GITDATE := $(shell git show -s --format='%ct') -VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +VERSION ?= $(shell git describe --tags --always --dirty --abbrev=8 2>/dev/null || echo "dev") BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) # Set by the morphnode-repro target below. Strips everything that varies between