From af37db39b0c68ec7e070333ee6b57cc778ac7d71 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Thu, 6 Feb 2020 15:36:13 +0100 Subject: [PATCH] lite2: cross-check new header with all witnesses (#4373) As opposed to checking a random witness, all witnesses provided should be used as a reference against the header provided by the primary node. This increases security (at the tradeoff of speed) but also gives control to the user. The more witnesses provided, the more secure the lite client can be. --- lite2/client.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lite2/client.go b/lite2/client.go index c46160712..c6e2077dd 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -11,7 +11,6 @@ import ( "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" - tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/lite2/provider" "github.com/tendermint/tendermint/lite2/store" "github.com/tendermint/tendermint/types" @@ -558,7 +557,7 @@ func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.Vali return errors.Errorf("header at more recent height #%d exists", c.trustedHeader.Height) } - if err := c.compareNewHeaderWithRandomWitness(newHeader); err != nil { + if err := c.compareNewHeaderWithWitnesses(newHeader); err != nil { c.logger.Error("Error when comparing new header with one from a witness", "err", err) return err } @@ -855,32 +854,33 @@ func (c *Client) backwards(toHeight int64, fromHeader *types.SignedHeader, now t return trustedHeader, nil } -// compare header with one from a random witness. -func (c *Client) compareNewHeaderWithRandomWitness(h *types.SignedHeader) error { +// compare header with all witnesses provided. +func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error { c.providerMutex.Lock() + defer c.providerMutex.Unlock() // 0. Check witnesses exist if len(c.witnesses) == 0 { return errors.New("could not find any witnesses") } - // 1. Pick a witness. - witness := c.witnesses[tmrand.Intn(len(c.witnesses))] - c.providerMutex.Unlock() + // 1. Loop through all witnesses. + for _, witness := range c.witnesses { - // 2. Fetch the header. - altH, err := witness.SignedHeader(h.Height) - if err != nil { - return errors.Wrapf(err, - "failed to obtain header #%d from the witness %v", h.Height, witness) - } + // 2. Fetch the header. + altH, err := witness.SignedHeader(h.Height) + if err != nil { + return errors.Wrapf(err, + "failed to obtain header #%d from the witness %v", h.Height, witness) + } - // 3. Compare hashes. - if !bytes.Equal(h.Hash(), altH.Hash()) { - // TODO: One of the providers is lying. Send the evidence to fork - // accountability server. - return errors.Errorf( - "header hash %X does not match one %X from the witness %v", - h.Hash(), altH.Hash(), witness) + // 3. Compare hashes. + if !bytes.Equal(h.Hash(), altH.Hash()) { + // TODO: One of the providers is lying. Send the evidence to fork + // accountability server. + return errors.Errorf( + "header hash %X does not match one %X from the witness %v", + h.Hash(), altH.Hash(), witness) + } } return nil