Skip to content

Commit 24477ac

Browse files
committed
cmd/statesync: Add command calculating recommended trust
1 parent 49b6f25 commit 24477ac

6 files changed

Lines changed: 180 additions & 0 deletions

File tree

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/oasisprotocol/cli/cmd/network"
1515
"github.com/oasisprotocol/cli/cmd/paratime"
1616
"github.com/oasisprotocol/cli/cmd/rofl"
17+
"github.com/oasisprotocol/cli/cmd/statesync"
1718
"github.com/oasisprotocol/cli/cmd/wallet"
1819
"github.com/oasisprotocol/cli/config"
1920
"github.com/oasisprotocol/cli/version"
@@ -101,6 +102,7 @@ func init() {
101102
rootCmd.AddCommand(network.Cmd)
102103
rootCmd.AddCommand(paratime.Cmd)
103104
rootCmd.AddCommand(wallet.Cmd)
105+
rootCmd.AddCommand(statesync.Cmd)
104106
rootCmd.AddCommand(account.Cmd)
105107
rootCmd.AddCommand(addressBookCmd)
106108
rootCmd.AddCommand(contractCmd)

cmd/statesync/statesync.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Package statesync defines utility comands for the consensus state sync.
2+
package statesync
3+
4+
import (
5+
"github.com/spf13/cobra"
6+
)
7+
8+
// Cmd is the statesync sub-command.
9+
var Cmd = &cobra.Command{
10+
Use: "state-sync",
11+
Short: "State sync utilities for the consensus layer",
12+
}
13+
14+
func init() {
15+
Cmd.AddCommand(trustCmd)
16+
}

cmd/statesync/trust.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package statesync
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/oasisprotocol/oasis-core/go/consensus/api"
9+
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/config"
10+
11+
"github.com/oasisprotocol/cli/cmd/common"
12+
cliConfig "github.com/oasisprotocol/cli/config"
13+
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/connection"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
const (
18+
// maxVerificationTime significantly overestimates the time needed for the
19+
// light client to fetch and verify light headers and possibly detect and penalize
20+
// misbehavior of byzantine node.
21+
maxVerificationTime = 24 * time.Hour
22+
// trustPeriodFraction defines the fraction of the debonding period that determines
23+
// a valid trust period.
24+
//
25+
// According to the CometBFT documentation, the sum of the trust period, the time
26+
// required to verify headers, and the time needed to detect and penalize misbehavior
27+
// should be significantly smaller than the total debonding period.
28+
trustPeriodFraction = 3.0 / 4.0
29+
)
30+
31+
var trustCmd = &cobra.Command{
32+
Use: "trust",
33+
Short: "Show the recommended light client trust for consensus state sync",
34+
Args: cobra.NoArgs,
35+
RunE: func(_ *cobra.Command, _ []string) error {
36+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
37+
defer cancel()
38+
39+
// Establish connection with the target network.
40+
cfg := cliConfig.Global()
41+
npa := common.GetNPASelection(cfg)
42+
conn, err := connection.Connect(ctx, npa.Network)
43+
if err != nil {
44+
return fmt.Errorf("failed to establish connection with the target network: %w", err)
45+
}
46+
47+
trustCfg, err := calcTrustCfg(ctx, conn)
48+
if err != nil {
49+
return fmt.Errorf("failed to calculate consensus state sync trust root: %w", err)
50+
}
51+
52+
switch common.OutputFormat() {
53+
case common.FormatJSON:
54+
str, err := common.PrettyJSONMarshal(trustCfg)
55+
if err != nil {
56+
return fmt.Errorf("failed to pretty json marshal: %w", err)
57+
}
58+
fmt.Println(string(str))
59+
default:
60+
fmt.Println("Trust period: ", trustCfg.Period)
61+
fmt.Println("Trust height: ", trustCfg.Height)
62+
fmt.Println("Trust hash: ", trustCfg.Hash)
63+
}
64+
65+
return nil
66+
},
67+
}
68+
69+
// calculateTrustCfg calculates and verifies the recommended trust config.
70+
func calcTrustCfg(ctx context.Context, conn connection.Connection) (config.TrustConfig, error) {
71+
var trust config.TrustConfig
72+
73+
latestBlk, err := conn.Consensus().Core().GetBlock(ctx, api.HeightLatest)
74+
if err != nil {
75+
return trust, fmt.Errorf("failed to get latest block: %w", err)
76+
}
77+
latestHeight := latestBlk.Height
78+
79+
cpInterval, err := fetchCheckpointInterval(ctx, conn, latestHeight)
80+
if err != nil {
81+
return trust, fmt.Errorf("failed to fetch checkpoint interval: %w", err)
82+
}
83+
84+
blkTime, err := calcAvgBlkTime(ctx, conn, latestBlk)
85+
if err != nil {
86+
return trust, fmt.Errorf("failed to calculate average block time: %w", err)
87+
}
88+
89+
debondingPeriod, err := calcDebondingPeriod(ctx, conn, latestHeight, blkTime)
90+
if err != nil {
91+
return trust, fmt.Errorf("failed to calculate debonding period (height: %d): %w", latestHeight, err)
92+
}
93+
trustPeriod := calcTrustPeriod(debondingPeriod)
94+
95+
// Going back the whole checkpoint interval guarantees there will be at least
96+
// one checkpoint available.
97+
if cpInterval > latestHeight {
98+
return trust, fmt.Errorf("checkpoint interval exceeds latest height")
99+
}
100+
candHeight := latestHeight - cpInterval
101+
candBlk, err := conn.Consensus().Core().GetBlock(ctx, candHeight)
102+
if err != nil {
103+
return trust, fmt.Errorf("failed to get candidate trust (height: %d): %w", candHeight, err)
104+
}
105+
106+
// Sanity check: the trusted root must not be older than the sum of the trust
107+
// period and the maximum verification time. If it is, the light client will
108+
// not be able to safely use this block as a trusted root for the state sync.
109+
if candBlk.Time.Add(trustPeriod).Add(maxVerificationTime).Before(time.Now()) {
110+
return trust, fmt.Errorf("failed to verify candidate trust root")
111+
}
112+
113+
trust.Period = trustPeriod
114+
trust.Height = uint64(candHeight) // #nosec G115
115+
trust.Hash = candBlk.Hash.Hex()
116+
return trust, nil
117+
}
118+
119+
func fetchCheckpointInterval(ctx context.Context, conn connection.Connection, height int64) (int64, error) {
120+
params, err := conn.Consensus().Core().GetParameters(ctx, height)
121+
if err != nil {
122+
return 0, fmt.Errorf("failed to get consensus parameters (height: %d): %w", height, err)
123+
}
124+
return int64(params.Parameters.StateCheckpointInterval), nil // #nosec G115
125+
}
126+
127+
func calcAvgBlkTime(ctx context.Context, conn connection.Connection, latest *api.Block) (time.Duration, error) {
128+
var deltaBlocks int64 = 1000
129+
blk, err := conn.Consensus().Core().GetBlock(ctx, latest.Height-deltaBlocks)
130+
if err != nil {
131+
return 0, fmt.Errorf("failed to get latest block: %w", err)
132+
}
133+
134+
return latest.Time.Sub(blk.Time) / time.Duration(deltaBlocks), nil
135+
}
136+
137+
func calcDebondingPeriod(ctx context.Context, conn connection.Connection, height int64, blkTime time.Duration) (time.Duration, error) {
138+
stakingParams, err := conn.Consensus().Staking().ConsensusParameters(ctx, height)
139+
if err != nil {
140+
return 0, fmt.Errorf("failed to get staking parameters: %w", err)
141+
}
142+
beaconParams, err := conn.Consensus().Beacon().ConsensusParameters(ctx, height)
143+
if err != nil {
144+
return 0, fmt.Errorf("failed to get beacon parameters: %w", err)
145+
}
146+
debondingBlks := int64(stakingParams.DebondingInterval) * beaconParams.Interval() // #nosec G115
147+
return time.Duration(debondingBlks) * blkTime, nil
148+
}
149+
150+
func calcTrustPeriod(debondingPeriod time.Duration) time.Duration {
151+
return time.Duration(float64(debondingPeriod) * trustPeriodFraction)
152+
}
153+
154+
func init() {
155+
trustCmd.Flags().AddFlagSet(common.FormatFlag)
156+
trustCmd.Flags().AddFlagSet(common.SelectorNFlags)
157+
}

examples/setup/first-run.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Available Commands:
1212
network Consensus layer operations
1313
paratime ParaTime layer operations
1414
rofl ROFL app management
15+
state-sync State sync utilities for the consensus layer
1516
transaction Raw transaction operations
1617
update Download and install the newest Oasis CLI release
1718
wallet Manage accounts in the local wallet
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./oasis state-sync trust --network testnet
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Trust period: 239h31m33.6s
2+
Trust height: 29073305
3+
Trust hash: 846151e51599876c91427b2239775994fafa48d3cc5172b7b3d16be105249041

0 commit comments

Comments
 (0)