Skip to content

Commit cd1d559

Browse files
authored
fix(l1 follower, rollup verifier): blockhash mismatch (#1192)
* implement missing header fields reader and manager * chore: auto version bump [bot] * increase download timeout * sanitize BaseFee when executing blocks from DA * initialize and pass missing header manager to DA syncing pipeline * add state root to deduplicated header * overwrite state root if given via missing header file * fix test * set correct links and missing header file hashes * allow reading of previous headers by resetting file and buffer to support reset of syncing pipeline * address review comments * add coinbase and nonce field to missing header reader * replace missing header reader in toolkit with actual implementation * update sync from DA pipeline to include coinbase and nonce from missing header fields file * update sha256 hashes for missing header fields files * lint * address review comments * address review comments
1 parent 3b41bb8 commit cd1d559

File tree

23 files changed

+585
-79
lines changed

23 files changed

+585
-79
lines changed

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ var (
177177
utils.RollupVerifyEnabledFlag,
178178
utils.ShadowforkPeersFlag,
179179
utils.DASyncEnabledFlag,
180+
utils.DAMissingHeaderFieldsBaseURLFlag,
180181
utils.DABlockNativeAPIEndpointFlag,
181182
utils.DABlobScanAPIEndpointFlag,
182183
utils.DABeaconNodeAPIEndpointFlag,

cmd/geth/usage.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
237237
utils.L1DisableMessageQueueV2Flag,
238238
utils.RollupVerifyEnabledFlag,
239239
utils.DASyncEnabledFlag,
240+
utils.DAMissingHeaderFieldsBaseURLFlag,
240241
utils.DABlobScanAPIEndpointFlag,
241242
utils.DABlockNativeAPIEndpointFlag,
242243
utils.DABeaconNodeAPIEndpointFlag,

cmd/utils/flags.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,12 @@ var (
898898
Name: "da.sync",
899899
Usage: "Enable node syncing from DA",
900900
}
901+
DAMissingHeaderFieldsBaseURLFlag = cli.StringFlag{
902+
Name: "da.missingheaderfields.baseurl",
903+
Usage: "Base URL for fetching missing header fields for pre-EuclidV2 blocks",
904+
Value: "https://scroll-block-missing-metadata.s3.us-west-2.amazonaws.com/",
905+
}
906+
901907
DABlobScanAPIEndpointFlag = cli.StringFlag{
902908
Name: "da.blob.blobscan",
903909
Usage: "BlobScan blob API endpoint",
@@ -1382,6 +1388,8 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
13821388
cfg.DaSyncingEnabled = ctx.Bool(DASyncEnabledFlag.Name)
13831389
}
13841390

1391+
cfg.DAMissingHeaderFieldsBaseURL = ctx.GlobalString(DAMissingHeaderFieldsBaseURLFlag.Name)
1392+
13851393
if ctx.GlobalIsSet(ExternalSignerFlag.Name) {
13861394
cfg.ExternalSigner = ctx.GlobalString(ExternalSignerFlag.Name)
13871395
}

core/blockchain.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,15 +1880,18 @@ func (bc *BlockChain) BuildAndWriteBlock(parentBlock *types.Block, header *types
18801880

18811881
header.ParentHash = parentBlock.Hash()
18821882

1883+
// sanitize base fee
1884+
// Note: setting the base fee to 0 will cause problems as nil != 0 when serializing the header and thus block hash will be different.
1885+
if header.BaseFee != nil && header.BaseFee.Cmp(common.Big0) == 0 {
1886+
header.BaseFee = nil
1887+
}
1888+
18831889
tempBlock := types.NewBlockWithHeader(header).WithBody(txs, nil)
18841890
receipts, logs, gasUsed, err := bc.processor.Process(tempBlock, statedb, bc.vmConfig)
18851891
if err != nil {
18861892
return nil, NonStatTy, fmt.Errorf("error processing block: %w", err)
18871893
}
18881894

1889-
// TODO: once we have the extra and difficulty we need to verify the signature of the block with Clique
1890-
// This should be done with https://github.com/scroll-tech/go-ethereum/pull/913.
1891-
18921895
if sign {
18931896
// Prevent Engine from overriding timestamp.
18941897
originalTime := header.Time
@@ -1901,7 +1904,11 @@ func (bc *BlockChain) BuildAndWriteBlock(parentBlock *types.Block, header *types
19011904

19021905
// finalize and assemble block as fullBlock: replicates consensus.FinalizeAndAssemble()
19031906
header.GasUsed = gasUsed
1904-
header.Root = statedb.IntermediateRoot(bc.chainConfig.IsEIP158(header.Number))
1907+
1908+
// state root might be set from partial header. If it is not set, we calculate it.
1909+
if header.Root == (common.Hash{}) {
1910+
header.Root = statedb.IntermediateRoot(bc.chainConfig.IsEIP158(header.Number))
1911+
}
19051912

19061913
fullBlock := types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil))
19071914

eth/backend.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import (
2222
"errors"
2323
"fmt"
2424
"math/big"
25+
"net/url"
26+
"path"
27+
"path/filepath"
2528
"runtime"
2629
"sync"
2730
"sync/atomic"
@@ -61,6 +64,7 @@ import (
6164
"github.com/scroll-tech/go-ethereum/rollup/ccc"
6265
"github.com/scroll-tech/go-ethereum/rollup/da_syncer"
6366
"github.com/scroll-tech/go-ethereum/rollup/l1"
67+
"github.com/scroll-tech/go-ethereum/rollup/missing_header_fields"
6468
"github.com/scroll-tech/go-ethereum/rollup/rollup_sync_service"
6569
"github.com/scroll-tech/go-ethereum/rollup/sync_service"
6670
"github.com/scroll-tech/go-ethereum/rpc"
@@ -241,7 +245,12 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether
241245
if config.EnableDASyncing {
242246
// Do not start syncing pipeline if we are producing blocks for permissionless batches.
243247
if !config.DA.ProduceBlocks {
244-
eth.syncingPipeline, err = da_syncer.NewSyncingPipeline(context.Background(), eth.blockchain, chainConfig, eth.chainDb, l1Client, stack.Config().L1DeploymentBlock, config.DA)
248+
missingHeaderFieldsManager, err := createMissingHeaderFieldsManager(stack, chainConfig)
249+
if err != nil {
250+
return nil, fmt.Errorf("cannot create missing header fields manager: %w", err)
251+
}
252+
253+
eth.syncingPipeline, err = da_syncer.NewSyncingPipeline(context.Background(), eth.blockchain, chainConfig, eth.chainDb, l1Client, stack.Config().L1DeploymentBlock, config.DA, missingHeaderFieldsManager)
245254
if err != nil {
246255
return nil, fmt.Errorf("cannot initialize da syncer: %w", err)
247256
}
@@ -337,6 +346,22 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client l1.Client) (*Ether
337346
return eth, nil
338347
}
339348

349+
func createMissingHeaderFieldsManager(stack *node.Node, chainConfig *params.ChainConfig) (*missing_header_fields.Manager, error) {
350+
downloadURL, err := url.Parse(stack.Config().DAMissingHeaderFieldsBaseURL)
351+
if err != nil {
352+
return nil, fmt.Errorf("invalid DAMissingHeaderFieldsBaseURL: %w", err)
353+
}
354+
downloadURL.Path = path.Join(downloadURL.Path, chainConfig.ChainID.String()+".bin")
355+
356+
expectedSHA256Checksum := chainConfig.Scroll.MissingHeaderFieldsSHA256
357+
if expectedSHA256Checksum == nil {
358+
return nil, fmt.Errorf("missing expected SHA256 checksum for missing header fields file in chain config")
359+
}
360+
361+
filePath := filepath.Join(stack.Config().DataDir, fmt.Sprintf("missing-header-fields-%s-%s", chainConfig.ChainID, expectedSHA256Checksum.Hex()))
362+
return missing_header_fields.NewManager(context.Background(), filePath, downloadURL.String(), *expectedSHA256Checksum), nil
363+
}
364+
340365
func makeExtraData(extra []byte) []byte {
341366
if len(extra) == 0 {
342367
// create default extradata

node/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ type Config struct {
201201
L1DisableMessageQueueV2 bool `toml:",omitempty"`
202202
// Is daSyncingEnabled
203203
DaSyncingEnabled bool `toml:",omitempty"`
204+
// Base URL for missing header fields file
205+
DAMissingHeaderFieldsBaseURL string `toml:",omitempty"`
204206
}
205207

206208
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into

params/config.go

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,18 @@ import (
3030

3131
// Genesis hashes to enforce below configs on.
3232
var (
33-
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
34-
RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
35-
SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")
36-
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
37-
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
38-
ScrollAlphaGenesisHash = common.HexToHash("0xa4fc62b9b0643e345bdcebe457b3ae898bef59c7203c3db269200055e037afda")
39-
ScrollSepoliaGenesisHash = common.HexToHash("0xaa62d1a8b2bffa9e5d2368b63aae0d98d54928bd713125e3fd9e5c896c68592c")
40-
ScrollMainnetGenesisHash = common.HexToHash("0xbbc05efd412b7cd47a2ed0e5ddfcf87af251e414ea4c801d78b6784513180a80")
41-
ScrollSepoliaGenesisState = common.HexToHash("0x20695989e9038823e35f0e88fbc44659ffdbfa1fe89fbeb2689b43f15fa64cb5")
42-
ScrollMainnetGenesisState = common.HexToHash("0x08d535cc60f40af5dd3b31e0998d7567c2d568b224bed2ba26070aeb078d1339")
33+
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
34+
RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
35+
SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")
36+
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
37+
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
38+
ScrollAlphaGenesisHash = common.HexToHash("0xa4fc62b9b0643e345bdcebe457b3ae898bef59c7203c3db269200055e037afda")
39+
ScrollSepoliaGenesisHash = common.HexToHash("0xaa62d1a8b2bffa9e5d2368b63aae0d98d54928bd713125e3fd9e5c896c68592c")
40+
ScrollMainnetGenesisHash = common.HexToHash("0xbbc05efd412b7cd47a2ed0e5ddfcf87af251e414ea4c801d78b6784513180a80")
41+
ScrollSepoliaGenesisState = common.HexToHash("0x20695989e9038823e35f0e88fbc44659ffdbfa1fe89fbeb2689b43f15fa64cb5")
42+
ScrollMainnetGenesisState = common.HexToHash("0x08d535cc60f40af5dd3b31e0998d7567c2d568b224bed2ba26070aeb078d1339")
43+
ScrollMainnetMissingHeaderFieldsSHA256 = common.HexToHash("0xfa2746026ec9590e37e495cb20046e20a38fd0e7099abd2012640dddf6c88b25")
44+
ScrollSepoliaMissingHeaderFieldsSHA256 = common.HexToHash("0xa02354c12ca0f918bf4768255af9ed13c137db7e56252348f304b17bb4088924")
4345
)
4446

4547
func newUint64(val uint64) *uint64 { return &val }
@@ -354,7 +356,8 @@ var (
354356
ScrollChainAddress: common.HexToAddress("0x2D567EcE699Eabe5afCd141eDB7A4f2D0D6ce8a0"),
355357
L2SystemConfigAddress: common.HexToAddress("0xF444cF06A3E3724e20B35c2989d3942ea8b59124"),
356358
},
357-
GenesisStateRoot: &ScrollSepoliaGenesisState,
359+
GenesisStateRoot: &ScrollSepoliaGenesisState,
360+
MissingHeaderFieldsSHA256: &ScrollSepoliaMissingHeaderFieldsSHA256,
358361
},
359362
}
360363

@@ -406,7 +409,8 @@ var (
406409
ScrollChainAddress: common.HexToAddress("0xa13BAF47339d63B743e7Da8741db5456DAc1E556"),
407410
L2SystemConfigAddress: common.HexToAddress("0x331A873a2a85219863d80d248F9e2978fE88D0Ea"),
408411
},
409-
GenesisStateRoot: &ScrollMainnetGenesisState,
412+
GenesisStateRoot: &ScrollMainnetGenesisState,
413+
MissingHeaderFieldsSHA256: &ScrollMainnetMissingHeaderFieldsSHA256,
410414
},
411415
}
412416

@@ -710,6 +714,9 @@ type ScrollConfig struct {
710714

711715
// Genesis State Root for MPT clients
712716
GenesisStateRoot *common.Hash `json:"genesisStateRoot,omitempty"`
717+
718+
// MissingHeaderFieldsSHA256 is the SHA256 hash of the missing header fields file.
719+
MissingHeaderFieldsSHA256 *common.Hash `json:"missingHeaderFieldsSHA256,omitempty"`
713720
}
714721

715722
// L1Config contains the l1 parameters needed to sync l1 contract events (e.g., l1 messages, commit/revert/finalize batches) in the sequencer
@@ -760,8 +767,13 @@ func (s ScrollConfig) String() string {
760767
genesisStateRoot = fmt.Sprintf("%v", *s.GenesisStateRoot)
761768
}
762769

763-
return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, l1Config: %v, genesisStateRoot: %v}",
764-
s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.L1Config.String(), genesisStateRoot)
770+
missingHeaderFieldsSHA256 := "<nil>"
771+
if s.MissingHeaderFieldsSHA256 != nil {
772+
missingHeaderFieldsSHA256 = fmt.Sprintf("%v", *s.MissingHeaderFieldsSHA256)
773+
}
774+
775+
return fmt.Sprintf("{useZktrie: %v, maxTxPerBlock: %v, MaxTxPayloadBytesPerBlock: %v, feeVaultAddress: %v, l1Config: %v, genesisStateRoot: %v, missingHeaderFieldsSHA256: %v}",
776+
s.UseZktrie, maxTxPerBlock, maxTxPayloadBytesPerBlock, s.FeeVaultAddress, s.L1Config.String(), genesisStateRoot, missingHeaderFieldsSHA256)
765777
}
766778

767779
// IsValidTxCount returns whether the given block's transaction count is below the limit.

rollup/da_syncer/block_queue.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,22 @@ import (
66

77
"github.com/scroll-tech/go-ethereum/core/rawdb"
88
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/da"
9+
"github.com/scroll-tech/go-ethereum/rollup/missing_header_fields"
910
)
1011

1112
// BlockQueue is a pipeline stage that reads batches from BatchQueue, extracts all da.PartialBlock from it and
1213
// provides them to the next stage one-by-one.
1314
type BlockQueue struct {
14-
batchQueue *BatchQueue
15-
blocks []*da.PartialBlock
15+
batchQueue *BatchQueue
16+
blocks []*da.PartialBlock
17+
missingHeaderFieldsManager *missing_header_fields.Manager
1618
}
1719

18-
func NewBlockQueue(batchQueue *BatchQueue) *BlockQueue {
20+
func NewBlockQueue(batchQueue *BatchQueue, missingHeaderFieldsManager *missing_header_fields.Manager) *BlockQueue {
1921
return &BlockQueue{
20-
batchQueue: batchQueue,
21-
blocks: make([]*da.PartialBlock, 0),
22+
batchQueue: batchQueue,
23+
blocks: make([]*da.PartialBlock, 0),
24+
missingHeaderFieldsManager: missingHeaderFieldsManager,
2225
}
2326
}
2427

@@ -40,7 +43,7 @@ func (bq *BlockQueue) getBlocksFromBatch(ctx context.Context) error {
4043
return err
4144
}
4245

43-
bq.blocks, err = entryWithBlocks.Blocks()
46+
bq.blocks, err = entryWithBlocks.Blocks(bq.missingHeaderFieldsManager)
4447
if err != nil {
4548
return fmt.Errorf("failed to get blocks from entry: %w", err)
4649
}

rollup/da_syncer/da/commitV0.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/scroll-tech/go-ethereum/log"
1414
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/serrors"
1515
"github.com/scroll-tech/go-ethereum/rollup/l1"
16+
"github.com/scroll-tech/go-ethereum/rollup/missing_header_fields"
1617
)
1718

1819
type CommitBatchDAV0 struct {
@@ -109,7 +110,7 @@ func (c *CommitBatchDAV0) CompareTo(other Entry) int {
109110
return 0
110111
}
111112

112-
func (c *CommitBatchDAV0) Blocks() ([]*PartialBlock, error) {
113+
func (c *CommitBatchDAV0) Blocks(manager *missing_header_fields.Manager) ([]*PartialBlock, error) {
113114
l1Txs, err := getL1Messages(c.db, c.parentTotalL1MessagePopped, c.skippedL1MessageBitmap, c.l1MessagesPopped)
114115
if err != nil {
115116
return nil, fmt.Errorf("failed to get L1 messages for v0 batch %d: %w", c.batchIndex, err)
@@ -120,7 +121,7 @@ func (c *CommitBatchDAV0) Blocks() ([]*PartialBlock, error) {
120121

121122
curL1TxIndex := c.parentTotalL1MessagePopped
122123
for _, chunk := range c.chunks {
123-
for blockId, daBlock := range chunk.Blocks {
124+
for blockIndex, daBlock := range chunk.Blocks {
124125
// create txs
125126
txs := make(types.Transactions, 0, daBlock.NumTransactions())
126127
// insert l1 msgs
@@ -132,16 +133,24 @@ func (c *CommitBatchDAV0) Blocks() ([]*PartialBlock, error) {
132133
curL1TxIndex += uint64(daBlock.NumL1Messages())
133134

134135
// insert l2 txs
135-
txs = append(txs, chunk.Transactions[blockId]...)
136+
txs = append(txs, chunk.Transactions[blockIndex]...)
137+
138+
difficulty, stateRoot, coinbase, nonce, extraData, err := manager.GetMissingHeaderFields(daBlock.Number())
139+
if err != nil {
140+
return nil, fmt.Errorf("failed to get missing header fields for block %d: %w", daBlock.Number(), err)
141+
}
136142

137143
block := NewPartialBlock(
138144
&PartialHeader{
139145
Number: daBlock.Number(),
140146
Time: daBlock.Timestamp(),
141147
BaseFee: daBlock.BaseFee(),
142148
GasLimit: daBlock.GasLimit(),
143-
Difficulty: 10, // TODO: replace with real difficulty
144-
ExtraData: []byte{1, 2, 3, 4, 5, 6, 7, 8}, // TODO: replace with real extra data
149+
Difficulty: difficulty,
150+
ExtraData: extraData,
151+
StateRoot: stateRoot,
152+
Coinbase: coinbase,
153+
Nonce: nonce,
145154
},
146155
txs)
147156
blocks = append(blocks, block)

rollup/da_syncer/da/commitV7.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/blob_client"
1414
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/serrors"
1515
"github.com/scroll-tech/go-ethereum/rollup/l1"
16+
"github.com/scroll-tech/go-ethereum/rollup/missing_header_fields"
1617

1718
"github.com/scroll-tech/go-ethereum/common"
1819
"github.com/scroll-tech/go-ethereum/crypto/kzg4844"
@@ -113,7 +114,7 @@ func (c *CommitBatchDAV7) Event() l1.RollupEvent {
113114
return c.event
114115
}
115116

116-
func (c *CommitBatchDAV7) Blocks() ([]*PartialBlock, error) {
117+
func (c *CommitBatchDAV7) Blocks(_ *missing_header_fields.Manager) ([]*PartialBlock, error) {
117118
initialL1MessageIndex := c.parentTotalL1MessagePopped
118119

119120
l1Txs, err := getL1MessagesV7(c.db, c.blobPayload.Blocks(), initialL1MessageIndex)

0 commit comments

Comments
 (0)