From ee93a2097117a799c5e2d24f6e17dcb1ddc7b178 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 9 Oct 2026 10:04:01 +0800 Subject: [PATCH 1/2] fix(prover): accept Glamsterdam L1 block access list hashes Glamsterdam L1 headers carry the EIP-7928 block access list hash, and the L1 block hash commits to it. gaiko2 rejected that field in every header, so any proposal whose origin block comes after the fork could not be proven. The pinned taiko-geth header also had no such field, so the recomputed L1 hash would not have matched either. - Bump taiko-geth to taikoxyz/taiko-geth#611 (f2fe2fd49), whose header keeps the block access list hash. - Decode taiko.l1_header and taiko.l1_ancestor_headers with the field. L2 headers still reject it. - Pin a real Sepolia header hash and cover anchor linkage with Glamsterdam L1 headers. Co-Authored-By: Claude Opus 5.5 --- go.mod | 2 +- go.sum | 4 +- internal/prover/decode.go | 18 ++++++- internal/prover/manifest_validate.go | 4 +- internal/prover/manifest_validate_test.go | 52 +++++++++++++++++++ internal/prover/replay_fixture_test.go | 6 ++- ...1_header_sepolia_11873940_glamsterdam.json | 25 +++++++++ 7 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 testdata/l1_header_sepolia_11873940_glamsterdam.json diff --git a/go.mod b/go.mod index 6529eda..9df0bbd 100644 --- a/go.mod +++ b/go.mod @@ -46,4 +46,4 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -replace github.com/ethereum/go-ethereum => github.com/taikoxyz/taiko-geth v1.18.1-0.20260924044618-8e98046bfd6a +replace github.com/ethereum/go-ethereum => github.com/taikoxyz/taiko-geth v1.18.1-0.20261009014337-f2fe2fd498d2 diff --git a/go.sum b/go.sum index 56d9fc6..fe2b0d0 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/taikoxyz/taiko-geth v1.18.1-0.20260924044618-8e98046bfd6a h1:Vtn63Z+JyxXuSvL8rcUs+2HD26sEsZtPj4lMOB72F+g= -github.com/taikoxyz/taiko-geth v1.18.1-0.20260924044618-8e98046bfd6a/go.mod h1:KHcRXfGOUfUmKg51IhQ0IowiqZ6PqZf08CMtk0g5K1o= +github.com/taikoxyz/taiko-geth v1.18.1-0.20261009014337-f2fe2fd498d2 h1:ZhzWMgsnXGbdnrZZnLMlazPP25bQMpq51PqCuKdrT30= +github.com/taikoxyz/taiko-geth v1.18.1-0.20261009014337-f2fe2fd498d2/go.mod h1:KHcRXfGOUfUmKg51IhQ0IowiqZ6PqZf08CMtk0g5K1o= 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= diff --git a/internal/prover/decode.go b/internal/prover/decode.go index 19b0c15..8c5a6e7 100644 --- a/internal/prover/decode.go +++ b/internal/prover/decode.go @@ -83,7 +83,21 @@ func decodeBlockEnvelope(raw json.RawMessage) (rawBlockEnvelope, error) { return decoded, nil } +// decodeHeader decodes an L2 header, which never carries a block access list hash. func decodeHeader(raw json.RawMessage) (*types.Header, error) { + header, err := decodeL1Header(raw) + if err != nil { + return nil, err + } + if header.BlockAccessListHash != nil { + return nil, fmt.Errorf("field block_access_list_hash is not supported by taiko-geth replay") + } + return header, nil +} + +// decodeL1Header decodes an L1 header. Glamsterdam L1 headers carry the +// EIP-7928 block access list hash, and their block hash commits to it. +func decodeL1Header(raw json.RawMessage) (*types.Header, error) { fields, err := decodeJSONObject(raw) if err != nil { return nil, fmt.Errorf("unmarshal header: %w", err) @@ -210,8 +224,8 @@ func decodeHeader(raw json.RawMessage) (*types.Header, error) { } if blockAccessListHash, err := optionalHash(fields, "block_access_list_hash", "blockAccessListHash"); err != nil { return nil, err - } else if blockAccessListHash != nil { - return nil, fmt.Errorf("field block_access_list_hash is not supported by taiko-geth replay") + } else { + header.BlockAccessListHash = blockAccessListHash } return header, nil diff --git a/internal/prover/manifest_validate.go b/internal/prover/manifest_validate.go index f7e73e1..795a950 100644 --- a/internal/prover/manifest_validate.go +++ b/internal/prover/manifest_validate.go @@ -1289,7 +1289,7 @@ func decodeGuestInputL1Headers(raw json.RawMessage) (*types.Header, []*types.Hea if !ok || isEmptyOrNullRawMessage(l1HeaderRaw) { return nil, nil, fmt.Errorf("missing taiko.l1_header") } - l1Header, err := decodeHeader(l1HeaderRaw) + l1Header, err := decodeL1Header(l1HeaderRaw) if err != nil { return nil, nil, fmt.Errorf("decode taiko.l1_header: %w", err) } @@ -1303,7 +1303,7 @@ func decodeGuestInputL1Headers(raw json.RawMessage) (*types.Header, []*types.Hea } ancestors := make([]*types.Header, len(rawList)) for i, r := range rawList { - h, err := decodeHeader(r) + h, err := decodeL1Header(r) if err != nil { return nil, nil, fmt.Errorf("decode taiko.l1_ancestor_headers[%d]: %w", i, err) } diff --git a/internal/prover/manifest_validate_test.go b/internal/prover/manifest_validate_test.go index c1f3939..5bcd4fa 100644 --- a/internal/prover/manifest_validate_test.go +++ b/internal/prover/manifest_validate_test.go @@ -537,6 +537,36 @@ func TestDecodeGuestInputL1HeadersReadsOriginAndAncestors(t *testing.T) { } } +func TestDecodeGuestInputL1HeadersKeepsGlamsterdamHeaderHash(t *testing.T) { + header, err := os.ReadFile(testdataPath("l1_header_sepolia_11873940_glamsterdam.json")) + if err != nil { + t.Fatalf("read L1 header fixture: %v", err) + } + raw := mustRawMessage(t, fmt.Sprintf(`{"l1_header":%s,"l1_ancestor_headers":[%s]}`, header, header)) + + origin, ancestors, err := decodeGuestInputL1Headers(raw) + if err != nil { + t.Fatalf("decode: %v", err) + } + // Sepolia block 11873940 hash from eth_getBlockByNumber. + want := common.HexToHash("0xfd4eb21751cb7c7d94d75f61c9f6364558923a2db73599f9ed917ee65d8c5680") + if got := origin.Hash(); got != want { + t.Fatalf("origin hash: got %s, want %s", got, want) + } + if len(ancestors) != 1 || ancestors[0].Hash() != want { + t.Fatalf("ancestor hash mismatch") + } +} + +func TestValidateManifestBindingAcceptsGlamsterdamL1Headers(t *testing.T) { + fixture := newManifestBindingFixture(t) + fixture.l1Glamsterdam = true + + if err := ValidateGuestInputManifestBinding(fixture.view(t)); err != nil { + t.Fatalf("validate manifest binding: %v", err) + } +} + func TestValidateManifestBindingRejectsEmptyProposalSources(t *testing.T) { view := newManifestBindingFixture(t).view(t) taikoFields, err := decodeJSONObject(view.TaikoRaw) @@ -1204,6 +1234,9 @@ type manifestBindingFixture struct { anchorCheckpointBlockHash common.Hash anchorCheckpointStateRoot common.Hash omitL1Headers bool + // l1Glamsterdam adds the Glamsterdam block access list hash and slot number + // to every synthetic L1 header. + l1Glamsterdam bool } type anchorSignatureMode uint8 @@ -1357,6 +1390,12 @@ func (f *manifestBindingFixture) buildL1Chain(t *testing.T) { Nonce: types.BlockNonce{}, BaseFee: new(big.Int).SetUint64(manifestTestBaseFee), } + if f.l1Glamsterdam { + blockAccessListHash := common.HexToHash(fmt.Sprintf("0x%064x", 0x200000+number)) + slotNumber := number + header.BlockAccessListHash = &blockAccessListHash + header.SlotNumber = &slotNumber + } parentHash = header.Hash() headers = append(headers, header) } @@ -2027,6 +2066,19 @@ func headerJSON(t *testing.T, header *types.Header) string { binary.BigEndian.Uint64(header.Nonce[:]), baseFee, ) + if header.BlockAccessListHash != nil || header.SlotNumber != nil { + fields, err := decodeJSONObject(json.RawMessage(raw)) + if err != nil { + t.Fatalf("decode header object: %v", err) + } + if header.BlockAccessListHash != nil { + fields["blockAccessListHash"] = mustRawMessage(t, fmt.Sprintf("%q", header.BlockAccessListHash.Hex())) + } + if header.SlotNumber != nil { + fields["slotNumber"] = mustRawMessage(t, fmt.Sprintf(`"0x%x"`, *header.SlotNumber)) + } + raw = string(mustMarshalRawMessage(t, fields)) + } return raw } diff --git a/internal/prover/replay_fixture_test.go b/internal/prover/replay_fixture_test.go index 568f56d..5bb5b3b 100644 --- a/internal/prover/replay_fixture_test.go +++ b/internal/prover/replay_fixture_test.go @@ -178,7 +178,11 @@ func loadSharedShastaFixture(t *testing.T) protocol.ShastaRequest { } func sharedShastaFixturePath() string { + return testdataPath(sharedFixtureName) +} + +func testdataPath(name string) string { _, file, _, _ := runtime.Caller(0) root := filepath.Dir(filepath.Dir(filepath.Dir(file))) - return filepath.Join(root, "testdata", sharedFixtureName) + return filepath.Join(root, "testdata", name) } diff --git a/testdata/l1_header_sepolia_11873940_glamsterdam.json b/testdata/l1_header_sepolia_11873940_glamsterdam.json new file mode 100644 index 0000000..afc6c16 --- /dev/null +++ b/testdata/l1_header_sepolia_11873940_glamsterdam.json @@ -0,0 +1,25 @@ +{ + "parentHash": "0x44a35abdb22170ba76e8f1edefd1a6c1296337e6efc1494d59501413f500f9d3", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "miner": "0x455e5aa18469bc6ccef49594645666c587a3a71b", + "stateRoot": "0x7472b5b444ca7f0ed9118a31693a36f7fff53716791997058a8931efcbdb35d5", + "transactionsRoot": "0x9d8832ebbeeb31a396d3bfda702206391babc4486b516cfb9731fbdf7f7fd0c9", + "receiptsRoot": "0x47362efc87c565c6c57e7e017af19d6ec0c8ca5dea69e989a4c4f6d73cc56433", + "logsBloom": "0x3ca304f2111151500bcfcb0bb0041659987016f58255f20fbdf00f556a8e9d7b6fc9da2460a6f04bd6202543eb7554367a0fc61452d12429823302496bb315175b27c94302ea37ca24b000dedd988c33215896683083051f00618cb88c815c3a871a509e325820112b18506144003cc077e5104003bc041190015c78c5e8a65805d28ca59ab64ac008d911527603cc0305b428d5b03c13693d97944e802b460002042262019421c018d0d94ac109c04a434719916c9d000583c063c0c2226d367909d946353951c19c0954a76ac2020b5f33928db62801340edbf945406872442850006b40a648cbe8221805340ae294f9db3307898fa35917cb0b239a474f69", + "difficulty": "0x0", + "number": "0xb52e94", + "gasLimit": "0xbe5cce1", + "gasUsed": "0x3fe6190", + "timestamp": "0x6ac83dec", + "extraData": "0x4e65746865726d696e642076322e312e30", + "mixHash": "0x1fb20bb9e8ef3e13f25914fea50b848dca3ff37f34075946cb200e9863a8b6da", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0xf", + "withdrawalsRoot": "0xf54a76996e5af32f5369c3a3d5653193870395ce8e2b4a460e5e13467d464ef3", + "blobGasUsed": "0x100000", + "excessBlobGas": "0x1a0000", + "parentBeaconBlockRoot": "0xfb2eecbafbb7f1b06db299563146a0a1943c1408aba30c81221066dcc6deaade", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "blockAccessListHash": "0x68398eaab5b495749cccc4a7b91b7eaa3e2f863e569ddf0cc2fe0d4ea16c526b", + "slotNumber": "0xaca561" +} From ee13f4f2d6dc1b9fb0e2450b358f0dc39a002f8f Mon Sep 17 00:00:00 2001 From: David Date: Fri, 9 Oct 2026 10:11:44 +0800 Subject: [PATCH 2/2] chore(deps): pin merged taiko-geth revision Pin the taiko branch squash commit of taikoxyz/taiko-geth#611 (8a71a2ecb). Its tree is identical to the reviewed PR head f2fe2fd49. Co-Authored-By: Claude Opus 5.5 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9df0bbd..bd0b0be 100644 --- a/go.mod +++ b/go.mod @@ -46,4 +46,4 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect ) -replace github.com/ethereum/go-ethereum => github.com/taikoxyz/taiko-geth v1.18.1-0.20261009014337-f2fe2fd498d2 +replace github.com/ethereum/go-ethereum => github.com/taikoxyz/taiko-geth v1.18.1-0.20261009020728-8a71a2ecb3b7 diff --git a/go.sum b/go.sum index fe2b0d0..fdfdaa6 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/taikoxyz/taiko-geth v1.18.1-0.20261009014337-f2fe2fd498d2 h1:ZhzWMgsnXGbdnrZZnLMlazPP25bQMpq51PqCuKdrT30= -github.com/taikoxyz/taiko-geth v1.18.1-0.20261009014337-f2fe2fd498d2/go.mod h1:KHcRXfGOUfUmKg51IhQ0IowiqZ6PqZf08CMtk0g5K1o= +github.com/taikoxyz/taiko-geth v1.18.1-0.20261009020728-8a71a2ecb3b7 h1:rl2KI2v+yrtZtlWiPEzKUSkVtVZ6cb52VYC1kVPHZSk= +github.com/taikoxyz/taiko-geth v1.18.1-0.20261009020728-8a71a2ecb3b7/go.mod h1:KHcRXfGOUfUmKg51IhQ0IowiqZ6PqZf08CMtk0g5K1o= 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=