Browse Source

lite2: indicate success/failure of Update (#4536)

error itself is not enough since it only signals if there were any
errors. Either (types.SignedHeader) or (success bool) is needed to
indicate the status of the operation. Returning a header is optimal
since most of the clients will want to get a newly verified header
anyway.
pull/4481/merge
Anton Kaliaev 5 years ago
committed by GitHub
parent
commit
d3f965ba68
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 25 additions and 32 deletions
  1. +4
    -5
      docs/architecture/adr-042-state-sync.md
  2. +5
    -5
      docs/architecture/adr-046-light-client-implementation.md
  3. +9
    -8
      lite2/client.go
  4. +5
    -7
      lite2/client_test.go
  5. +2
    -7
      lite2/example_test.go

+ 4
- 5
docs/architecture/adr-042-state-sync.md View File

@ -34,7 +34,7 @@ across different criteria:
### Implementation Question ### Implementation Question
* What is the format of a snapshot * What is the format of a snapshot
* Complete snapshot
* Complete snapshot
* Ordered IAVL key ranges * Ordered IAVL key ranges
* Compressed individually chunks which can be validated * Compressed individually chunks which can be validated
* How is data validated * How is data validated
@ -58,7 +58,7 @@ request time. This solution would create an auxiliary data structure
optimized for batch read/writes. optimized for batch read/writes.
Additionally the propsosals tend to vary on how they provide safety Additionally the propsosals tend to vary on how they provide safety
properties.
properties.
**LightClient** Where a client can aquire the merkle root from the block **LightClient** Where a client can aquire the merkle root from the block
headers synchronized from a trusted validator set. Subsets of the application state, headers synchronized from a trusted validator set. Subsets of the application state,
@ -70,7 +70,7 @@ downloaded and compared against versions provided by a majority of
peers. peers.
#### Lazy StateSync #### Lazy StateSync
An [initial specification](https://docs.google.com/document/d/15MFsQtNA0MGBv7F096FFWRDzQ1vR6_dics5Y49vF8JU/edit?ts=5a0f3629) was published by Alexis Sellier.
An initial specification was published by Alexis Sellier.
In this design, the state has a given `size` of primitive elements (like In this design, the state has a given `size` of primitive elements (like
keys or nodes), each element is assigned a number from 0 to `size-1`, keys or nodes), each element is assigned a number from 0 to `size-1`,
and chunks consists of a range of such elements. Ackratos raised and chunks consists of a range of such elements. Ackratos raised
@ -104,7 +104,7 @@ chunks and snappy compressed. Hashes of snappy compressed chunks are stored in a
manifest file which co-ordinates the state-sync. Obtaining a correct manifest manifest file which co-ordinates the state-sync. Obtaining a correct manifest
file seems to require an honest majority of peers. This means you may not find file seems to require an honest majority of peers. This means you may not find
out the state is incorrect until you download the whole thing and compare it out the state is incorrect until you download the whole thing and compare it
with a verified block header.
with a verified block header.
A similar solution was implemented by Binance in A similar solution was implemented by Binance in
[#3594](https://github.com/tendermint/tendermint/pull/3594) [#3594](https://github.com/tendermint/tendermint/pull/3594)
@ -229,7 +229,6 @@ Proposed
## References ## References
[sync: Sync current state without full replay for Applications](https://github.com/tendermint/tendermint/issues/828) - original issue [sync: Sync current state without full replay for Applications](https://github.com/tendermint/tendermint/issues/828) - original issue
[tendermint state sync proposal](https://docs.google.com/document/d/15MFsQtNA0MGBv7F096FFWRDzQ1vR6_dics5Y49vF8JU/edit?ts=5a0f3629) - Cloudhead proposal
[tendermint state sync proposal 2](https://docs.google.com/document/d/1npGTAa1qxe8EQZ1wG0a0Sip9t5oX2vYZNUDwr_LVRR4/edit) - ackratos proposal [tendermint state sync proposal 2](https://docs.google.com/document/d/1npGTAa1qxe8EQZ1wG0a0Sip9t5oX2vYZNUDwr_LVRR4/edit) - ackratos proposal
[proposal 2 implementation](https://github.com/tendermint/tendermint/pull/3243) - ackratos implementation [proposal 2 implementation](https://github.com/tendermint/tendermint/pull/3243) - ackratos implementation
[WIP General/Lazy State-Sync pseudo-spec](https://github.com/tendermint/tendermint/issues/3639) - Jae Proposal [WIP General/Lazy State-Sync pseudo-spec](https://github.com/tendermint/tendermint/issues/3639) - Jae Proposal


+ 5
- 5
docs/architecture/adr-046-light-client-implementation.md View File

@ -15,7 +15,10 @@ latest header from primary and compares it with the currently trusted one.
```go ```go
type Client interface { type Client interface {
Cleanup() error
// verify new headers
VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error)
VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error
Update(now time.Time) (*types.SignedHeader, error)
// get trusted headers & validators // get trusted headers & validators
TrustedHeader(height int64) (*types.SignedHeader, error) TrustedHeader(height int64) (*types.SignedHeader, error)
@ -28,10 +31,7 @@ type Client interface {
Primary() provider.Provider Primary() provider.Provider
Witnesses() []provider.Provider Witnesses() []provider.Provider
// verify new headers
VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error)
VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error
Update(now time.Time) error
Cleanup() error
} }
``` ```


+ 9
- 8
lite2/client.go View File

@ -895,33 +895,34 @@ func (c *Client) removeWitness(idx int) {
} }
// Update attempts to advance the state by downloading the latest header and // Update attempts to advance the state by downloading the latest header and
// comparing it with the existing one.
func (c *Client) Update(now time.Time) error {
// comparing it with the existing one. It returns a new header on a successful
// update. Otherwise, it returns nil (plus an error, if any).
func (c *Client) Update(now time.Time) (*types.SignedHeader, error) {
lastTrustedHeight, err := c.LastTrustedHeight() lastTrustedHeight, err := c.LastTrustedHeight()
if err != nil { if err != nil {
return errors.Wrap(err, "can't get last trusted height")
return nil, errors.Wrap(err, "can't get last trusted height")
} }
if lastTrustedHeight == -1 { if lastTrustedHeight == -1 {
// no headers yet => wait // no headers yet => wait
return nil
return nil, nil
} }
latestHeader, latestVals, err := c.fetchHeaderAndValsAtHeight(0) latestHeader, latestVals, err := c.fetchHeaderAndValsAtHeight(0)
if err != nil { if err != nil {
return errors.Wrapf(err, "can't get latest header and vals")
return nil, errors.Wrapf(err, "can't get latest header and vals")
} }
if latestHeader.Height > lastTrustedHeight { if latestHeader.Height > lastTrustedHeight {
err = c.VerifyHeader(latestHeader, latestVals, now) err = c.VerifyHeader(latestHeader, latestVals, now)
if err != nil { if err != nil {
return err
return nil, err
} }
c.logger.Info("Advanced to new state", "height", latestHeader.Height, "hash", hash2str(latestHeader.Hash())) c.logger.Info("Advanced to new state", "height", latestHeader.Height, "hash", hash2str(latestHeader.Hash()))
return latestHeader, nil
} }
return nil
return nil, nil
} }
// replaceProvider takes the first alternative provider and promotes it as the // replaceProvider takes the first alternative provider and promotes it as the


+ 5
- 7
lite2/client_test.go View File

@ -604,13 +604,11 @@ func TestClient_Update(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// should result in downloading & verifying header #3 // should result in downloading & verifying header #3
err = c.Update(bTime.Add(2 * time.Hour))
require.NoError(t, err)
h, err := c.TrustedHeader(3)
h, err := c.Update(bTime.Add(2 * time.Hour))
assert.NoError(t, err) assert.NoError(t, err)
require.NotNil(t, h)
assert.EqualValues(t, 3, h.Height)
if assert.NotNil(t, h) {
assert.EqualValues(t, 3, h.Height)
}
valSet, _, err := c.TrustedValidatorSet(3) valSet, _, err := c.TrustedValidatorSet(3)
assert.NoError(t, err) assert.NoError(t, err)
@ -675,7 +673,7 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
) )
require.NoError(t, err) require.NoError(t, err)
err = c.Update(bTime.Add(2 * time.Hour))
_, err = c.Update(bTime.Add(2 * time.Hour))
require.NoError(t, err) require.NoError(t, err)
assert.NotEqual(t, c.Primary(), deadNode) assert.NotEqual(t, c.Primary(), deadNode)


+ 2
- 7
lite2/example_test.go View File

@ -73,17 +73,12 @@ func ExampleClient_Update() {
// monotonic component (see types/time/time.go) b) single instance is being // monotonic component (see types/time/time.go) b) single instance is being
// run. // run.
// https://github.com/tendermint/tendermint/issues/4489 // https://github.com/tendermint/tendermint/issues/4489
err = c.Update(time.Now().Add(30 * time.Minute))
h, err := c.Update(time.Now().Add(30 * time.Minute))
if err != nil { if err != nil {
stdlog.Fatal(err) stdlog.Fatal(err)
} }
h, err := c.TrustedHeader(0)
if err != nil {
stdlog.Fatal(err)
}
if h.Height > 2 {
if h != nil && h.Height > 2 {
fmt.Println("successful update") fmt.Println("successful update")
} else { } else {
fmt.Println("update failed") fmt.Println("update failed")


Loading…
Cancel
Save