Don't allow duplicates for light client providers (#7781)

* Allow for zero witness providers

* Verify provider duplicates, fix tests

* Add duplicate provider ID to the error

* Return error on attempt to remove last witness

* Verify duplicates when restoring from store
This commit is contained in:
Anca Zamfir
2022-02-08 22:09:10 +01:00
committed by GitHub
parent fd50d90b70
commit 27ccf3b590
9 changed files with 165 additions and 120 deletions

View File

@@ -142,14 +142,29 @@ type Client struct {
logger log.Logger
}
func validatePrimaryAndWitnesses(primary provider.Provider, witnesses []provider.Provider) error {
witnessMap := make(map[string]struct{})
for _, w := range witnesses {
if w.ID() == primary.ID() {
return fmt.Errorf("primary (%s) cannot be also configured as witness", primary.ID())
}
if _, duplicate := witnessMap[w.ID()]; duplicate {
return fmt.Errorf("witness list must not contain duplicates; duplicate found: %s", w.ID())
}
witnessMap[w.ID()] = struct{}{}
}
return nil
}
// NewClient returns a new light client. It returns an error if it fails to
// obtain the light block from the primary or they are invalid (e.g. trust
// obtain the light block from the primary, or they are invalid (e.g. trust
// hash does not match with the one from the headers).
//
// Witnesses are providers, which will be used for cross-checking the primary
// provider. At least one witness must be given when skipping verification is
// used (default). A witness can become a primary iff the current primary is
// unavailable.
// provider. At least one witness should be given when skipping verification is
// used (default). A verified header is compared with the headers at same height
// obtained from the specified witnesses. A witness can become a primary iff the
// current primary is unavailable.
//
// See all Option(s) for the additional configuration.
func NewClient(
@@ -174,16 +189,16 @@ func NewClient(
)
}
// Check that the witness list does not include duplicates or the primary
if err := validatePrimaryAndWitnesses(primary, witnesses); err != nil {
return nil, err
}
// Validate trust options
if err := trustOptions.ValidateBasic(); err != nil {
return nil, fmt.Errorf("invalid TrustOptions: %w", err)
}
// Validate the number of witnesses.
if len(witnesses) < 1 {
return nil, ErrNoWitnesses
}
c := &Client{
chainID: chainID,
trustingPeriod: trustOptions.Period,
@@ -226,6 +241,11 @@ func NewClientFromTrustedStore(
trustedStore store.Store,
options ...Option) (*Client, error) {
// Check that the witness list does not include duplicates or the primary
if err := validatePrimaryAndWitnesses(primary, witnesses); err != nil {
return nil, err
}
c := &Client{
chainID: chainID,
trustingPeriod: trustingPeriod,
@@ -244,11 +264,6 @@ func NewClientFromTrustedStore(
o(c)
}
// Validate the number of witnesses.
if len(c.witnesses) < 1 {
return nil, ErrNoWitnesses
}
// Validate trust level.
if err := ValidateTrustLevel(c.trustLevel); err != nil {
return nil, err
@@ -960,7 +975,6 @@ func (c *Client) getLightBlock(ctx context.Context, p provider.Provider, height
// NOTE: requires a providerMutex lock
func (c *Client) removeWitnesses(indexes []int) error {
// check that we will still have witnesses remaining
if len(c.witnesses) <= len(indexes) {
return ErrNoWitnesses
}
@@ -1082,7 +1096,7 @@ func (c *Client) compareFirstHeaderWithWitnesses(ctx context.Context, h *types.S
defer c.providerMutex.Unlock()
if len(c.witnesses) < 1 {
return ErrNoWitnesses
return nil
}
errc := make(chan error, len(c.witnesses))