Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions sidecar/client/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,16 +338,27 @@ func (t ConfigPatchTask) ToTaskRequest() TaskRequest {
// height as the trust height and sets use-local-snapshot = true in config.toml.
type ConfigureStateSyncTask struct {
UseLocalSnapshot bool
TrustPeriod string
BackfillBlocks int64
}

func (t ConfigureStateSyncTask) TaskType() string { return TaskTypeConfigureStateSync }
func (t ConfigureStateSyncTask) Validate() error { return nil }

func (t ConfigureStateSyncTask) ToTaskRequest() TaskRequest {
if !t.UseLocalSnapshot {
p := map[string]interface{}{}
if t.UseLocalSnapshot {
p["useLocalSnapshot"] = true
}
if t.TrustPeriod != "" {
p["trustPeriod"] = t.TrustPeriod
}
if t.BackfillBlocks > 0 {
p["backfillBlocks"] = t.BackfillBlocks
}
if len(p) == 0 {
return TaskRequest{Type: t.TaskType()}
}
p := map[string]interface{}{"useLocalSnapshot": true}
return TaskRequest{Type: t.TaskType(), Params: &p}
}

Expand Down
60 changes: 44 additions & 16 deletions sidecar/tasks/statesync.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ const (
type StateSyncConfig struct {
TrustHeight int64
TrustHash string
TrustPeriod string
RpcServers string
UseLocalSnapshot bool
BackfillBlocks int64
}

// HTTPDoer abstracts HTTP requests for testability.
Expand All @@ -56,15 +58,34 @@ func NewStateSyncConfigurer(homeDir string, client HTTPDoer) *StateSyncConfigure
func (s *StateSyncConfigurer) Handler() engine.TaskHandler {
return func(ctx context.Context, params map[string]any) error {
useLocal, _ := params["useLocalSnapshot"].(bool)
return s.Configure(ctx, useLocal)
trustPeriod, _ := params["trustPeriod"].(string)
var backfill int64
switch v := params["backfillBlocks"].(type) {
case float64:
backfill = int64(v)
case int64:
backfill = v
}
return s.Configure(ctx, StateSyncParams{
UseLocalSnapshot: useLocal,
TrustPeriod: trustPeriod,
BackfillBlocks: backfill,
})
}
}

// StateSyncParams groups the caller-provided parameters for state-sync configuration.
type StateSyncParams struct {
UseLocalSnapshot bool
TrustPeriod string
BackfillBlocks int64
}

// Configure reads persistent-peers from config.toml, queries a peer for a
// trust point, and writes the state sync settings back to config.toml.
// When useLocalSnapshot is true, the trust height is derived from the
// When UseLocalSnapshot is true, the trust height is derived from the
// locally-restored snapshot and use-local-snapshot is set in config.toml.
func (s *StateSyncConfigurer) Configure(ctx context.Context, useLocalSnapshot bool) error {
func (s *StateSyncConfigurer) Configure(ctx context.Context, p StateSyncParams) error {
if markerExists(s.homeDir, stateSyncMarkerFile) {
ssLog.Debug("already completed, skipping")
return nil
Expand All @@ -85,7 +106,7 @@ func (s *StateSyncConfigurer) Configure(ctx context.Context, useLocalSnapshot bo
}

var trustHeight int64
if useLocalSnapshot {
if p.UseLocalSnapshot {
h, err := discoverLocalSnapshotHeight(s.homeDir)
if err != nil {
return fmt.Errorf("configure-state-sync: discovering local snapshot height: %w", err)
Expand Down Expand Up @@ -121,12 +142,15 @@ func (s *StateSyncConfigurer) Configure(ctx context.Context, useLocalSnapshot bo
cfg := StateSyncConfig{
TrustHeight: trustHeight,
TrustHash: trustHash,
TrustPeriod: p.TrustPeriod,
RpcServers: strings.Join(rpcServers, ","),
UseLocalSnapshot: useLocalSnapshot,
UseLocalSnapshot: p.UseLocalSnapshot,
BackfillBlocks: p.BackfillBlocks,
}

ssLog.Info("writing config", "trust-height", trustHeight, "trust-hash", trustHash,
"rpc-servers", cfg.RpcServers, "use-local-snapshot", useLocalSnapshot)
"trust-period", p.TrustPeriod, "rpc-servers", cfg.RpcServers,
"use-local-snapshot", p.UseLocalSnapshot, "backfill-blocks", p.BackfillBlocks)
if err := writeStateSyncToConfig(s.homeDir, cfg); err != nil {
return fmt.Errorf("configure-state-sync: writing config.toml: %w", err)
}
Expand Down Expand Up @@ -251,16 +275,20 @@ func (s *StateSyncConfigurer) doGet(ctx context.Context, url string) ([]byte, er

func writeStateSyncToConfig(homeDir string, cfg StateSyncConfig) error {
configPath := filepath.Join(homeDir, "config", "config.toml")
ssPatch := map[string]any{
"statesync": map[string]any{
"enable": true,
"trust-height": cfg.TrustHeight,
"trust-hash": cfg.TrustHash,
"rpc-servers": cfg.RpcServers,
"use-local-snapshot": cfg.UseLocalSnapshot,
},
}
return mergeAndWrite(configPath, ssPatch)
ss := map[string]any{
"enable": true,
"trust-height": cfg.TrustHeight,
"trust-hash": cfg.TrustHash,
"rpc-servers": cfg.RpcServers,
"use-local-snapshot": cfg.UseLocalSnapshot,
}
if cfg.TrustPeriod != "" {
ss["trust-period"] = cfg.TrustPeriod
}
if cfg.BackfillBlocks > 0 {
ss["backfill-blocks"] = cfg.BackfillBlocks
}
return mergeAndWrite(configPath, map[string]any{"statesync": ss})
}

// readPeersFromConfig reads the persistent-peers value from config.toml and
Expand Down
60 changes: 53 additions & 7 deletions sidecar/tasks/statesync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestStateSyncConfigurer_Success(t *testing.T) {
}

configurer := NewStateSyncConfigurer(homeDir, mock)
if err := configurer.Configure(context.Background(), false); err != nil {
if err := configurer.Configure(context.Background(), StateSyncParams{}); err != nil {
t.Fatalf("Configure failed: %v", err)
}

Expand Down Expand Up @@ -101,7 +101,7 @@ func TestStateSyncConfigurer_MarkerSkips(t *testing.T) {
}

configurer := NewStateSyncConfigurer(homeDir, &mockHTTPDoer{})
if err := configurer.Configure(context.Background(), false); err != nil {
if err := configurer.Configure(context.Background(), StateSyncParams{}); err != nil {
t.Fatalf("expected nil error when marker exists, got: %v", err)
}
}
Expand All @@ -111,7 +111,7 @@ func TestStateSyncConfigurer_NoPeers(t *testing.T) {
setupPeersInConfig(t, homeDir, nil)

configurer := NewStateSyncConfigurer(homeDir, &mockHTTPDoer{})
err := configurer.Configure(context.Background(), false)
err := configurer.Configure(context.Background(), StateSyncParams{})
if err == nil {
t.Fatal("expected error when no peers in config")
}
Expand All @@ -134,7 +134,7 @@ func TestStateSyncConfigurer_LowHeight(t *testing.T) {
}

configurer := NewStateSyncConfigurer(homeDir, mock)
if err := configurer.Configure(context.Background(), false); err != nil {
if err := configurer.Configure(context.Background(), StateSyncParams{}); err != nil {
t.Fatalf("Configure failed: %v", err)
}

Expand Down Expand Up @@ -180,7 +180,7 @@ func TestStateSyncConfigurer_InvalidBlockHash(t *testing.T) {
}

configurer := NewStateSyncConfigurer(homeDir, mock)
err := configurer.Configure(context.Background(), false)
err := configurer.Configure(context.Background(), StateSyncParams{})
if err == nil {
t.Fatal("expected error")
}
Expand Down Expand Up @@ -281,6 +281,44 @@ func TestStateSyncConfigurer_Handler(t *testing.T) {
}
}

func TestStateSyncConfigurer_NetworkWithBackfill(t *testing.T) {
homeDir := t.TempDir()
setupPeersInConfig(t, homeDir, []string{"nodeId1@1.2.3.4:26656", "nodeId2@5.6.7.8:26656"})

hash := generateBlockHash()
mock := &mockHTTPDoer{
responses: map[string]*http.Response{
"http://1.2.3.4:26657/status": jsonResponse(`{
"sync_info": {"latest_block_height": "10000"}
}`),
"http://1.2.3.4:26657/block?height=8000": jsonResponse(fmt.Sprintf(`{
"block_id": {"hash": %q}
}`, hash)),
},
}

configurer := NewStateSyncConfigurer(homeDir, mock)
err := configurer.Configure(context.Background(), StateSyncParams{
TrustPeriod: "168h0m0s",
BackfillBlocks: 6000,
})
if err != nil {
t.Fatalf("Configure failed: %v", err)
}

configDoc := readTOML(t, filepath.Join(homeDir, "config", "config.toml"))
ss := configDoc["statesync"].(map[string]any)
if ss["trust-period"] != "168h0m0s" {
t.Errorf("expected trust-period = 168h0m0s, got %v", ss["trust-period"])
}
if ss["backfill-blocks"] != int64(6000) {
t.Errorf("expected backfill-blocks = 6000, got %v", ss["backfill-blocks"])
}
if ss["use-local-snapshot"] != false {
t.Errorf("expected use-local-snapshot = false, got %v", ss["use-local-snapshot"])
}
}

func TestStateSyncConfigurer_LocalSnapshot(t *testing.T) {
homeDir := t.TempDir()
setupPeersInConfig(t, homeDir, []string{"nodeId1@1.2.3.4:26656", "nodeId2@5.6.7.8:26656"})
Expand All @@ -301,7 +339,12 @@ func TestStateSyncConfigurer_LocalSnapshot(t *testing.T) {
}

configurer := NewStateSyncConfigurer(homeDir, mock)
if err := configurer.Configure(context.Background(), true); err != nil {
err := configurer.Configure(context.Background(), StateSyncParams{
UseLocalSnapshot: true,
TrustPeriod: "9999h0m0s",
BackfillBlocks: 0,
})
if err != nil {
t.Fatalf("Configure with local snapshot failed: %v", err)
}

Expand All @@ -317,6 +360,9 @@ func TestStateSyncConfigurer_LocalSnapshot(t *testing.T) {
if ss["use-local-snapshot"] != true {
t.Errorf("expected use-local-snapshot = true, got %v", ss["use-local-snapshot"])
}
if ss["trust-period"] != "9999h0m0s" {
t.Errorf("expected trust-period = 9999h0m0s, got %v", ss["trust-period"])
}
if ss["enable"] != true {
t.Error("expected statesync.enable = true")
}
Expand All @@ -327,7 +373,7 @@ func TestStateSyncConfigurer_LocalSnapshotNoDir(t *testing.T) {
setupPeersInConfig(t, homeDir, []string{"nodeId1@1.2.3.4:26656"})

configurer := NewStateSyncConfigurer(homeDir, &mockHTTPDoer{})
err := configurer.Configure(context.Background(), true)
err := configurer.Configure(context.Background(), StateSyncParams{UseLocalSnapshot: true})
if err == nil {
t.Fatal("expected error when no snapshot directory exists")
}
Expand Down
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "v0.0.13"
"version": "v0.0.14"
}
Loading