From b05fe4aa6453520cfc5a664318b6b3e7d1855f46 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 15 Sep 2025 19:47:41 +0200 Subject: [PATCH] cmd/keeper: add the keeper zkvm guest program (#32543) Keeper is a zmvm guest program that runs the block transition. It relies on the zkvm maker implementing `getInput`. For now, we only provide a single implementation for the 'ziren' VM. Why keeper? In the _Mass Effect_ lore, the keepers are animals (?) who maintain the citadel. Nothing is known from them, and attempts at tampering with them have failed, as they self-destruct upon inquiry. They have a secret, nefarious purpose that is only revealed later in the game series, don't want any spoilers so I didn't dig deeper. All in all, a good metaphor for zkvms. --------- Co-authored-by: weilzkm <140377101+weilzkm@users.noreply.github.com> Co-authored-by: Felix Lange --- build/ci.go | 49 ++++++++-- cmd/keeper/1192c3_block.rlp | Bin 0 -> 1368 bytes cmd/keeper/1192c3_witness.rlp | Bin 0 -> 40843 bytes cmd/keeper/README.md | 69 ++++++++++++++ cmd/keeper/chainconfig.go | 38 ++++++++ cmd/keeper/getpayload_example.go | 102 +++++++++++++++++++++ cmd/keeper/getpayload_ziren.go | 31 +++++++ cmd/keeper/go.mod | 48 ++++++++++ cmd/keeper/go.sum | 148 +++++++++++++++++++++++++++++++ cmd/keeper/main.go | 63 +++++++++++++ cmd/keeper/stubs.go | 26 ++++++ go.work | 6 ++ 12 files changed, 573 insertions(+), 7 deletions(-) create mode 100644 cmd/keeper/1192c3_block.rlp create mode 100644 cmd/keeper/1192c3_witness.rlp create mode 100644 cmd/keeper/README.md create mode 100644 cmd/keeper/chainconfig.go create mode 100644 cmd/keeper/getpayload_example.go create mode 100644 cmd/keeper/getpayload_ziren.go create mode 100644 cmd/keeper/go.mod create mode 100644 cmd/keeper/go.sum create mode 100644 cmd/keeper/main.go create mode 100644 cmd/keeper/stubs.go create mode 100644 go.work diff --git a/build/ci.go b/build/ci.go index 3856f32925..6a9848876d 100644 --- a/build/ci.go +++ b/build/ci.go @@ -322,9 +322,9 @@ func doTest(cmdline []string) { gotest.Args = append(gotest.Args, "-short") } - packages := []string{"./..."} - if len(flag.CommandLine.Args()) > 0 { - packages = flag.CommandLine.Args() + packages := flag.CommandLine.Args() + if len(packages) == 0 { + packages = workspacePackagePatterns() } gotest.Args = append(gotest.Args, packages...) build.MustRun(gotest) @@ -364,7 +364,7 @@ func doCheckGenerate() { protocPath = downloadProtoc(*cachedir) protocGenGoPath = downloadProtocGenGo(*cachedir) ) - c := tc.Go("generate", "./...") + c := tc.Go("generate", workspacePackagePatterns()...) pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")} c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator))) build.MustRun(c) @@ -424,9 +424,16 @@ func doLint(cmdline []string) { cachedir = flag.String("cachedir", "./build/cache", "directory for caching golangci-lint binary.") ) flag.CommandLine.Parse(cmdline) - packages := []string{"./..."} - if len(flag.CommandLine.Args()) > 0 { - packages = flag.CommandLine.Args() + + packages := flag.CommandLine.Args() + if len(packages) == 0 { + // Get module directories in workspace. + packages = []string{"./..."} + modules := workspaceModules() + for _, m := range modules[1:] { + dir := strings.TrimPrefix(m, modules[0]) + packages = append(packages, "."+dir+"/...") + } } linter := downloadLinter(*cachedir) @@ -1169,3 +1176,31 @@ func doSanityCheck() { csdb := download.MustLoadChecksums("build/checksums.txt") csdb.DownloadAndVerifyAll() } + +// workspaceModules lists the module paths in the current work. +func workspaceModules() []string { + listing, err := new(build.GoToolchain).Go("list", "-m").Output() + if err != nil { + log.Fatalf("go list failed:", err) + } + var modules []string + for _, m := range bytes.Split(listing, []byte("\n")) { + m = bytes.TrimSpace(m) + if len(m) > 0 { + modules = append(modules, string(m)) + } + } + if len(modules) == 0 { + panic("no modules found") + } + return modules +} + +func workspacePackagePatterns() []string { + modules := workspaceModules() + patterns := make([]string, len(modules)) + for i, m := range modules { + patterns[i] = m + "/..." + } + return patterns +} diff --git a/cmd/keeper/1192c3_block.rlp b/cmd/keeper/1192c3_block.rlp new file mode 100644 index 0000000000000000000000000000000000000000..e788c2b8fdc08b6e4fab78b51e1e541c62c822e2 GIT binary patch literal 1368 zcmey#8v2u|VS%@n*`7#Slhn}Ssa}=+s*MgCE~`xvwPal?U2+&dmy zwYqg{`nEGyq%I4&N>Ax35&p2y>950;$?j7McQ)==xAx`w5443Zd^exu5Y{MP zQu@dIz<=e(xBs8KeB64$s})B$W-gO@I!*Na%+{}uB>np@ZLL+eerA0y-T1|9q18JX z8Adv2XcnAwxP^IgJ3~_tM@zM!AL`7x;P9Fo*Hu5weNcF0 z@3lkPK~vLyHS0`0&{pg$cA0;Y?j7@HCI*Iv1Wh`z)C{Q@SE(N9d0^J7zKe znBL6My`h<9r%Q|VtWyjvJHMn)0{UnQ!;PIU!}p!xtXSEh&vW&8+1Z+Cg@%R$U_}oX zFs|61q0zKreMX_mv131Vn0E`L6&9{K##5eJb8Bb7&4&#O=Dz**)V&~K8}s$|dY%o2 zrR?@ng0364ZH=3KZxvsJTkwLwLwo)>bgI8AzT@}SOIy}1m$g6JOwi!$eX~761zi!- z4*X;?c+|oup8B9!Xwu~=WlEKNLG~Zt$M52}|6+BjyS_oOZ?mEDX0VcnKqXgjC~-># zD|rM|autUXjx?~6$3P|5uq!e6WCT|71gPXXb|r=e@?a%Tfl6**S7Pu>5Uk`GP{~c~ zN(>u5fR#K4D!GMSiNX3%u#y)*CAYCFF_f?eD|rc2atDW!FiWtKS3o6qr(Ac-iNDrf za-Az)bi((o^6m2^brqTo??6m>4ODU;yAmVqMzASwfJz=s@l^K^|7OLNSzsk^fl6LYaVfrYcYk^MtQ70pslI#LuGRM%eQGwc>HsTw2UPMJhZ6mr QU?uN?O5R{sqW0Ym02GW4_y7O^ literal 0 HcmV?d00001 diff --git a/cmd/keeper/1192c3_witness.rlp b/cmd/keeper/1192c3_witness.rlp new file mode 100644 index 0000000000000000000000000000000000000000..4990f2299f9e71ecc36283953af6d68555627b66 GIT binary patch literal 40843 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 H1?~Pn*?i0# literal 0 HcmV?d00001 diff --git a/cmd/keeper/README.md b/cmd/keeper/README.md new file mode 100644 index 0000000000..4737a22674 --- /dev/null +++ b/cmd/keeper/README.md @@ -0,0 +1,69 @@ +# Keeper - geth as a zkvm guest + +Keeper command is a specialized tool for validating stateless execution of Ethereum blocks. It's designed to run as a zkvm guest. + +## Overview + +The keeper reads an RLP-encoded payload containing: +- A block to execute +- A witness with the necessary state data +- A chainID + +It then executes the block statelessly and validates that the computed state root and receipt root match the values in the block header. + +## Building Keeper + +The keeper uses build tags to compile platform-specific input methods and chain configurations: + +### Example Implementation + +See `getpayload_example.go` for a complete example with embedded Hoodi block data: + +```bash +# Build example with different chain configurations +go build -tags "example" ./cmd/keeper +``` + +### Ziren zkVM Implementation + +Build for the Ziren zkVM platform, which is a MIPS ISA-based zkvm: + +```bash +GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -tags "ziren" ./cmd/keeper +``` + +As an example runner, refer to https://gist.github.com/gballet/7b669a99eb3ab2b593324e3a76abd23d + +## Creating a Custom Platform Implementation + +To add support for a new platform (e.g., "myplatform"), create a new file with the appropriate build tag: + +### 1. Create `getinput_myplatform.go` + +```go +//go:build myplatform + +package main + +import ( + "github.com/ethereum/go-ethereum/params" + // ... other imports as needed +) + +// getInput returns the RLP-encoded payload +func getInput() []byte { + // Your platform-specific code to retrieve the RLP-encoded payload + // This might read from: + // - Memory-mapped I/O + // - Hardware registers + // - Serial port + // - Network interface + // - File system + + // The payload must be RLP-encoded and contain: + // - Block with transactions + // - Witness with parent headers and state data + + return encodedPayload +} +``` diff --git a/cmd/keeper/chainconfig.go b/cmd/keeper/chainconfig.go new file mode 100644 index 0000000000..c9859d450f --- /dev/null +++ b/cmd/keeper/chainconfig.go @@ -0,0 +1,38 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/params" +) + +// getChainConfig returns the appropriate chain configuration based on the chainID. +// Returns an error for unsupported chain IDs. +func getChainConfig(chainID uint64) (*params.ChainConfig, error) { + switch chainID { + case 0, params.MainnetChainConfig.ChainID.Uint64(): + return params.MainnetChainConfig, nil + case params.SepoliaChainConfig.ChainID.Uint64(): + return params.SepoliaChainConfig, nil + case params.HoodiChainConfig.ChainID.Uint64(): + return params.HoodiChainConfig, nil + default: + return nil, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} diff --git a/cmd/keeper/getpayload_example.go b/cmd/keeper/getpayload_example.go new file mode 100644 index 0000000000..683cc79248 --- /dev/null +++ b/cmd/keeper/getpayload_example.go @@ -0,0 +1,102 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build example + +package main + +import ( + _ "embed" + "fmt" + "os" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +// ExtWitness is a witness RLP encoding for transferring across clients. +// This is taken from PR #32216 until it's merged. +// It contains block headers, contract codes, state nodes, and storage keys +// required for stateless execution verification. +type ExtWitness struct { + Headers []*types.Header `json:"headers"` + Codes []hexutil.Bytes `json:"codes"` + State []hexutil.Bytes `json:"state"` + Keys []hexutil.Bytes `json:"keys"` +} + +// This is taken from PR #32216 until it's merged +// fromExtWitness converts the consensus witness format into our internal one. +func fromExtWitness(ext *ExtWitness) (*stateless.Witness, error) { + w := &stateless.Witness{} + w.Headers = ext.Headers + + w.Codes = make(map[string]struct{}, len(ext.Codes)) + for _, code := range ext.Codes { + w.Codes[string(code)] = struct{}{} + } + w.State = make(map[string]struct{}, len(ext.State)) + for _, node := range ext.State { + w.State[string(node)] = struct{}{} + } + return w, nil +} + +//go:embed 1192c3_witness.rlp +var witnessRlp []byte + +//go:embed 1192c3_block.rlp +var blockRlp []byte + +// getInput is a platform-specific function that will recover the input payload +// and returns it as a slice. It is expected to be an RLP-encoded Payload structure +// that contains the witness and the block. +// This is a demo version, that is intended to run on a regular computer, so what +// it does is embed a small Hoodi block, encodes the Payload structure containing +// the block and its witness as RLP, and returns the encoding. +func getInput() []byte { + var block types.Block + err := rlp.DecodeBytes(blockRlp, &block) + if err != nil { + panic(err) + } + + var extwitness ExtWitness + err = rlp.DecodeBytes(witnessRlp, &extwitness) + if err != nil { + panic(err) + } + witness, err := fromExtWitness(&extwitness) + if err != nil { + panic(err) + } + + payload := Payload{ + ChainID: params.HoodiChainConfig.ChainID.Uint64(), + Block: &block, + Witness: witness, + } + + encoded, err := rlp.EncodeToBytes(payload) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to encode payload: %v\n", err) + os.Exit(20) + } + return encoded +} diff --git a/cmd/keeper/getpayload_ziren.go b/cmd/keeper/getpayload_ziren.go new file mode 100644 index 0000000000..11c5845bcc --- /dev/null +++ b/cmd/keeper/getpayload_ziren.go @@ -0,0 +1,31 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build ziren + +package main + +import ( + zkruntime "github.com/zkMIPS/zkMIPS/crates/go-runtime/zkm_runtime" +) + +// getInput reads the input payload from the zkVM runtime environment. +// The zkVM host provides the RLP-encoded Payload structure containing +// the block and witness data through the runtime's input mechanism. +func getInput() []byte { + input := zkruntime.Read[[]byte]() + return input +} diff --git a/cmd/keeper/go.mod b/cmd/keeper/go.mod new file mode 100644 index 0000000000..72bec0fef3 --- /dev/null +++ b/cmd/keeper/go.mod @@ -0,0 +1,48 @@ +module github.com/ethereum/go-ethereum/cmd/keeper + +go 1.24.0 + +require ( + github.com/ethereum/go-ethereum v0.0.0-00010101000000-000000000000 + github.com/zkMIPS/zkMIPS/crates/go-runtime/zkm_runtime v0.0.0-20250915074013-fbc07aa2c6f5 +) + +require ( + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/consensys/gnark-crypto v0.18.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect + github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.14 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) + +replace ( + github.com/ethereum/go-ethereum => ../../ + github.com/zkMIPS/zkMIPS/crates/go-runtime/zkm_runtime => github.com/weilzkm/zkMIPS/crates/go-runtime/zkvm_runtime v0.0.0-20250915074013-fbc07aa2c6f5 +) diff --git a/cmd/keeper/go.sum b/cmd/keeper/go.sum new file mode 100644 index 0000000000..036d5ebd4b --- /dev/null +++ b/cmd/keeper/go.sum @@ -0,0 +1,148 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= +github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= +github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI= +github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w= +github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E= +github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= +github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= +github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/weilzkm/zkMIPS/crates/go-runtime/zkvm_runtime v0.0.0-20250915074013-fbc07aa2c6f5 h1:MxKlbmI7Dta6O6Nsc9OAer/rOltjoL11CVLMqCiYnxU= +github.com/weilzkm/zkMIPS/crates/go-runtime/zkvm_runtime v0.0.0-20250915074013-fbc07aa2c6f5/go.mod h1:zk/SUgiiVz2U1ufZ+yM2MHPbD93W25KH5zK3qAxXbT4= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/keeper/main.go b/cmd/keeper/main.go new file mode 100644 index 0000000000..cfb06f0da0 --- /dev/null +++ b/cmd/keeper/main.go @@ -0,0 +1,63 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package main + +import ( + "fmt" + "os" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/rlp" +) + +// Payload represents the input data for stateless execution containing +// a block and its associated witness data for verification. +type Payload struct { + ChainID uint64 + Block *types.Block + Witness *stateless.Witness +} + +func main() { + input := getInput() + var payload Payload + rlp.DecodeBytes(input, &payload) + + chainConfig, err := getChainConfig(payload.ChainID) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to get chain config: %v\n", err) + os.Exit(13) + } + vmConfig := vm.Config{} + + crossStateRoot, crossReceiptRoot, err := core.ExecuteStateless(chainConfig, vmConfig, payload.Block, payload.Witness) + if err != nil { + fmt.Fprintf(os.Stderr, "stateless self-validation failed: %v\n", err) + os.Exit(10) + } + if crossStateRoot != payload.Block.Root() { + fmt.Fprintf(os.Stderr, "stateless self-validation root mismatch (cross: %x local: %x)\n", crossStateRoot, payload.Block.Root()) + os.Exit(11) + } + if crossReceiptRoot != payload.Block.ReceiptHash() { + fmt.Fprintf(os.Stderr, "stateless self-validation receipt root mismatch (cross: %x local: %x)\n", crossReceiptRoot, payload.Block.ReceiptHash()) + os.Exit(12) + } +} diff --git a/cmd/keeper/stubs.go b/cmd/keeper/stubs.go new file mode 100644 index 0000000000..04a3bc735b --- /dev/null +++ b/cmd/keeper/stubs.go @@ -0,0 +1,26 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build !example && !ziren + +package main + +// getInput is a stub implementation for when no platform-specific build tags are set. +// This allows golangci-lint to typecheck the code without errors. +// The actual implementations are provided in platform-specific files. +func getInput() []byte { + panic("stub") +} diff --git a/go.work b/go.work new file mode 100644 index 0000000000..54e37dd75a --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.24.0 + +use ( + . + ./cmd/keeper +)