Skip to content

Commit 00494d5

Browse files
committed
feat: follower node sync from DA #631
Conflicts: cmd/geth/main.go core/state_processor_test.go core/txpool/legacypool/legacypool.go eth/backend.go eth/ethconfig/config.go eth/gasprice/gasprice_test.go eth/handler.go eth/protocols/eth/broadcast.go eth/protocols/eth/handlers.go go.mod go.sum miner/miner.go miner/miner_test.go miner/scroll_worker.go miner/scroll_worker_test.go params/config.go params/version.go rollup/rollup_sync_service/rollup_sync_service_test.go
1 parent 0c312be commit 00494d5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2553
-30
lines changed

cmd/geth/main.go

+6
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ var (
153153
utils.CircuitCapacityCheckEnabledFlag,
154154
utils.RollupVerifyEnabledFlag,
155155
utils.ShadowforkPeersFlag,
156+
utils.DASyncEnabledFlag,
157+
utils.DAModeFlag,
158+
utils.DASnapshotFileFlag,
159+
utils.DABlockNativeAPIEndpointFlag,
160+
utils.DABlobScanAPIEndpointFlag,
161+
utils.DABeaconNodeAPIEndpointFlag,
156162
}, utils.NetworkFlags, utils.DatabaseFlags)
157163

158164
rpcFlags = []cli.Flag{

cmd/utils/flags.go

+54
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import (
7272
"github.com/scroll-tech/go-ethereum/p2p/nat"
7373
"github.com/scroll-tech/go-ethereum/p2p/netutil"
7474
"github.com/scroll-tech/go-ethereum/params"
75+
"github.com/scroll-tech/go-ethereum/rollup/da_syncer"
7576
"github.com/scroll-tech/go-ethereum/rollup/tracing"
7677
"github.com/scroll-tech/go-ethereum/rpc"
7778
"github.com/scroll-tech/go-ethereum/trie"
@@ -1017,6 +1018,34 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
10171018
Name: "net.shadowforkpeers",
10181019
Usage: "peer ids of shadow fork peers",
10191020
}
1021+
1022+
// DA syncing settings
1023+
DASyncEnabledFlag = cli.BoolFlag{
1024+
Name: "da.sync",
1025+
Usage: "Enable node syncing from DA",
1026+
}
1027+
defaultDA = ethconfig.Defaults.DA.FetcherMode
1028+
DAModeFlag = TextMarshalerFlag{
1029+
Name: "da.mode",
1030+
Usage: `DA sync mode ("l1rpc" or "snapshot")`,
1031+
Value: &defaultDA,
1032+
}
1033+
DASnapshotFileFlag = cli.StringFlag{
1034+
Name: "da.snapshot.file",
1035+
Usage: "Snapshot file to sync from DA",
1036+
}
1037+
DABlobScanAPIEndpointFlag = cli.StringFlag{
1038+
Name: "da.blob.blobscan",
1039+
Usage: "BlobScan blob API endpoint",
1040+
}
1041+
DABlockNativeAPIEndpointFlag = cli.StringFlag{
1042+
Name: "da.blob.blocknative",
1043+
Usage: "BlockNative blob API endpoint",
1044+
}
1045+
DABeaconNodeAPIEndpointFlag = cli.StringFlag{
1046+
Name: "da.blob.beaconnode",
1047+
Usage: "Beacon node API endpoint",
1048+
}
10201049
)
10211050

10221051
var (
@@ -1505,6 +1534,9 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
15051534
SetDataDir(ctx, cfg)
15061535
setSmartCard(ctx, cfg)
15071536
setL1(ctx, cfg)
1537+
if ctx.GlobalIsSet(DASyncEnabledFlag.Name) {
1538+
cfg.DaSyncingEnabled = ctx.GlobalBool(DASyncEnabledFlag.Name)
1539+
}
15081540

15091541
if ctx.IsSet(JWTSecretFlag.Name) {
15101542
cfg.JWTSecret = ctx.String(JWTSecretFlag.Name)
@@ -1751,6 +1783,27 @@ func setEnableRollupVerify(ctx *cli.Context, cfg *ethconfig.Config) {
17511783
}
17521784
}
17531785

1786+
func setDA(ctx *cli.Context, cfg *ethconfig.Config) {
1787+
if ctx.GlobalIsSet(DASyncEnabledFlag.Name) {
1788+
cfg.EnableDASyncing = ctx.GlobalBool(DASyncEnabledFlag.Name)
1789+
if ctx.GlobalIsSet(DAModeFlag.Name) {
1790+
cfg.DA.FetcherMode = *GlobalTextMarshaler(ctx, DAModeFlag.Name).(*da_syncer.FetcherMode)
1791+
}
1792+
if ctx.GlobalIsSet(DASnapshotFileFlag.Name) {
1793+
cfg.DA.SnapshotFilePath = ctx.GlobalString(DASnapshotFileFlag.Name)
1794+
}
1795+
if ctx.GlobalIsSet(DABlobScanAPIEndpointFlag.Name) {
1796+
cfg.DA.BlobScanAPIEndpoint = ctx.GlobalString(DABlobScanAPIEndpointFlag.Name)
1797+
}
1798+
if ctx.GlobalIsSet(DABlockNativeAPIEndpointFlag.Name) {
1799+
cfg.DA.BlockNativeAPIEndpoint = ctx.GlobalString(DABlockNativeAPIEndpointFlag.Name)
1800+
}
1801+
if ctx.GlobalIsSet(DABeaconNodeAPIEndpointFlag.Name) {
1802+
cfg.DA.BeaconNodeAPIEndpoint = ctx.GlobalString(DABeaconNodeAPIEndpointFlag.Name)
1803+
}
1804+
}
1805+
}
1806+
17541807
func setMaxBlockRange(ctx *cli.Context, cfg *ethconfig.Config) {
17551808
if ctx.IsSet(MaxBlockRangeFlag.Name) {
17561809
cfg.MaxBlockRange = ctx.Int64(MaxBlockRangeFlag.Name)
@@ -1816,6 +1869,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
18161869
setLes(ctx, cfg)
18171870
setCircuitCapacityCheck(ctx, cfg)
18181871
setEnableRollupVerify(ctx, cfg)
1872+
setDA(ctx, cfg)
18191873
setMaxBlockRange(ctx, cfg)
18201874
if ctx.IsSet(ShadowforkPeersFlag.Name) {
18211875
cfg.ShadowForkPeerIDs = ctx.StringSlice(ShadowforkPeersFlag.Name)

common/backoff/exponential.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package backoff
2+
3+
import (
4+
"math"
5+
"math/rand"
6+
"time"
7+
)
8+
9+
// Exponential is a backoff strategy that increases the delay between retries exponentially.
10+
type Exponential struct {
11+
attempt int
12+
13+
maxJitter time.Duration
14+
15+
min time.Duration
16+
max time.Duration
17+
}
18+
19+
func NewExponential(minimum, maximum, maxJitter time.Duration) *Exponential {
20+
return &Exponential{
21+
min: minimum,
22+
max: maximum,
23+
maxJitter: maxJitter,
24+
}
25+
}
26+
27+
func (e *Exponential) NextDuration() time.Duration {
28+
var jitter time.Duration
29+
if e.maxJitter > 0 {
30+
jitter = time.Duration(rand.Int63n(e.maxJitter.Nanoseconds()))
31+
}
32+
33+
minFloat := float64(e.min)
34+
duration := math.Pow(2, float64(e.attempt)) * minFloat
35+
36+
// limit at configured maximum
37+
if duration > float64(e.max) {
38+
duration = float64(e.max)
39+
}
40+
41+
e.attempt++
42+
return time.Duration(duration) + jitter
43+
}
44+
45+
func (e *Exponential) Reset() {
46+
e.attempt = 0
47+
}
48+
49+
func (e *Exponential) Attempt() int {
50+
return e.attempt
51+
}

common/backoff/exponential_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package backoff
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestExponentialBackoff(t *testing.T) {
11+
t.Run("Multiple attempts", func(t *testing.T) {
12+
e := NewExponential(100*time.Millisecond, 10*time.Second, 0)
13+
expectedDurations := []time.Duration{
14+
100 * time.Millisecond,
15+
200 * time.Millisecond,
16+
400 * time.Millisecond,
17+
800 * time.Millisecond,
18+
1600 * time.Millisecond,
19+
3200 * time.Millisecond,
20+
6400 * time.Millisecond,
21+
10 * time.Second, // capped at max
22+
}
23+
for i, expected := range expectedDurations {
24+
require.Equal(t, expected, e.NextDuration(), "attempt %d", i)
25+
}
26+
})
27+
28+
t.Run("Jitter added", func(t *testing.T) {
29+
e := NewExponential(1*time.Second, 10*time.Second, 1*time.Second)
30+
duration := e.NextDuration()
31+
require.GreaterOrEqual(t, duration, 1*time.Second)
32+
require.Less(t, duration, 2*time.Second)
33+
})
34+
35+
t.Run("Edge case: min > max", func(t *testing.T) {
36+
e := NewExponential(10*time.Second, 5*time.Second, 0)
37+
require.Equal(t, 5*time.Second, e.NextDuration())
38+
})
39+
}

common/heap.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package common
2+
3+
import (
4+
"container/heap"
5+
)
6+
7+
// Heap is a generic min-heap (or max-heap, depending on Comparable behavior) implementation.
8+
type Heap[T Comparable[T]] struct {
9+
heap innerHeap[T]
10+
}
11+
12+
func NewHeap[T Comparable[T]]() *Heap[T] {
13+
return &Heap[T]{
14+
heap: make(innerHeap[T], 0),
15+
}
16+
}
17+
18+
func (h *Heap[T]) Len() int {
19+
return len(h.heap)
20+
}
21+
22+
func (h *Heap[T]) Push(element T) *HeapElement[T] {
23+
heapElement := NewHeapElement(element)
24+
heap.Push(&h.heap, heapElement)
25+
26+
return heapElement
27+
}
28+
29+
func (h *Heap[T]) Pop() *HeapElement[T] {
30+
return heap.Pop(&h.heap).(*HeapElement[T])
31+
}
32+
33+
func (h *Heap[T]) Peek() *HeapElement[T] {
34+
if h.Len() == 0 {
35+
return nil
36+
}
37+
38+
return h.heap[0]
39+
}
40+
41+
func (h *Heap[T]) Remove(element *HeapElement[T]) {
42+
heap.Remove(&h.heap, element.index)
43+
}
44+
45+
func (h *Heap[T]) Clear() {
46+
h.heap = make(innerHeap[T], 0)
47+
}
48+
49+
type innerHeap[T Comparable[T]] []*HeapElement[T]
50+
51+
func (h innerHeap[T]) Len() int {
52+
return len(h)
53+
}
54+
55+
func (h innerHeap[T]) Less(i, j int) bool {
56+
return h[i].Value().CompareTo(h[j].Value()) < 0
57+
}
58+
59+
func (h innerHeap[T]) Swap(i, j int) {
60+
h[i], h[j] = h[j], h[i]
61+
h[i].index, h[j].index = i, j
62+
}
63+
64+
func (h *innerHeap[T]) Push(x interface{}) {
65+
data := x.(*HeapElement[T])
66+
*h = append(*h, data)
67+
data.index = len(*h) - 1
68+
}
69+
70+
func (h *innerHeap[T]) Pop() interface{} {
71+
n := len(*h)
72+
element := (*h)[n-1]
73+
(*h)[n-1] = nil // avoid memory leak
74+
*h = (*h)[:n-1]
75+
element.index = -1
76+
77+
return element
78+
}
79+
80+
// Comparable is an interface for types that can be compared.
81+
type Comparable[T any] interface {
82+
// CompareTo compares x with other.
83+
// To create a min heap, return:
84+
// -1 if x < other
85+
// 0 if x == other
86+
// +1 if x > other
87+
// To create a max heap, return the opposite.
88+
CompareTo(other T) int
89+
}
90+
91+
// HeapElement is a wrapper around the value stored in the heap.
92+
type HeapElement[T Comparable[T]] struct {
93+
value T
94+
index int
95+
}
96+
97+
func NewHeapElement[T Comparable[T]](value T) *HeapElement[T] {
98+
return &HeapElement[T]{
99+
value: value,
100+
}
101+
}
102+
103+
func (h *HeapElement[T]) Value() T {
104+
return h.value
105+
}
106+
107+
func (h *HeapElement[T]) Index() int {
108+
return h.index
109+
}

common/heap_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package common
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
type Int int
10+
11+
func (i Int) CompareTo(other Int) int {
12+
if i < other {
13+
return -1
14+
} else if i > other {
15+
return 1
16+
} else {
17+
return 0
18+
}
19+
}
20+
21+
func TestHeap(t *testing.T) {
22+
h := NewHeap[Int]()
23+
24+
require.Equal(t, 0, h.Len(), "Heap should be empty initially")
25+
26+
h.Push(Int(3))
27+
h.Push(Int(1))
28+
h.Push(Int(2))
29+
30+
require.Equal(t, 3, h.Len(), "Heap should have three elements after pushing")
31+
32+
require.EqualValues(t, 1, h.Pop(), "Pop should return the smallest element")
33+
require.Equal(t, 2, h.Len(), "Heap should have two elements after popping")
34+
35+
require.EqualValues(t, 2, h.Pop(), "Pop should return the next smallest element")
36+
require.Equal(t, 1, h.Len(), "Heap should have one element after popping")
37+
38+
require.EqualValues(t, 3, h.Pop(), "Pop should return the last element")
39+
require.Equal(t, 0, h.Len(), "Heap should be empty after popping all elements")
40+
}

0 commit comments

Comments
 (0)