mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-19 22:44:24 +00:00
statesync: implement p2p state provider (#6807)
This commit is contained in:
+34
-22
@@ -52,6 +52,8 @@ const (
|
||||
|
||||
// 10s is sufficient for most networks.
|
||||
defaultMaxBlockLag = 10 * time.Second
|
||||
|
||||
defaultProviderTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// Option sets a parameter for the light client.
|
||||
@@ -61,9 +63,7 @@ type Option func(*Client)
|
||||
// check the blocks (every block, in ascending height order). Note this is
|
||||
// much slower than SkippingVerification, albeit more secure.
|
||||
func SequentialVerification() Option {
|
||||
return func(c *Client) {
|
||||
c.verificationMode = sequential
|
||||
}
|
||||
return func(c *Client) { c.verificationMode = sequential }
|
||||
}
|
||||
|
||||
// SkippingVerification option configures the light client to skip blocks as
|
||||
@@ -87,24 +87,18 @@ func SkippingVerification(trustLevel tmmath.Fraction) Option {
|
||||
// the h amount of light blocks will be removed from the store.
|
||||
// Default: 1000. A pruning size of 0 will not prune the light client at all.
|
||||
func PruningSize(h uint16) Option {
|
||||
return func(c *Client) {
|
||||
c.pruningSize = h
|
||||
}
|
||||
return func(c *Client) { c.pruningSize = h }
|
||||
}
|
||||
|
||||
// Logger option can be used to set a logger for the client.
|
||||
func Logger(l log.Logger) Option {
|
||||
return func(c *Client) {
|
||||
c.logger = l
|
||||
}
|
||||
return func(c *Client) { c.logger = l }
|
||||
}
|
||||
|
||||
// MaxClockDrift defines how much new header's time can drift into
|
||||
// the future relative to the light clients local time. Default: 10s.
|
||||
func MaxClockDrift(d time.Duration) Option {
|
||||
return func(c *Client) {
|
||||
c.maxClockDrift = d
|
||||
}
|
||||
return func(c *Client) { c.maxClockDrift = d }
|
||||
}
|
||||
|
||||
// MaxBlockLag represents the maximum time difference between the realtime
|
||||
@@ -116,9 +110,13 @@ func MaxClockDrift(d time.Duration) Option {
|
||||
// was 12:00. Then the lag here is 5 minutes.
|
||||
// Default: 10s
|
||||
func MaxBlockLag(d time.Duration) Option {
|
||||
return func(c *Client) {
|
||||
c.maxBlockLag = d
|
||||
}
|
||||
return func(c *Client) { c.maxBlockLag = d }
|
||||
}
|
||||
|
||||
// Provider timeout is the maximum time that the light client will wait for a
|
||||
// provider to respond with a light block.
|
||||
func ProviderTimeout(d time.Duration) Option {
|
||||
return func(c *Client) { c.providerTimeout = d }
|
||||
}
|
||||
|
||||
// Client represents a light client, connected to a single chain, which gets
|
||||
@@ -133,6 +131,7 @@ type Client struct {
|
||||
trustLevel tmmath.Fraction
|
||||
maxClockDrift time.Duration
|
||||
maxBlockLag time.Duration
|
||||
providerTimeout time.Duration
|
||||
|
||||
// Mutex for locking during changes of the light clients providers
|
||||
providerMutex tmsync.Mutex
|
||||
@@ -197,12 +196,13 @@ func NewClient(
|
||||
chainID: chainID,
|
||||
trustingPeriod: trustOptions.Period,
|
||||
verificationMode: skipping,
|
||||
trustLevel: DefaultTrustLevel,
|
||||
maxClockDrift: defaultMaxClockDrift,
|
||||
maxBlockLag: defaultMaxBlockLag,
|
||||
primary: primary,
|
||||
witnesses: witnesses,
|
||||
trustedStore: trustedStore,
|
||||
trustLevel: DefaultTrustLevel,
|
||||
maxClockDrift: defaultMaxClockDrift,
|
||||
maxBlockLag: defaultMaxBlockLag,
|
||||
providerTimeout: defaultProviderTimeout,
|
||||
pruningSize: defaultPruningSize,
|
||||
logger: log.NewNopLogger(),
|
||||
}
|
||||
@@ -693,7 +693,9 @@ func (c *Client) verifySkipping(
|
||||
if depth == len(blockCache)-1 {
|
||||
// schedule what the next height we need to fetch is
|
||||
pivotHeight := c.schedule(verifiedBlock.Height, blockCache[depth].Height)
|
||||
interimBlock, providerErr := source.LightBlock(ctx, pivotHeight)
|
||||
subCtx, cancel := context.WithTimeout(ctx, c.providerTimeout)
|
||||
defer cancel()
|
||||
interimBlock, providerErr := c.getLightBlock(subCtx, source, pivotHeight)
|
||||
if providerErr != nil {
|
||||
return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: pivotHeight, Reason: providerErr}
|
||||
}
|
||||
@@ -930,7 +932,7 @@ func (c *Client) backwards(
|
||||
// any other error, the primary is permanently dropped and is replaced by a witness.
|
||||
func (c *Client) lightBlockFromPrimary(ctx context.Context, height int64) (*types.LightBlock, error) {
|
||||
c.providerMutex.Lock()
|
||||
l, err := c.primary.LightBlock(ctx, height)
|
||||
l, err := c.getLightBlock(ctx, c.primary, height)
|
||||
c.providerMutex.Unlock()
|
||||
|
||||
switch err {
|
||||
@@ -957,6 +959,16 @@ func (c *Client) lightBlockFromPrimary(ctx context.Context, height int64) (*type
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) getLightBlock(ctx context.Context, p provider.Provider, height int64) (*types.LightBlock, error) {
|
||||
subCtx, cancel := context.WithTimeout(ctx, c.providerTimeout)
|
||||
defer cancel()
|
||||
l, err := p.LightBlock(subCtx, height)
|
||||
if err == context.DeadlineExceeded || ctx.Err() != nil {
|
||||
return nil, provider.ErrNoResponse
|
||||
}
|
||||
return l, err
|
||||
}
|
||||
|
||||
// NOTE: requires a providerMutex lock
|
||||
func (c *Client) removeWitnesses(indexes []int) error {
|
||||
// check that we will still have witnesses remaining
|
||||
@@ -989,7 +1001,7 @@ func (c *Client) findNewPrimary(ctx context.Context, height int64, remove bool)
|
||||
c.providerMutex.Lock()
|
||||
defer c.providerMutex.Unlock()
|
||||
|
||||
if len(c.witnesses) <= 1 {
|
||||
if len(c.witnesses) < 1 {
|
||||
return nil, ErrNoWitnesses
|
||||
}
|
||||
|
||||
@@ -1001,7 +1013,7 @@ func (c *Client) findNewPrimary(ctx context.Context, height int64, remove bool)
|
||||
)
|
||||
|
||||
// send out a light block request to all witnesses
|
||||
subctx, cancel := context.WithCancel(ctx)
|
||||
subctx, cancel := context.WithTimeout(ctx, c.providerTimeout)
|
||||
defer cancel()
|
||||
for index := range c.witnesses {
|
||||
wg.Add(1)
|
||||
|
||||
+24
-43
@@ -724,51 +724,32 @@ func TestClient_BackwardsVerification(t *testing.T) {
|
||||
|
||||
}
|
||||
{
|
||||
testCases := []struct {
|
||||
headers map[int64]*types.SignedHeader
|
||||
vals map[int64]*types.ValidatorSet
|
||||
}{
|
||||
{
|
||||
// 7) provides incorrect height
|
||||
headers: map[int64]*types.SignedHeader{
|
||||
2: keys.GenSignedHeader(chainID, 1, bTime.Add(30*time.Minute), nil, vals, vals,
|
||||
hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
|
||||
3: h3,
|
||||
},
|
||||
vals: valSet,
|
||||
},
|
||||
{
|
||||
// 8) provides incorrect hash
|
||||
headers: map[int64]*types.SignedHeader{
|
||||
2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
|
||||
hash("app_hash2"), hash("cons_hash23"), hash("results_hash30"), 0, len(keys)),
|
||||
3: h3,
|
||||
},
|
||||
vals: valSet,
|
||||
},
|
||||
// 8) provides incorrect hash
|
||||
headers := map[int64]*types.SignedHeader{
|
||||
2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
|
||||
hash("app_hash2"), hash("cons_hash23"), hash("results_hash30"), 0, len(keys)),
|
||||
3: h3,
|
||||
}
|
||||
vals := valSet
|
||||
mockNode := mockNodeFromHeadersAndVals(headers, vals)
|
||||
c, err := light.NewClient(
|
||||
ctx,
|
||||
chainID,
|
||||
light.TrustOptions{
|
||||
Period: 1 * time.Hour,
|
||||
Height: 3,
|
||||
Hash: h3.Hash(),
|
||||
},
|
||||
mockNode,
|
||||
[]provider.Provider{mockNode},
|
||||
dbs.New(dbm.NewMemDB()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
for idx, tc := range testCases {
|
||||
mockNode := mockNodeFromHeadersAndVals(tc.headers, tc.vals)
|
||||
c, err := light.NewClient(
|
||||
ctx,
|
||||
chainID,
|
||||
light.TrustOptions{
|
||||
Period: 1 * time.Hour,
|
||||
Height: 3,
|
||||
Hash: h3.Hash(),
|
||||
},
|
||||
mockNode,
|
||||
[]provider.Provider{mockNode},
|
||||
dbs.New(dbm.NewMemDB()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err, idx)
|
||||
|
||||
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour).Add(1*time.Second))
|
||||
assert.Error(t, err, idx)
|
||||
mockNode.AssertExpectations(t)
|
||||
}
|
||||
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour).Add(1*time.Second))
|
||||
assert.Error(t, err)
|
||||
mockNode.AssertExpectations(t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -110,7 +110,7 @@ func (c *Client) detectDivergence(ctx context.Context, primaryTrace []*types.Lig
|
||||
func (c *Client) compareNewHeaderWithWitness(ctx context.Context, errc chan error, h *types.SignedHeader,
|
||||
witness provider.Provider, witnessIndex int) {
|
||||
|
||||
lightBlock, err := witness.LightBlock(ctx, h.Height)
|
||||
lightBlock, err := c.getLightBlock(ctx, witness, h.Height)
|
||||
switch err {
|
||||
// no error means we move on to checking the hash of the two headers
|
||||
case nil:
|
||||
@@ -331,7 +331,7 @@ func (c *Client) examineConflictingHeaderAgainstTrace(
|
||||
if traceBlock.Height == targetBlock.Height {
|
||||
sourceBlock = targetBlock
|
||||
} else {
|
||||
sourceBlock, err = source.LightBlock(ctx, traceBlock.Height)
|
||||
sourceBlock, err = c.getLightBlock(ctx, source, traceBlock.Height)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to examine trace: %w", err)
|
||||
}
|
||||
@@ -379,7 +379,7 @@ func (c *Client) getTargetBlockOrLatest(
|
||||
height int64,
|
||||
witness provider.Provider,
|
||||
) (bool, *types.LightBlock, error) {
|
||||
lightBlock, err := witness.LightBlock(ctx, 0)
|
||||
lightBlock, err := c.getLightBlock(ctx, witness, 0)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
@@ -394,7 +394,7 @@ func (c *Client) getTargetBlockOrLatest(
|
||||
// the witness has caught up. We recursively call the function again. However in order
|
||||
// to avoud a wild goose chase where the witness sends us one header below and one header
|
||||
// above the height we set a timeout to the context
|
||||
lightBlock, err := witness.LightBlock(ctx, height)
|
||||
lightBlock, err := c.getLightBlock(ctx, witness, height)
|
||||
return true, lightBlock, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user