Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement validators rpc #78

Merged
merged 2 commits into from
May 28, 2020
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
1 change: 1 addition & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Blockchain Protocol

### FEATURES:
- [types] [\#78](https://github.com/line/tendermint/pull/78) Add validators rpc

### IMPROVEMENTS:

Expand Down
25 changes: 25 additions & 0 deletions lite/client/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,31 @@ func (p *provider) fetchLatestCommit(minHeight int64, maxHeight int64) (*ctypes.
}

// Implements Provider.
func (p *provider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
return p.getValidatorSet(chainID, height)
}

func (p *provider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
if chainID != p.chainID {
err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
return
}
if height < 1 {
err = fmt.Errorf("expected height >= 1, got height %v", height)
return
}

var res *ctypes.ResultValidators
res, err = p.client.Validators(&height, 0, 0)

if err != nil {
// TODO pass through other types of errors.
return nil, lerr.ErrUnknownValidators(chainID, height)
}
valset = types.NewValidatorSet(res.Validators)
return
}

func (p *provider) VoterSet(chainID string, height int64) (valset *types.VoterSet, err error) {
return p.getVoterSet(chainID, height)
}
Expand Down
31 changes: 31 additions & 0 deletions lite/dbprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,37 @@ func (dbp *DBProvider) LatestFullCommit(chainID string, minHeight, maxHeight int
return FullCommit{}, lerr.ErrCommitNotFound()
}

func (dbp *DBProvider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
return dbp.getValidatorSet(chainID, height)
}

func (dbp *DBProvider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
vsBz, err := dbp.db.Get(voterSetKey(chainID, height))
if err != nil {
return nil, err
}
if len(vsBz) == 0 {
err = lerr.ErrUnknownValidators(chainID, height)
return
}
var voterSet *types.VoterSet
err = dbp.cdc.UnmarshalBinaryLengthPrefixed(vsBz, &voterSet)
if err != nil {
return
}

voterSet.TotalVotingPower()

// cannot get validator set in lite
// so after getting voter set and convert it to validator set
valset = &types.ValidatorSet{
Validators: voterSet.Voters,
}
valset.TotalVotingPower()

return
}

func (dbp *DBProvider) VoterSet(chainID string, height int64) (valset *types.VoterSet, err error) {
return dbp.getVoterSet(chainID, height)
}
Expand Down
11 changes: 11 additions & 0 deletions lite/multiprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ func (mc *multiProvider) LatestFullCommit(chainID string, minHeight, maxHeight i
return
}

func (mc *multiProvider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
for _, p := range mc.providers {
valset, err = p.ValidatorSet(chainID, height)
if err == nil {
// TODO Log unexpected types of errors.
return valset, nil
}
}
return nil, lerr.ErrUnknownValidators(chainID, height)
}

// VoterSet returns validator set at height as provided by the first
// provider which has it, or an error otherwise.
func (mc *multiProvider) VoterSet(chainID string, height int64) (valset *types.VoterSet, err error) {
Expand Down
2 changes: 2 additions & 0 deletions lite/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type Provider interface {
// If maxHeight is zero, returns the latest where minHeight <= height.
LatestFullCommit(chainID string, minHeight, maxHeight int64) (FullCommit, error)

ValidatorSet(chainID string, height int64) (*types.ValidatorSet, error)

// Get the voterSet that corresponds to chainID and height and return.
// Height must be >= 1.
VoterSet(chainID string, height int64) (*types.VoterSet, error)
Expand Down
3 changes: 3 additions & 0 deletions lite/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ func (missingProvider) SaveFullCommit(FullCommit) error { return nil }
func (missingProvider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (FullCommit, error) {
return FullCommit{}, lerr.ErrCommitNotFound()
}
func (missingProvider) ValidatorSet(chainID string, height int64) (*types.ValidatorSet, error) {
return nil, errors.New("missing validator set")
}
func (missingProvider) VoterSet(chainID string, height int64) (*types.VoterSet, error) {
return nil, errors.New("missing voter set")
}
Expand Down
56 changes: 28 additions & 28 deletions lite2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func NewClientFromTrustedStore(
return nil, err
}

if err := c.restoreTrustedHeaderAndVals(); err != nil {
if err := c.restoreTrustedHeaderAndVoters(); err != nil {
return nil, err
}

Expand All @@ -227,7 +227,7 @@ func NewClientFromTrustedStore(

// restoreTrustedHeaderAndVals loads trustedHeader and trustedVals from
// trustedStore.
func (c *Client) restoreTrustedHeaderAndVals() error {
func (c *Client) restoreTrustedHeaderAndVoters() error {
lastHeight, err := c.trustedStore.LastSignedHeaderHeight()
if err != nil {
return errors.Wrap(err, "can't get last trusted header height")
Expand Down Expand Up @@ -355,26 +355,26 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
}

// 2) Fetch and verify the vals.
vals, err := c.validatorSetFromPrimary(options.Height)
voters, err := c.voterSetFromPrimary(options.Height)
if err != nil {
return err
}

if !bytes.Equal(h.VotersHash, vals.Hash()) {
if !bytes.Equal(h.VotersHash, voters.Hash()) {
return errors.Errorf("expected header's validators (%X) to match those that were supplied (%X)",
h.VotersHash,
vals.Hash(),
voters.Hash(),
)
}

// Ensure that +2/3 of validators signed correctly.
err = vals.VerifyCommit(c.chainID, h.Commit.BlockID, h.Height, h.Commit)
err = voters.VerifyCommit(c.chainID, h.Commit.BlockID, h.Height, h.Commit)
if err != nil {
return errors.Wrap(err, "invalid commit")
}

// 3) Persist both of them and continue.
return c.updateTrustedHeaderAndVals(h, vals)
return c.updateTrustedHeaderAndVoters(h, voters)
}

// TrustedHeader returns a trusted header at the given height (0 - the latest).
Expand All @@ -400,7 +400,7 @@ func (c *Client) TrustedHeader(height int64) (*types.SignedHeader, error) {
return c.trustedStore.SignedHeader(height)
}

// TrustedValidatorSet returns a trusted validator set at the given height (0 -
// TrustedVoterSet returns a trusted voter set at the given height (0 -
// latest). The second return parameter is the height used (useful if 0 was
// passed; otherwise can be ignored).
//
Expand All @@ -417,7 +417,7 @@ func (c *Client) TrustedHeader(height int64) (*types.SignedHeader, error) {
// - header signed by that validator set has not been verified yet
//
// Safe for concurrent use by multiple goroutines.
func (c *Client) TrustedValidatorSet(height int64) (valSet *types.VoterSet, heightUsed int64, err error) {
func (c *Client) TrustedVoterSet(height int64) (valSet *types.VoterSet, heightUsed int64, err error) {
heightUsed, err = c.compareWithLatestHeight(height)
if err != nil {
return nil, heightUsed, err
Expand Down Expand Up @@ -495,7 +495,7 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe
}

// Request the header and the vals.
newHeader, newVals, err := c.fetchHeaderAndValsAtHeight(height)
newHeader, newVals, err := c.fetchHeaderAndVotersAtHeight(height)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -582,7 +582,7 @@ func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.Vote
return err
}

return c.updateTrustedHeaderAndVals(newHeader, newVals)
return c.updateTrustedHeaderAndVoters(newHeader, newVals)
}

// Primary returns the primary provider.
Expand Down Expand Up @@ -636,7 +636,7 @@ func (c *Client) cleanupAfter(height int64) error {

c.latestTrustedHeader = nil
c.latestTrustedVals = nil
err := c.restoreTrustedHeaderAndVals()
err := c.restoreTrustedHeaderAndVoters()
if err != nil {
return err
}
Expand Down Expand Up @@ -665,7 +665,7 @@ func (c *Client) sequence(
if height == newHeader.Height { // last header
interimHeader, interimVals = newHeader, newVals
} else { // intermediate headers
interimHeader, interimVals, err = c.fetchHeaderAndValsAtHeight(height)
interimHeader, interimVals, err = c.fetchHeaderAndVotersAtHeight(height)
if err != nil {
return errors.Wrapf(err, "failed to obtain the header #%d", height)
}
Expand Down Expand Up @@ -710,17 +710,17 @@ func (c *Client) sequence(
// see VerifyHeader
func (c *Client) bisection(
initiallyTrustedHeader *types.SignedHeader,
initiallyTrustedVals *types.VoterSet,
initiallyTrustedVoters *types.VoterSet,
newHeader *types.SignedHeader,
newVals *types.VoterSet,
newVoters *types.VoterSet,
now time.Time) error {

var (
trustedHeader = initiallyTrustedHeader
trustedVals = initiallyTrustedVals
trustedVoters = initiallyTrustedVoters

interimHeader = newHeader
interimVals = newVals
interimVoters = newVoters
)

for {
Expand All @@ -730,7 +730,7 @@ func (c *Client) bisection(
"newHeight", interimHeader.Height,
"newHash", hash2str(interimHeader.Hash()))

err := Verify(c.chainID, trustedHeader, trustedVals, interimHeader, interimVals, c.trustingPeriod, now,
err := Verify(c.chainID, trustedHeader, trustedVoters, interimHeader, interimVoters, c.trustingPeriod, now,
c.trustLevel)
switch err.(type) {
case nil:
Expand All @@ -739,13 +739,13 @@ func (c *Client) bisection(
}

// Update the lower bound to the previous upper bound
trustedHeader, trustedVals = interimHeader, interimVals
trustedHeader, trustedVoters = interimHeader, interimVoters
// Update the upper bound to the untrustedHeader
interimHeader, interimVals = newHeader, newVals
interimHeader, interimVoters = newHeader, newVoters

case ErrNewValSetCantBeTrusted:
pivotHeight := (interimHeader.Height + trustedHeader.Height) / 2
interimHeader, interimVals, err = c.fetchHeaderAndValsAtHeight(pivotHeight)
interimHeader, interimVoters, err = c.fetchHeaderAndVotersAtHeight(pivotHeight)
if err != nil {
return err
}
Expand All @@ -769,7 +769,7 @@ func (c *Client) bisection(
}
}

func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.VoterSet) error {
func (c *Client) updateTrustedHeaderAndVoters(h *types.SignedHeader, vals *types.VoterSet) error {
if !bytes.Equal(h.VotersHash, vals.Hash()) {
return errors.Errorf("expected validator's hash %X, but got %X", h.VotersHash, vals.Hash())
}
Expand All @@ -794,12 +794,12 @@ func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.V

// fetch header and validators for the given height (0 - latest) from primary
// provider.
func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader, *types.VoterSet, error) {
func (c *Client) fetchHeaderAndVotersAtHeight(height int64) (*types.SignedHeader, *types.VoterSet, error) {
h, err := c.signedHeaderFromPrimary(height)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to obtain the header #%d", height)
}
vals, err := c.validatorSetFromPrimary(height)
vals, err := c.voterSetFromPrimary(height)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to obtain the vals #%d", height)
}
Expand Down Expand Up @@ -938,13 +938,13 @@ func (c *Client) Update(now time.Time) (*types.SignedHeader, error) {
return nil, nil
}

latestHeader, latestVals, err := c.fetchHeaderAndValsAtHeight(0)
latestHeader, latestVoters, err := c.fetchHeaderAndVotersAtHeight(0)
if err != nil {
return nil, errors.Wrapf(err, "can't get latest header and vals")
}

if latestHeader.Height > lastTrustedHeight {
err = c.VerifyHeader(latestHeader, latestVals, now)
err = c.VerifyHeader(latestHeader, latestVoters, now)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1005,7 +1005,7 @@ func (c *Client) signedHeaderFromPrimary(height int64) (*types.SignedHeader, err
// validatorSetFromPrimary retrieves the VoterSet from the primary provider
// at the specified height. Handles dropout by the primary provider after 5
// attempts by replacing it with an alternative provider.
func (c *Client) validatorSetFromPrimary(height int64) (*types.VoterSet, error) {
func (c *Client) voterSetFromPrimary(height int64) (*types.VoterSet, error) {
for attempt := uint16(1); attempt <= c.maxRetryAttempts; attempt++ {
c.providerMutex.Lock()
vals, err := c.primary.VoterSet(height)
Expand All @@ -1023,7 +1023,7 @@ func (c *Client) validatorSetFromPrimary(height int64) (*types.VoterSet, error)
return nil, err
}

return c.validatorSetFromPrimary(height)
return c.voterSetFromPrimary(height)
}

// exponential backoff (with jitter)
Expand Down
Loading