Browse Source

rpc/client: take context as first param (#5347)

Closes #5145

also applies to light/client
pull/5386/head
Anton Kaliaev 4 years ago
committed by GitHub
parent
commit
85a4be87a7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 706 additions and 503 deletions
  1. +2
    -0
      CHANGELOG_PENDING.md
  2. +4
    -3
      cmd/tendermint/commands/debug/util.go
  3. +3
    -1
      cmd/tendermint/commands/lite.go
  4. +37
    -31
      light/client.go
  5. +9
    -5
      light/client_benchmark_test.go
  6. +47
    -25
      light/client_test.go
  7. +26
    -14
      light/detector.go
  8. +9
    -5
      light/detector_test.go
  9. +7
    -4
      light/example_test.go
  10. +10
    -9
      light/provider/http/http.go
  11. +5
    -4
      light/provider/http/http_test.go
  12. +3
    -2
      light/provider/mock/deadmock.go
  13. +3
    -2
      light/provider/mock/mock.go
  14. +4
    -2
      light/provider/provider.go
  15. +23
    -23
      light/proxy/routes.go
  16. +62
    -62
      light/rpc/client.go
  17. +3
    -0
      light/setup.go
  18. +3
    -0
      node/node.go
  19. +3
    -2
      rpc/client/event_test.go
  20. +6
    -5
      rpc/client/evidence_test.go
  21. +7
    -6
      rpc/client/examples_test.go
  22. +1
    -1
      rpc/client/helpers.go
  23. +99
    -53
      rpc/client/http/http.go
  24. +26
    -25
      rpc/client/interface.go
  25. +41
    -28
      rpc/client/local/local.go
  26. +32
    -23
      rpc/client/mock/abci.go
  27. +25
    -14
      rpc/client/mock/abci_test.go
  28. +30
    -22
      rpc/client/mock/client.go
  29. +5
    -3
      rpc/client/mock/status.go
  30. +2
    -1
      rpc/client/mock/status_test.go
  31. +60
    -54
      rpc/client/rpc_test.go
  32. +16
    -12
      rpc/jsonrpc/client/http_json_client.go
  33. +19
    -4
      rpc/jsonrpc/client/http_uri_client.go
  34. +8
    -4
      rpc/jsonrpc/jsonrpc_test.go
  35. +1
    -1
      rpc/test/helpers.go
  36. +23
    -21
      statesync/mocks/state_provider.go
  37. +6
    -1
      statesync/snapshots.go
  38. +8
    -8
      statesync/snapshots_test.go
  39. +15
    -13
      statesync/stateprovider.go
  40. +5
    -2
      statesync/syncer.go
  41. +8
    -8
      statesync/syncer_test.go

+ 2
- 0
CHANGELOG_PENDING.md View File

@ -9,6 +9,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
- CLI/RPC/Config
- [config] \#5315 Rename `prof_laddr` to `pprof_laddr` and move it to `rpc` section (@melekes)
- [rpc] \#5315 Remove `/unsafe_start_cpu_profiler`, `/unsafe_stop_cpu_profiler` and `/unsafe_write_heap_profile`. Please use pprof functionality instead (@melekes)
- [rpc/client, rpc/jsonrpc/client] \#5347 All client methods now accept `context.Context` as 1st param (@melekes)
- Apps
@ -23,6 +24,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
- [evidence] [\#5361](https://github.com/tendermint/tendermint/pull/5361) Add LightClientAttackEvidence and change evidence interface (@cmwaters)
- [params] \#5319 Remove `ProofofTrialPeriod` from evidence params (@marbar3778)
- [crypto/secp256k1] \#5280 `secp256k1` has been removed from the Tendermint repo. (@marbar3778)
- [light] \#5347 `NewClient`, `NewHTTPClient`, `VerifyHeader` and `VerifyLightBlockAtHeight` now accept `context.Context` as 1st param (@melekes)
- [state] \#5348 Define an Interface for the state store. (@marbar3778)
- Blockchain Protocol


+ 4
- 3
cmd/tendermint/commands/debug/util.go View File

@ -1,6 +1,7 @@
package debug
import (
"context"
"fmt"
"io/ioutil"
"net/http"
@ -15,7 +16,7 @@ import (
// dumpStatus gets node status state dump from the Tendermint RPC and writes it
// to file. It returns an error upon failure.
func dumpStatus(rpc *rpchttp.HTTP, dir, filename string) error {
status, err := rpc.Status()
status, err := rpc.Status(context.Background())
if err != nil {
return fmt.Errorf("failed to get node status: %w", err)
}
@ -26,7 +27,7 @@ func dumpStatus(rpc *rpchttp.HTTP, dir, filename string) error {
// dumpNetInfo gets network information state dump from the Tendermint RPC and
// writes it to file. It returns an error upon failure.
func dumpNetInfo(rpc *rpchttp.HTTP, dir, filename string) error {
netInfo, err := rpc.NetInfo()
netInfo, err := rpc.NetInfo(context.Background())
if err != nil {
return fmt.Errorf("failed to get node network information: %w", err)
}
@ -37,7 +38,7 @@ func dumpNetInfo(rpc *rpchttp.HTTP, dir, filename string) error {
// dumpConsensusState gets consensus state dump from the Tendermint RPC and
// writes it to file. It returns an error upon failure.
func dumpConsensusState(rpc *rpchttp.HTTP, dir, filename string) error {
consDump, err := rpc.DumpConsensusState()
consDump, err := rpc.DumpConsensusState(context.Background())
if err != nil {
return fmt.Errorf("failed to get node consensus dump: %w", err)
}


+ 3
- 1
cmd/tendermint/commands/lite.go View File

@ -1,6 +1,7 @@
package commands
import (
"context"
"errors"
"fmt"
"net/http"
@ -36,7 +37,7 @@ that, it will present the same interface as a full Tendermint node.
Furthermore to the chainID, a fresh instance of a light client will
need a primary RPC address, a trusted hash and height and witness RPC addresses
(if not using sequential verification). To restart the node, thereafter
only the chainID is required.
only the chainID is required.
`,
RunE: runProxy,
@ -148,6 +149,7 @@ func runProxy(cmd *cobra.Command, args []string) error {
var c *light.Client
if trustedHeight > 0 && len(trustedHash) > 0 { // fresh installation
c, err = light.NewHTTPClient(
context.Background(),
chainID,
light.TrustOptions{
Period: trustingPeriod,


+ 37
- 31
light/client.go View File

@ -2,6 +2,7 @@ package light
import (
"bytes"
"context"
"errors"
"fmt"
"time"
@ -151,6 +152,7 @@ type Client struct {
//
// See all Option(s) for the additional configuration.
func NewClient(
ctx context.Context,
chainID string,
trustOptions TrustOptions,
primary provider.Provider,
@ -169,14 +171,14 @@ func NewClient(
if c.latestTrustedBlock != nil {
c.logger.Info("Checking trusted light block using options")
if err := c.checkTrustedHeaderUsingOptions(trustOptions); err != nil {
if err := c.checkTrustedHeaderUsingOptions(ctx, trustOptions); err != nil {
return nil, err
}
}
if c.latestTrustedBlock == nil || c.latestTrustedBlock.Height < trustOptions.Height {
c.logger.Info("Downloading trusted light block using options")
if err := c.initializeWithTrustOptions(trustOptions); err != nil {
if err := c.initializeWithTrustOptions(ctx, trustOptions); err != nil {
return nil, err
}
}
@ -277,11 +279,11 @@ func (c *Client) restoreTrustedLightBlock() error {
//
// The intuition here is the user is always right. I.e. if she decides to reset
// the light client with an older header, there must be a reason for it.
func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error {
func (c *Client) checkTrustedHeaderUsingOptions(ctx context.Context, options TrustOptions) error {
var primaryHash []byte
switch {
case options.Height > c.latestTrustedBlock.Height:
h, err := c.lightBlockFromPrimary(c.latestTrustedBlock.Height)
h, err := c.lightBlockFromPrimary(ctx, c.latestTrustedBlock.Height)
if err != nil {
return err
}
@ -336,9 +338,9 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error {
// initializeWithTrustOptions fetches the weakly-trusted light block from
// primary provider.
func (c *Client) initializeWithTrustOptions(options TrustOptions) error {
func (c *Client) initializeWithTrustOptions(ctx context.Context, options TrustOptions) error {
// 1) Fetch and verify the light block.
l, err := c.lightBlockFromPrimary(options.Height)
l, err := c.lightBlockFromPrimary(ctx, options.Height)
if err != nil {
return err
}
@ -405,7 +407,7 @@ func (c *Client) compareWithLatestHeight(height int64) (int64, error) {
// Update attempts to advance the state by downloading the latest light
// block and verifying it. It returns a new light block on a successful
// update. Otherwise, it returns nil (plus an error, if any).
func (c *Client) Update(now time.Time) (*types.LightBlock, error) {
func (c *Client) Update(ctx context.Context, now time.Time) (*types.LightBlock, error) {
lastTrustedHeight, err := c.LastTrustedHeight()
if err != nil {
return nil, fmt.Errorf("can't get last trusted height: %w", err)
@ -416,13 +418,13 @@ func (c *Client) Update(now time.Time) (*types.LightBlock, error) {
return nil, nil
}
latestBlock, err := c.lightBlockFromPrimary(0)
latestBlock, err := c.lightBlockFromPrimary(ctx, 0)
if err != nil {
return nil, err
}
if latestBlock.Height > lastTrustedHeight {
err = c.verifyLightBlock(latestBlock, now)
err = c.verifyLightBlock(ctx, latestBlock, now)
if err != nil {
return nil, err
}
@ -443,7 +445,7 @@ func (c *Client) Update(now time.Time) (*types.LightBlock, error) {
// primary.
//
// It will replace the primary provider if an error from a request to the provider occurs
func (c *Client) VerifyLightBlockAtHeight(height int64, now time.Time) (*types.LightBlock, error) {
func (c *Client) VerifyLightBlockAtHeight(ctx context.Context, height int64, now time.Time) (*types.LightBlock, error) {
if height <= 0 {
return nil, errors.New("negative or zero height")
}
@ -457,12 +459,12 @@ func (c *Client) VerifyLightBlockAtHeight(height int64, now time.Time) (*types.L
}
// Request the light block from primary
l, err := c.lightBlockFromPrimary(height)
l, err := c.lightBlockFromPrimary(ctx, height)
if err != nil {
return nil, err
}
return l, c.verifyLightBlock(l, now)
return l, c.verifyLightBlock(ctx, l, now)
}
// VerifyHeader verifies a new header against the trusted state. It returns
@ -493,7 +495,7 @@ func (c *Client) VerifyLightBlockAtHeight(height int64, now time.Time) (*types.L
// If, at any moment, a LightBlock is not found by the primary provider as part of
// verification then the provider will be replaced by another and the process will
// restart.
func (c *Client) VerifyHeader(newHeader *types.Header, now time.Time) error {
func (c *Client) VerifyHeader(ctx context.Context, newHeader *types.Header, now time.Time) error {
if newHeader == nil {
return errors.New("nil header")
}
@ -514,7 +516,7 @@ func (c *Client) VerifyHeader(newHeader *types.Header, now time.Time) error {
}
// Request the header and the vals.
l, err = c.lightBlockFromPrimary(newHeader.Height)
l, err = c.lightBlockFromPrimary(ctx, newHeader.Height)
if err != nil {
return fmt.Errorf("failed to retrieve light block from primary to verify against: %w", err)
}
@ -523,14 +525,14 @@ func (c *Client) VerifyHeader(newHeader *types.Header, now time.Time) error {
return fmt.Errorf("light block header %X does not match newHeader %X", l.Hash(), newHeader.Hash())
}
return c.verifyLightBlock(l, now)
return c.verifyLightBlock(ctx, l, now)
}
func (c *Client) verifyLightBlock(newLightBlock *types.LightBlock, now time.Time) error {
func (c *Client) verifyLightBlock(ctx context.Context, newLightBlock *types.LightBlock, now time.Time) error {
c.logger.Info("VerifyHeader", "height", newLightBlock.Height, "hash", hash2str(newLightBlock.Hash()))
var (
verifyFunc func(trusted *types.LightBlock, new *types.LightBlock, now time.Time) error
verifyFunc func(ctx context.Context, trusted *types.LightBlock, new *types.LightBlock, now time.Time) error
err error
)
@ -551,7 +553,7 @@ func (c *Client) verifyLightBlock(newLightBlock *types.LightBlock, now time.Time
switch {
// Verifying forwards
case newLightBlock.Height >= c.latestTrustedBlock.Height:
err = verifyFunc(c.latestTrustedBlock, newLightBlock, now)
err = verifyFunc(ctx, c.latestTrustedBlock, newLightBlock, now)
// Verifying backwards
case newLightBlock.Height < firstBlockHeight:
@ -560,7 +562,7 @@ func (c *Client) verifyLightBlock(newLightBlock *types.LightBlock, now time.Time
if err != nil {
return fmt.Errorf("can't get first light block: %w", err)
}
err = c.backwards(firstBlock.Header, newLightBlock.Header)
err = c.backwards(ctx, firstBlock.Header, newLightBlock.Header)
// Verifying between first and last trusted light block
default:
@ -569,7 +571,7 @@ func (c *Client) verifyLightBlock(newLightBlock *types.LightBlock, now time.Time
if err != nil {
return fmt.Errorf("can't get signed header before height %d: %w", newLightBlock.Height, err)
}
err = verifyFunc(closestBlock, newLightBlock, now)
err = verifyFunc(ctx, closestBlock, newLightBlock, now)
}
if err != nil {
c.logger.Error("Can't verify", "err", err)
@ -582,6 +584,7 @@ func (c *Client) verifyLightBlock(newLightBlock *types.LightBlock, now time.Time
// see VerifyHeader
func (c *Client) verifySequential(
ctx context.Context,
trustedBlock *types.LightBlock,
newLightBlock *types.LightBlock,
now time.Time) error {
@ -597,7 +600,7 @@ func (c *Client) verifySequential(
if height == newLightBlock.Height { // last light block
interimBlock = newLightBlock
} else { // intermediate light blocks
interimBlock, err = c.lightBlockFromPrimary(height)
interimBlock, err = c.lightBlockFromPrimary(ctx, height)
if err != nil {
return ErrVerificationFailed{From: verifiedBlock.Height, To: height, Reason: err}
}
@ -633,7 +636,7 @@ func (c *Client) verifySequential(
return err
}
replacementBlock, fErr := c.lightBlockFromPrimary(newLightBlock.Height)
replacementBlock, fErr := c.lightBlockFromPrimary(ctx, newLightBlock.Height)
if fErr != nil {
c.logger.Error("Can't fetch light block from primary", "err", fErr)
// return original error
@ -672,6 +675,7 @@ func (c *Client) verifySequential(
// light client tries again to verify the new light block in the middle, the light
// client does not need to ask for all the same light blocks again.
func (c *Client) verifySkipping(
ctx context.Context,
source provider.Provider,
trustedBlock *types.LightBlock,
newLightBlock *types.LightBlock,
@ -715,7 +719,7 @@ func (c *Client) verifySkipping(
if depth == len(blockCache)-1 {
pivotHeight := verifiedBlock.Height + (blockCache[depth].Height-verifiedBlock.
Height)*verifySkippingNumerator/verifySkippingDenominator
interimBlock, providerErr := source.LightBlock(pivotHeight)
interimBlock, providerErr := source.LightBlock(ctx, pivotHeight)
if providerErr != nil {
return nil, ErrVerificationFailed{From: verifiedBlock.Height, To: pivotHeight, Reason: providerErr}
}
@ -732,11 +736,12 @@ func (c *Client) verifySkipping(
// verifySkippingAgainstPrimary does verifySkipping plus it compares new header with
// witnesses and replaces primary if it sends the light client an invalid header
func (c *Client) verifySkippingAgainstPrimary(
ctx context.Context,
trustedBlock *types.LightBlock,
newLightBlock *types.LightBlock,
now time.Time) error {
trace, err := c.verifySkipping(c.primary, trustedBlock, newLightBlock, now)
trace, err := c.verifySkipping(ctx, c.primary, trustedBlock, newLightBlock, now)
switch errors.Unwrap(err).(type) {
case ErrInvalidHeader:
@ -757,7 +762,7 @@ func (c *Client) verifySkippingAgainstPrimary(
return err
}
replacementBlock, fErr := c.lightBlockFromPrimary(newLightBlock.Height)
replacementBlock, fErr := c.lightBlockFromPrimary(ctx, newLightBlock.Height)
if fErr != nil {
c.logger.Error("Can't fetch light block from primary", "err", fErr)
// return original error
@ -773,14 +778,14 @@ func (c *Client) verifySkippingAgainstPrimary(
}
// attempt to verify the header again
return c.verifySkippingAgainstPrimary(trustedBlock, replacementBlock, now)
return c.verifySkippingAgainstPrimary(ctx, trustedBlock, replacementBlock, now)
case nil:
// Compare header with the witnesses to ensure it's not a fork.
// More witnesses we have, more chance to notice one.
//
// CORRECTNESS ASSUMPTION: there's at least 1 correct full node
// (primary or one of the witnesses).
if cmpErr := c.detectDivergence(trace, now); cmpErr != nil {
if cmpErr := c.detectDivergence(ctx, trace, now); cmpErr != nil {
return cmpErr
}
default:
@ -892,6 +897,7 @@ func (c *Client) updateTrustedLightBlock(l *types.LightBlock) error {
// headers before a trusted header. If a sent header is invalid the primary is
// replaced with another provider and the operation is repeated.
func (c *Client) backwards(
ctx context.Context,
trustedHeader *types.Header,
newHeader *types.Header) error {
@ -901,7 +907,7 @@ func (c *Client) backwards(
)
for verifiedHeader.Height > newHeader.Height {
interimBlock, err := c.lightBlockFromPrimary(verifiedHeader.Height - 1)
interimBlock, err := c.lightBlockFromPrimary(ctx, verifiedHeader.Height-1)
if err != nil {
return fmt.Errorf("failed to obtain the header at height #%d: %w", verifiedHeader.Height-1, err)
}
@ -960,9 +966,9 @@ func (c *Client) replacePrimaryProvider() error {
// lightBlockFromPrimary retrieves the lightBlock from the primary provider
// at the specified height. Handles dropout by the primary provider by swapping
// with an alternative provider.
func (c *Client) lightBlockFromPrimary(height int64) (*types.LightBlock, error) {
func (c *Client) lightBlockFromPrimary(ctx context.Context, height int64) (*types.LightBlock, error) {
c.providerMutex.Lock()
l, err := c.primary.LightBlock(height)
l, err := c.primary.LightBlock(ctx, height)
c.providerMutex.Unlock()
if err != nil {
c.logger.Debug("Error on light block request from primary", "error", err)
@ -971,7 +977,7 @@ func (c *Client) lightBlockFromPrimary(height int64) (*types.LightBlock, error)
return nil, fmt.Errorf("%v. Tried to replace primary but: %w", err.Error(), replaceErr)
}
// replace primary and request a light block again
return c.lightBlockFromPrimary(height)
return c.lightBlockFromPrimary(ctx, height)
}
return l, err
}


+ 9
- 5
light/client_benchmark_test.go View File

@ -1,6 +1,7 @@
package light_test
import (
"context"
"testing"
"time"
@ -22,11 +23,12 @@ import (
// Remember that none of these benchmarks account for network latency.
var (
benchmarkFullNode = mockp.New(genMockNode(chainID, 1000, 100, 1, bTime))
genesisBlock, _ = benchmarkFullNode.LightBlock(1)
genesisBlock, _ = benchmarkFullNode.LightBlock(context.Background(), 1)
)
func BenchmarkSequence(b *testing.B) {
c, err := light.NewClient(
context.Background(),
chainID,
light.TrustOptions{
Period: 24 * time.Hour,
@ -45,7 +47,7 @@ func BenchmarkSequence(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err = c.VerifyLightBlockAtHeight(1000, bTime.Add(1000*time.Minute))
_, err = c.VerifyLightBlockAtHeight(context.Background(), 1000, bTime.Add(1000*time.Minute))
if err != nil {
b.Fatal(err)
}
@ -54,6 +56,7 @@ func BenchmarkSequence(b *testing.B) {
func BenchmarkBisection(b *testing.B) {
c, err := light.NewClient(
context.Background(),
chainID,
light.TrustOptions{
Period: 24 * time.Hour,
@ -71,7 +74,7 @@ func BenchmarkBisection(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err = c.VerifyLightBlockAtHeight(1000, bTime.Add(1000*time.Minute))
_, err = c.VerifyLightBlockAtHeight(context.Background(), 1000, bTime.Add(1000*time.Minute))
if err != nil {
b.Fatal(err)
}
@ -79,8 +82,9 @@ func BenchmarkBisection(b *testing.B) {
}
func BenchmarkBackwards(b *testing.B) {
trustedBlock, _ := benchmarkFullNode.LightBlock(0)
trustedBlock, _ := benchmarkFullNode.LightBlock(context.Background(), 0)
c, err := light.NewClient(
context.Background(),
chainID,
light.TrustOptions{
Period: 24 * time.Hour,
@ -98,7 +102,7 @@ func BenchmarkBackwards(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err = c.VerifyLightBlockAtHeight(1, bTime)
_, err = c.VerifyLightBlockAtHeight(context.Background(), 1, bTime)
if err != nil {
b.Fatal(err)
}


+ 47
- 25
light/client_test.go View File

@ -1,6 +1,7 @@
package light_test
import (
"context"
"sync"
"testing"
"time"
@ -23,6 +24,7 @@ const (
)
var (
ctx = context.Background()
keys = genPrivKeys(4)
vals = keys.ToValidators(20, 10)
bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
@ -111,7 +113,7 @@ func TestValidateTrustOptions(t *testing.T) {
}
func TestMock(t *testing.T) {
l, _ := fullNode.LightBlock(3)
l, _ := fullNode.LightBlock(ctx, 3)
assert.Equal(t, int64(3), l.Height)
}
@ -216,6 +218,7 @@ func TestClient_SequentialVerification(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
mockp.New(
@ -240,7 +243,7 @@ func TestClient_SequentialVerification(t *testing.T) {
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(3, bTime.Add(3*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(3*time.Hour))
if tc.verifyErr {
assert.Error(t, err)
} else {
@ -340,6 +343,7 @@ func TestClient_SkippingVerification(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
mockp.New(
@ -363,7 +367,7 @@ func TestClient_SkippingVerification(t *testing.T) {
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(3, bTime.Add(3*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(3*time.Hour))
if tc.verifyErr {
assert.Error(t, err)
} else {
@ -378,9 +382,10 @@ func TestClient_SkippingVerification(t *testing.T) {
// the appropriate range
func TestClientLargeBisectionVerification(t *testing.T) {
veryLargeFullNode := mockp.New(genMockNode(chainID, 100, 3, 0, bTime))
trustedLightBlock, err := veryLargeFullNode.LightBlock(5)
trustedLightBlock, err := veryLargeFullNode.LightBlock(ctx, 5)
require.NoError(t, err)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -393,15 +398,16 @@ func TestClientLargeBisectionVerification(t *testing.T) {
light.SkippingVerification(light.DefaultTrustLevel),
)
require.NoError(t, err)
h, err := c.Update(bTime.Add(100 * time.Minute))
h, err := c.Update(ctx, bTime.Add(100*time.Minute))
assert.NoError(t, err)
h2, err := veryLargeFullNode.LightBlock(100)
h2, err := veryLargeFullNode.LightBlock(ctx, 100)
require.NoError(t, err)
assert.Equal(t, h, h2)
}
func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -415,7 +421,7 @@ func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(3, bTime.Add(2*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(2*time.Hour))
require.NoError(t, err)
// confirm that the client already doesn't have the light block
@ -423,12 +429,13 @@ func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
require.Error(t, err)
// verify using bisection the light block between the two trusted light blocks
_, err = c.VerifyLightBlockAtHeight(2, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour))
assert.NoError(t, err)
}
func TestClient_Cleanup(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -458,6 +465,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
require.NoError(t, err)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -494,6 +502,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -525,6 +534,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
require.NoError(t, err)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -570,6 +580,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -603,6 +614,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
require.NoError(t, err)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -657,6 +669,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -686,6 +699,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
func TestClient_Update(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -696,7 +710,7 @@ func TestClient_Update(t *testing.T) {
require.NoError(t, err)
// should result in downloading & verifying header #3
l, err := c.Update(bTime.Add(2 * time.Hour))
l, err := c.Update(ctx, bTime.Add(2*time.Hour))
assert.NoError(t, err)
if assert.NotNil(t, l) {
assert.EqualValues(t, 3, l.Height)
@ -706,6 +720,7 @@ func TestClient_Update(t *testing.T) {
func TestClient_Concurrency(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -715,7 +730,7 @@ func TestClient_Concurrency(t *testing.T) {
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(2, bTime.Add(2*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(2*time.Hour))
require.NoError(t, err)
var wg sync.WaitGroup
@ -746,6 +761,7 @@ func TestClient_Concurrency(t *testing.T) {
func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
deadNode,
@ -756,7 +772,7 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
)
require.NoError(t, err)
_, err = c.Update(bTime.Add(2 * time.Hour))
_, err = c.Update(ctx, bTime.Add(2*time.Hour))
require.NoError(t, err)
assert.NotEqual(t, c.Primary(), deadNode)
@ -765,8 +781,9 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
func TestClient_BackwardsVerification(t *testing.T) {
{
trustHeader, _ := largeFullNode.LightBlock(6)
trustHeader, _ := largeFullNode.LightBlock(ctx, 6)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Minute,
@ -781,28 +798,28 @@ func TestClient_BackwardsVerification(t *testing.T) {
require.NoError(t, err)
// 1) verify before the trusted header using backwards => expect no error
h, err := c.VerifyLightBlockAtHeight(5, bTime.Add(6*time.Minute))
h, err := c.VerifyLightBlockAtHeight(ctx, 5, bTime.Add(6*time.Minute))
require.NoError(t, err)
if assert.NotNil(t, h) {
assert.EqualValues(t, 5, h.Height)
}
// 2) untrusted header is expired but trusted header is not => expect no error
h, err = c.VerifyLightBlockAtHeight(3, bTime.Add(8*time.Minute))
h, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(8*time.Minute))
assert.NoError(t, err)
assert.NotNil(t, h)
// 3) already stored headers should return the header without error
h, err = c.VerifyLightBlockAtHeight(5, bTime.Add(6*time.Minute))
h, err = c.VerifyLightBlockAtHeight(ctx, 5, bTime.Add(6*time.Minute))
assert.NoError(t, err)
assert.NotNil(t, h)
// 4a) First verify latest header
_, err = c.VerifyLightBlockAtHeight(9, bTime.Add(9*time.Minute))
_, err = c.VerifyLightBlockAtHeight(ctx, 9, bTime.Add(9*time.Minute))
require.NoError(t, err)
// 4b) Verify backwards using bisection => expect no error
_, err = c.VerifyLightBlockAtHeight(7, bTime.Add(9*time.Minute))
_, err = c.VerifyLightBlockAtHeight(ctx, 7, bTime.Add(9*time.Minute))
assert.NoError(t, err)
// shouldn't have verified this header in the process
_, err = c.TrustedLightBlock(8)
@ -810,7 +827,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
// 5) Try bisection method, but closest header (at 7) has expired
// so expect error
_, err = c.VerifyLightBlockAtHeight(8, bTime.Add(12*time.Minute))
_, err = c.VerifyLightBlockAtHeight(ctx, 8, bTime.Add(12*time.Minute))
assert.Error(t, err)
}
@ -848,6 +865,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
for idx, tc := range testCases {
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 1 * time.Hour,
@ -861,7 +879,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
)
require.NoError(t, err, idx)
_, err = c.VerifyLightBlockAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second))
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour).Add(1*time.Second))
assert.Error(t, err, idx)
}
}
@ -917,10 +935,11 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
},
)
lb1, _ := badProvider1.LightBlock(2)
lb1, _ := badProvider1.LightBlock(ctx, 2)
require.NotEqual(t, lb1.Hash(), l1.Hash())
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -934,14 +953,14 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
assert.EqualValues(t, 2, len(c.Witnesses()))
// witness behaves incorrectly -> removed from list, no error
l, err := c.VerifyLightBlockAtHeight(2, bTime.Add(2*time.Hour))
l, err := c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(2*time.Hour))
assert.NoError(t, err)
assert.EqualValues(t, 1, len(c.Witnesses()))
// light block should still be verified
assert.EqualValues(t, 2, l.Height)
// remaining witnesses don't have light block -> error
_, err = c.VerifyLightBlockAtHeight(3, bTime.Add(2*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(2*time.Hour))
if assert.Error(t, err) {
assert.Equal(t, light.ErrFailedHeaderCrossReferencing, err)
}
@ -970,6 +989,7 @@ func TestClient_TrustedValidatorSet(t *testing.T) {
)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -980,13 +1000,14 @@ func TestClient_TrustedValidatorSet(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 2, len(c.Witnesses()))
_, err = c.VerifyLightBlockAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second))
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(2*time.Hour).Add(1*time.Second))
assert.NoError(t, err)
assert.Equal(t, 1, len(c.Witnesses()))
}
func TestClientPrunesHeadersAndValidatorSets(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
@ -999,7 +1020,7 @@ func TestClientPrunesHeadersAndValidatorSets(t *testing.T) {
_, err = c.TrustedLightBlock(1)
require.NoError(t, err)
h, err := c.Update(bTime.Add(2 * time.Hour))
h, err := c.Update(ctx, bTime.Add(2*time.Hour))
require.NoError(t, err)
require.Equal(t, int64(3), h.Height)
@ -1059,6 +1080,7 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
tc.vals,
)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
badNode,
@ -1068,7 +1090,7 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(3, bTime.Add(2*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(2*time.Hour))
if tc.err {
assert.Error(t, err)
} else {


+ 26
- 14
light/detector.go View File

@ -2,6 +2,7 @@ package light
import (
"bytes"
"context"
"errors"
"fmt"
"time"
@ -25,7 +26,7 @@ import (
//
// If there are no conflictinge headers, the light client deems the verified target header
// trusted and saves it to the trusted store.
func (c *Client) detectDivergence(primaryTrace []*types.LightBlock, now time.Time) error {
func (c *Client) detectDivergence(ctx context.Context, primaryTrace []*types.LightBlock, now time.Time) error {
if primaryTrace == nil || len(primaryTrace) < 2 {
return errors.New("nil or single block primary trace")
}
@ -48,7 +49,7 @@ func (c *Client) detectDivergence(primaryTrace []*types.LightBlock, now time.Tim
// and compare it with the header from the primary
errc := make(chan error, len(c.witnesses))
for i, witness := range c.witnesses {
go c.compareNewHeaderWithWitness(errc, lastVerifiedHeader, witness, i)
go c.compareNewHeaderWithWitness(ctx, errc, lastVerifiedHeader, witness, i)
}
// handle errors from the header comparisons as they come in
@ -66,8 +67,13 @@ func (c *Client) detectDivergence(primaryTrace []*types.LightBlock, now time.Tim
// We combine these actions together, verifying the witnesses headers and outputting the trace
// which captures the bifurcation point and if successful provides the information to create
supportingWitness := c.witnesses[e.WitnessIndex]
witnessTrace, primaryBlock, err := c.examineConflictingHeaderAgainstTrace(primaryTrace, e.Block.SignedHeader,
supportingWitness, now)
witnessTrace, primaryBlock, err := c.examineConflictingHeaderAgainstTrace(
ctx,
primaryTrace,
e.Block.SignedHeader,
supportingWitness,
now,
)
if err != nil {
c.logger.Info("Error validating witness's divergent header", "witness", supportingWitness, "err", err)
witnessesToRemove = append(witnessesToRemove, e.WitnessIndex)
@ -90,13 +96,18 @@ func (c *Client) detectDivergence(primaryTrace []*types.LightBlock, now time.Tim
}
c.logger.Error("Attack detected. Sending evidence againt primary by witness", "ev", ev,
"primary", c.primary, "witness", supportingWitness)
c.sendEvidence(ev, supportingWitness)
c.sendEvidence(ctx, ev, supportingWitness)
// This may not be valid because the witness itself is at fault. So now we reverse it, examining the
// trace provided by the witness and holding the primary as the source of truth. Note: primary may not
// respond but this is okay as we will halt anyway.
primaryTrace, witnessBlock, err := c.examineConflictingHeaderAgainstTrace(witnessTrace, primaryBlock.SignedHeader,
c.primary, now)
primaryTrace, witnessBlock, err := c.examineConflictingHeaderAgainstTrace(
ctx,
witnessTrace,
primaryBlock.SignedHeader,
c.primary,
now,
)
if err != nil {
c.logger.Info("Error validating primary's divergent header", "primary", c.primary, "err", err)
continue
@ -117,7 +128,7 @@ func (c *Client) detectDivergence(primaryTrace []*types.LightBlock, now time.Tim
}
c.logger.Error("Sending evidence against witness by primary", "ev", ev,
"primary", c.primary, "witness", supportingWitness)
c.sendEvidence(ev, c.primary)
c.sendEvidence(ctx, ev, c.primary)
// We return the error and don't process anymore witnesses
return e
@ -154,10 +165,10 @@ func (c *Client) detectDivergence(primaryTrace []*types.LightBlock, now time.Tim
// 2: errBadWitness -> the witness has either not responded, doesn't have the header or has given us an invalid one
// Note: In the case of an invalid header we remove the witness
// 3: nil -> the hashes of the two headers match
func (c *Client) compareNewHeaderWithWitness(errc chan error, h *types.SignedHeader,
func (c *Client) compareNewHeaderWithWitness(ctx context.Context, errc chan error, h *types.SignedHeader,
witness provider.Provider, witnessIndex int) {
lightBlock, err := witness.LightBlock(h.Height)
lightBlock, err := witness.LightBlock(ctx, h.Height)
if err != nil {
errc <- errBadWitness{Reason: err, WitnessIndex: witnessIndex}
return
@ -172,8 +183,8 @@ func (c *Client) compareNewHeaderWithWitness(errc chan error, h *types.SignedHea
}
// sendEvidence sends evidence to a provider on a best effort basis.
func (c *Client) sendEvidence(ev *types.LightClientAttackEvidence, receiver provider.Provider) {
err := receiver.ReportEvidence(ev)
func (c *Client) sendEvidence(ctx context.Context, ev *types.LightClientAttackEvidence, receiver provider.Provider) {
err := receiver.ReportEvidence(ctx, ev)
if err != nil {
c.logger.Error("Failed to report evidence to provider", "ev", ev, "provider", receiver)
}
@ -188,6 +199,7 @@ func (c *Client) sendEvidence(ev *types.LightClientAttackEvidence, receiver prov
// 2. The source stops responding, doesn't have the block or sends an invalid header in which case we
// return the error and remove the witness
func (c *Client) examineConflictingHeaderAgainstTrace(
ctx context.Context,
trace []*types.LightBlock,
divergentHeader *types.SignedHeader,
source provider.Provider, now time.Time) ([]*types.LightBlock, *types.LightBlock, error) {
@ -197,7 +209,7 @@ func (c *Client) examineConflictingHeaderAgainstTrace(
for idx, traceBlock := range trace {
// The first block in the trace MUST be the same to the light block that the source produces
// else we cannot continue with verification.
sourceBlock, err := source.LightBlock(traceBlock.Height)
sourceBlock, err := source.LightBlock(ctx, traceBlock.Height)
if err != nil {
return nil, nil, err
}
@ -213,7 +225,7 @@ func (c *Client) examineConflictingHeaderAgainstTrace(
// we check that the source provider can verify a block at the same height of the
// intermediate height
trace, err := c.verifySkipping(source, previouslyVerifiedBlock, sourceBlock, now)
trace, err := c.verifySkipping(ctx, source, previouslyVerifiedBlock, sourceBlock, now)
if err != nil {
return nil, nil, fmt.Errorf("verifySkipping of conflicting header failed: %w", err)
}


+ 9
- 5
light/detector_test.go View File

@ -45,6 +45,7 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) {
primary := mockp.New(chainID, primaryHeaders, primaryValidators)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -60,7 +61,7 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) {
require.NoError(t, err)
// Check verification returns an error.
_, err = c.VerifyLightBlockAtHeight(10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "does not match primary")
}
@ -118,6 +119,7 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) {
primary := mockp.New(chainID, primaryHeaders, primaryValidators)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
@ -133,7 +135,7 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) {
require.NoError(t, err)
// Check verification returns an error.
_, err = c.VerifyLightBlockAtHeight(10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "does not match primary")
}
@ -162,11 +164,12 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) {
func TestClientDivergentTraces(t *testing.T) {
primary := mockp.New(genMockNode(chainID, 10, 5, 2, bTime))
firstBlock, err := primary.LightBlock(1)
firstBlock, err := primary.LightBlock(ctx, 1)
require.NoError(t, err)
witness := mockp.New(genMockNode(chainID, 10, 5, 2, bTime))
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Height: 1,
@ -183,7 +186,7 @@ func TestClientDivergentTraces(t *testing.T) {
// 1. Different nodes therefore a divergent header is produced but the
// light client can't verify it because it has a different trusted header.
_, err = c.VerifyLightBlockAtHeight(10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
assert.Error(t, err)
assert.Equal(t, 0, len(c.Witnesses()))
@ -191,6 +194,7 @@ func TestClientDivergentTraces(t *testing.T) {
// verification should be successful and all the witnesses should remain
c, err = light.NewClient(
ctx,
chainID,
light.TrustOptions{
Height: 1,
@ -204,7 +208,7 @@ func TestClientDivergentTraces(t *testing.T) {
light.MaxRetryAttempts(1),
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
assert.NoError(t, err)
assert.Equal(t, 3, len(c.Witnesses()))
}

+ 7
- 4
light/example_test.go View File

@ -1,6 +1,7 @@
package light_test
import (
"context"
"fmt"
"io/ioutil"
stdlog "log"
@ -40,7 +41,7 @@ func ExampleClient_Update() {
stdlog.Fatal(err)
}
block, err := primary.LightBlock(2)
block, err := primary.LightBlock(context.Background(), 2)
if err != nil {
stdlog.Fatal(err)
}
@ -51,6 +52,7 @@ func ExampleClient_Update() {
}
c, err := light.NewClient(
context.Background(),
chainID,
light.TrustOptions{
Period: 504 * time.Hour, // 21 days
@ -77,7 +79,7 @@ func ExampleClient_Update() {
// monotonic component (see types/time/time.go) b) single instance is being
// run.
// https://github.com/tendermint/tendermint/issues/4489
h, err := c.Update(time.Now().Add(30 * time.Minute))
h, err := c.Update(context.Background(), time.Now().Add(30*time.Minute))
if err != nil {
stdlog.Fatal(err)
}
@ -111,7 +113,7 @@ func ExampleClient_VerifyLightBlockAtHeight() {
stdlog.Fatal(err)
}
block, err := primary.LightBlock(2)
block, err := primary.LightBlock(context.Background(), 2)
if err != nil {
stdlog.Fatal(err)
}
@ -122,6 +124,7 @@ func ExampleClient_VerifyLightBlockAtHeight() {
}
c, err := light.NewClient(
context.Background(),
chainID,
light.TrustOptions{
Period: 504 * time.Hour, // 21 days
@ -142,7 +145,7 @@ func ExampleClient_VerifyLightBlockAtHeight() {
}
}()
_, err = c.VerifyLightBlockAtHeight(3, time.Now())
_, err = c.VerifyLightBlockAtHeight(context.Background(), 3, time.Now())
if err != nil {
stdlog.Fatal(err)
}


+ 10
- 9
light/provider/http/http.go View File

@ -1,6 +1,7 @@
package http
import (
"context"
"fmt"
"math/rand"
"regexp"
@ -61,18 +62,18 @@ func (p *http) String() string {
// LightBlock fetches a LightBlock at the given height and checks the
// chainID matches.
func (p *http) LightBlock(height int64) (*types.LightBlock, error) {
func (p *http) LightBlock(ctx context.Context, height int64) (*types.LightBlock, error) {
h, err := validateHeight(height)
if err != nil {
return nil, provider.ErrBadLightBlock{Reason: err}
}
sh, err := p.signedHeader(h)
sh, err := p.signedHeader(ctx, h)
if err != nil {
return nil, err
}
vs, err := p.validatorSet(h)
vs, err := p.validatorSet(ctx, h)
if err != nil {
return nil, err
}
@ -91,12 +92,12 @@ func (p *http) LightBlock(height int64) (*types.LightBlock, error) {
}
// ReportEvidence calls `/broadcast_evidence` endpoint.
func (p *http) ReportEvidence(ev types.Evidence) error {
_, err := p.client.BroadcastEvidence(ev)
func (p *http) ReportEvidence(ctx context.Context, ev types.Evidence) error {
_, err := p.client.BroadcastEvidence(ctx, ev)
return err
}
func (p *http) validatorSet(height *int64) (*types.ValidatorSet, error) {
func (p *http) validatorSet(ctx context.Context, height *int64) (*types.ValidatorSet, error) {
var (
maxPerPage = 100
vals = []*types.Validator{}
@ -105,7 +106,7 @@ func (p *http) validatorSet(height *int64) (*types.ValidatorSet, error) {
for len(vals)%maxPerPage == 0 {
for attempt := 1; attempt <= maxRetryAttempts; attempt++ {
res, err := p.client.Validators(height, &page, &maxPerPage)
res, err := p.client.Validators(ctx, height, &page, &maxPerPage)
if err != nil {
// TODO: standardize errors on the RPC side
if regexpMissingHeight.MatchString(err.Error()) {
@ -138,9 +139,9 @@ func (p *http) validatorSet(height *int64) (*types.ValidatorSet, error) {
return valSet, nil
}
func (p *http) signedHeader(height *int64) (*types.SignedHeader, error) {
func (p *http) signedHeader(ctx context.Context, height *int64) (*types.SignedHeader, error) {
for attempt := 1; attempt <= maxRetryAttempts; attempt++ {
commit, err := p.client.Commit(height)
commit, err := p.client.Commit(ctx, height)
if err != nil {
// TODO: standardize errors on the RPC side
if regexpMissingHeight.MatchString(err.Error()) {


+ 5
- 4
light/provider/http/http_test.go View File

@ -1,6 +1,7 @@
package http_test
import (
"context"
"fmt"
"os"
"testing"
@ -65,7 +66,7 @@ func TestProvider(t *testing.T) {
require.NoError(t, err)
// let's get the highest block
sh, err := p.LightBlock(0)
sh, err := p.LightBlock(context.Background(), 0)
require.NoError(t, err)
assert.True(t, sh.Height < 1000)
@ -74,16 +75,16 @@ func TestProvider(t *testing.T) {
// historical queries now work :)
lower := sh.Height - 3
sh, err = p.LightBlock(lower)
sh, err = p.LightBlock(context.Background(), lower)
require.NoError(t, err)
assert.Equal(t, lower, sh.Height)
// fetching missing heights (both future and pruned) should return appropriate errors
_, err = p.LightBlock(1000)
_, err = p.LightBlock(context.Background(), 1000)
require.Error(t, err)
assert.Equal(t, provider.ErrLightBlockNotFound, err)
_, err = p.LightBlock(1)
_, err = p.LightBlock(context.Background(), 1)
require.Error(t, err)
assert.Equal(t, provider.ErrLightBlockNotFound, err)
}

+ 3
- 2
light/provider/mock/deadmock.go View File

@ -1,6 +1,7 @@
package mock
import (
"context"
"errors"
"github.com/tendermint/tendermint/light/provider"
@ -22,10 +23,10 @@ func (p *deadMock) ChainID() string { return p.chainID }
func (p *deadMock) String() string { return "deadMock" }
func (p *deadMock) LightBlock(height int64) (*types.LightBlock, error) {
func (p *deadMock) LightBlock(_ context.Context, height int64) (*types.LightBlock, error) {
return nil, errNoResp
}
func (p *deadMock) ReportEvidence(ev types.Evidence) error {
func (p *deadMock) ReportEvidence(_ context.Context, ev types.Evidence) error {
return errNoResp
}

+ 3
- 2
light/provider/mock/mock.go View File

@ -1,6 +1,7 @@
package mock
import (
"context"
"errors"
"fmt"
"strings"
@ -48,7 +49,7 @@ func (p *Mock) String() string {
return fmt.Sprintf("Mock{headers: %s, vals: %v}", headers.String(), vals.String())
}
func (p *Mock) LightBlock(height int64) (*types.LightBlock, error) {
func (p *Mock) LightBlock(_ context.Context, height int64) (*types.LightBlock, error) {
var lb *types.LightBlock
if height == 0 && len(p.headers) > 0 {
sh := p.headers[int64(len(p.headers))]
@ -79,7 +80,7 @@ func (p *Mock) LightBlock(height int64) (*types.LightBlock, error) {
return lb, nil
}
func (p *Mock) ReportEvidence(ev types.Evidence) error {
func (p *Mock) ReportEvidence(_ context.Context, ev types.Evidence) error {
p.evidenceToReport[string(ev.Hash())] = ev
return nil
}


+ 4
- 2
light/provider/provider.go View File

@ -1,6 +1,8 @@
package provider
import (
"context"
"github.com/tendermint/tendermint/types"
)
@ -20,8 +22,8 @@ type Provider interface {
// issues, an error will be returned.
// If there's no LightBlock for the given height, ErrLightBlockNotFound
// error is returned.
LightBlock(height int64) (*types.LightBlock, error)
LightBlock(ctx context.Context, height int64) (*types.LightBlock, error)
// ReportEvidence reports an evidence of misbehavior.
ReportEvidence(ev types.Evidence) error
ReportEvidence(context.Context, types.Evidence) error
}

+ 23
- 23
light/proxy/routes.go View File

@ -53,7 +53,7 @@ type rpcHealthFunc func(ctx *rpctypes.Context) (*ctypes.ResultHealth, error)
func makeHealthFunc(c *lrpc.Client) rpcHealthFunc {
return func(ctx *rpctypes.Context) (*ctypes.ResultHealth, error) {
return c.Health()
return c.Health(ctx.Context())
}
}
@ -62,7 +62,7 @@ type rpcStatusFunc func(ctx *rpctypes.Context) (*ctypes.ResultStatus, error)
// nolint: interfacer
func makeStatusFunc(c *lrpc.Client) rpcStatusFunc {
return func(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
return c.Status()
return c.Status(ctx.Context())
}
}
@ -70,7 +70,7 @@ type rpcNetInfoFunc func(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ct
func makeNetInfoFunc(c *lrpc.Client) rpcNetInfoFunc {
return func(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultNetInfo, error) {
return c.NetInfo()
return c.NetInfo(ctx.Context())
}
}
@ -78,7 +78,7 @@ type rpcBlockchainInfoFunc func(ctx *rpctypes.Context, minHeight, maxHeight int6
func makeBlockchainInfoFunc(c *lrpc.Client) rpcBlockchainInfoFunc {
return func(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
return c.BlockchainInfo(minHeight, maxHeight)
return c.BlockchainInfo(ctx.Context(), minHeight, maxHeight)
}
}
@ -86,7 +86,7 @@ type rpcGenesisFunc func(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error)
func makeGenesisFunc(c *lrpc.Client) rpcGenesisFunc {
return func(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) {
return c.Genesis()
return c.Genesis(ctx.Context())
}
}
@ -94,7 +94,7 @@ type rpcBlockFunc func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBloc
func makeBlockFunc(c *lrpc.Client) rpcBlockFunc {
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error) {
return c.Block(height)
return c.Block(ctx.Context(), height)
}
}
@ -102,7 +102,7 @@ type rpcBlockByHashFunc func(ctx *rpctypes.Context, hash []byte) (*ctypes.Result
func makeBlockByHashFunc(c *lrpc.Client) rpcBlockByHashFunc {
return func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
return c.BlockByHash(hash)
return c.BlockByHash(ctx.Context(), hash)
}
}
@ -110,7 +110,7 @@ type rpcBlockResultsFunc func(ctx *rpctypes.Context, height *int64) (*ctypes.Res
func makeBlockResultsFunc(c *lrpc.Client) rpcBlockResultsFunc {
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlockResults, error) {
return c.BlockResults(height)
return c.BlockResults(ctx.Context(), height)
}
}
@ -118,7 +118,7 @@ type rpcCommitFunc func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCom
func makeCommitFunc(c *lrpc.Client) rpcCommitFunc {
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) {
return c.Commit(height)
return c.Commit(ctx.Context(), height)
}
}
@ -126,7 +126,7 @@ type rpcTxFunc func(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.Res
func makeTxFunc(c *lrpc.Client) rpcTxFunc {
return func(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
return c.Tx(hash, prove)
return c.Tx(ctx.Context(), hash, prove)
}
}
@ -136,7 +136,7 @@ type rpcTxSearchFunc func(ctx *rpctypes.Context, query string, prove bool,
func makeTxSearchFunc(c *lrpc.Client) rpcTxSearchFunc {
return func(ctx *rpctypes.Context, query string, prove bool, page, perPage *int, orderBy string) (
*ctypes.ResultTxSearch, error) {
return c.TxSearch(query, prove, page, perPage, orderBy)
return c.TxSearch(ctx.Context(), query, prove, page, perPage, orderBy)
}
}
@ -145,7 +145,7 @@ type rpcValidatorsFunc func(ctx *rpctypes.Context, height *int64,
func makeValidatorsFunc(c *lrpc.Client) rpcValidatorsFunc {
return func(ctx *rpctypes.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
return c.Validators(height, page, perPage)
return c.Validators(ctx.Context(), height, page, perPage)
}
}
@ -153,7 +153,7 @@ type rpcDumpConsensusStateFunc func(ctx *rpctypes.Context) (*ctypes.ResultDumpCo
func makeDumpConsensusStateFunc(c *lrpc.Client) rpcDumpConsensusStateFunc {
return func(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error) {
return c.DumpConsensusState()
return c.DumpConsensusState(ctx.Context())
}
}
@ -161,7 +161,7 @@ type rpcConsensusStateFunc func(ctx *rpctypes.Context) (*ctypes.ResultConsensusS
func makeConsensusStateFunc(c *lrpc.Client) rpcConsensusStateFunc {
return func(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) {
return c.ConsensusState()
return c.ConsensusState(ctx.Context())
}
}
@ -169,7 +169,7 @@ type rpcConsensusParamsFunc func(ctx *rpctypes.Context, height *int64) (*ctypes.
func makeConsensusParamsFunc(c *lrpc.Client) rpcConsensusParamsFunc {
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultConsensusParams, error) {
return c.ConsensusParams(height)
return c.ConsensusParams(ctx.Context(), height)
}
}
@ -177,7 +177,7 @@ type rpcUnconfirmedTxsFunc func(ctx *rpctypes.Context, limit *int) (*ctypes.Resu
func makeUnconfirmedTxsFunc(c *lrpc.Client) rpcUnconfirmedTxsFunc {
return func(ctx *rpctypes.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error) {
return c.UnconfirmedTxs(limit)
return c.UnconfirmedTxs(ctx.Context(), limit)
}
}
@ -185,7 +185,7 @@ type rpcNumUnconfirmedTxsFunc func(ctx *rpctypes.Context) (*ctypes.ResultUnconfi
func makeNumUnconfirmedTxsFunc(c *lrpc.Client) rpcNumUnconfirmedTxsFunc {
return func(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) {
return c.NumUnconfirmedTxs()
return c.NumUnconfirmedTxs(ctx.Context())
}
}
@ -193,7 +193,7 @@ type rpcBroadcastTxCommitFunc func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.
func makeBroadcastTxCommitFunc(c *lrpc.Client) rpcBroadcastTxCommitFunc {
return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
return c.BroadcastTxCommit(tx)
return c.BroadcastTxCommit(ctx.Context(), tx)
}
}
@ -201,7 +201,7 @@ type rpcBroadcastTxSyncFunc func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.Re
func makeBroadcastTxSyncFunc(c *lrpc.Client) rpcBroadcastTxSyncFunc {
return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.BroadcastTxSync(tx)
return c.BroadcastTxSync(ctx.Context(), tx)
}
}
@ -209,7 +209,7 @@ type rpcBroadcastTxAsyncFunc func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.R
func makeBroadcastTxAsyncFunc(c *lrpc.Client) rpcBroadcastTxAsyncFunc {
return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.BroadcastTxAsync(tx)
return c.BroadcastTxAsync(ctx.Context(), tx)
}
}
@ -217,7 +217,7 @@ type rpcABCIQueryFunc func(ctx *rpctypes.Context, path string, data bytes.HexByt
func makeABCIQueryFunc(c *lrpc.Client) rpcABCIQueryFunc {
return func(ctx *rpctypes.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQuery(path, data)
return c.ABCIQuery(ctx.Context(), path, data)
}
}
@ -225,7 +225,7 @@ type rpcABCIInfoFunc func(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error)
func makeABCIInfoFunc(c *lrpc.Client) rpcABCIInfoFunc {
return func(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) {
return c.ABCIInfo()
return c.ABCIInfo(ctx.Context())
}
}
@ -234,6 +234,6 @@ type rpcBroadcastEvidenceFunc func(ctx *rpctypes.Context, ev types.Evidence) (*c
// nolint: interfacer
func makeBroadcastEvidenceFunc(c *lrpc.Client) rpcBroadcastEvidenceFunc {
return func(ctx *rpctypes.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
return c.BroadcastEvidence(ev)
return c.BroadcastEvidence(ctx.Context(), ev)
}
}

+ 62
- 62
light/rpc/client.go View File

@ -61,24 +61,24 @@ func (c *Client) OnStop() {
}
}
func (c *Client) Status() (*ctypes.ResultStatus, error) {
return c.next.Status()
func (c *Client) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
return c.next.Status(ctx)
}
func (c *Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
return c.next.ABCIInfo()
func (c *Client) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
return c.next.ABCIInfo(ctx)
}
func (c *Client) ABCIQuery(path string, data tmbytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
func (c *Client) ABCIQuery(ctx context.Context, path string, data tmbytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions)
}
// GetWithProofOptions is useful if you want full access to the ABCIQueryOptions.
// XXX Usage of path? It's not used, and sometimes it's /, sometimes /key, sometimes /store.
func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes,
func (c *Client) ABCIQueryWithOptions(ctx context.Context, path string, data tmbytes.HexBytes,
opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
res, err := c.next.ABCIQueryWithOptions(path, data, opts)
res, err := c.next.ABCIQueryWithOptions(ctx, path, data, opts)
if err != nil {
return nil, err
}
@ -97,7 +97,7 @@ func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes,
// Update the light client if we're behind.
// NOTE: AppHash for height H is in header H+1.
l, err := c.updateLightClientIfNeededTo(resp.Height + 1)
l, err := c.updateLightClientIfNeededTo(ctx, resp.Height+1)
if err != nil {
return nil, err
}
@ -129,44 +129,44 @@ func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes,
return &ctypes.ResultABCIQuery{Response: resp}, nil
}
func (c *Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
return c.next.BroadcastTxCommit(tx)
func (c *Client) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
return c.next.BroadcastTxCommit(ctx, tx)
}
func (c *Client) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.next.BroadcastTxAsync(tx)
func (c *Client) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.next.BroadcastTxAsync(ctx, tx)
}
func (c *Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.next.BroadcastTxSync(tx)
func (c *Client) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.next.BroadcastTxSync(ctx, tx)
}
func (c *Client) UnconfirmedTxs(limit *int) (*ctypes.ResultUnconfirmedTxs, error) {
return c.next.UnconfirmedTxs(limit)
func (c *Client) UnconfirmedTxs(ctx context.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error) {
return c.next.UnconfirmedTxs(ctx, limit)
}
func (c *Client) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
return c.next.NumUnconfirmedTxs()
func (c *Client) NumUnconfirmedTxs(ctx context.Context) (*ctypes.ResultUnconfirmedTxs, error) {
return c.next.NumUnconfirmedTxs(ctx)
}
func (c *Client) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
return c.next.CheckTx(tx)
func (c *Client) CheckTx(ctx context.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
return c.next.CheckTx(ctx, tx)
}
func (c *Client) NetInfo() (*ctypes.ResultNetInfo, error) {
return c.next.NetInfo()
func (c *Client) NetInfo(ctx context.Context) (*ctypes.ResultNetInfo, error) {
return c.next.NetInfo(ctx)
}
func (c *Client) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
return c.next.DumpConsensusState()
func (c *Client) DumpConsensusState(ctx context.Context) (*ctypes.ResultDumpConsensusState, error) {
return c.next.DumpConsensusState(ctx)
}
func (c *Client) ConsensusState() (*ctypes.ResultConsensusState, error) {
return c.next.ConsensusState()
func (c *Client) ConsensusState(ctx context.Context) (*ctypes.ResultConsensusState, error) {
return c.next.ConsensusState(ctx)
}
func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
res, err := c.next.ConsensusParams(height)
func (c *Client) ConsensusParams(ctx context.Context, height *int64) (*ctypes.ResultConsensusParams, error) {
res, err := c.next.ConsensusParams(ctx, height)
if err != nil {
return nil, err
}
@ -180,7 +180,7 @@ func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams,
}
// Update the light client if we're behind.
l, err := c.updateLightClientIfNeededTo(res.BlockHeight)
l, err := c.updateLightClientIfNeededTo(ctx, res.BlockHeight)
if err != nil {
return nil, err
}
@ -194,14 +194,14 @@ func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams,
return res, nil
}
func (c *Client) Health() (*ctypes.ResultHealth, error) {
return c.next.Health()
func (c *Client) Health(ctx context.Context) (*ctypes.ResultHealth, error) {
return c.next.Health(ctx)
}
// BlockchainInfo calls rpcclient#BlockchainInfo and then verifies every header
// returned.
func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
res, err := c.next.BlockchainInfo(minHeight, maxHeight)
func (c *Client) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
res, err := c.next.BlockchainInfo(ctx, minHeight, maxHeight)
if err != nil {
return nil, err
}
@ -219,7 +219,7 @@ func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlock
// Update the light client if we're behind.
if len(res.BlockMetas) > 0 {
lastHeight := res.BlockMetas[len(res.BlockMetas)-1].Header.Height
if _, err := c.updateLightClientIfNeededTo(lastHeight); err != nil {
if _, err := c.updateLightClientIfNeededTo(ctx, lastHeight); err != nil {
return nil, err
}
}
@ -239,13 +239,13 @@ func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlock
return res, nil
}
func (c *Client) Genesis() (*ctypes.ResultGenesis, error) {
return c.next.Genesis()
func (c *Client) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) {
return c.next.Genesis(ctx)
}
// Block calls rpcclient#Block and then verifies the result.
func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
res, err := c.next.Block(height)
func (c *Client) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) {
res, err := c.next.Block(ctx, height)
if err != nil {
return nil, err
}
@ -263,7 +263,7 @@ func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
}
// Update the light client if we're behind.
l, err := c.updateLightClientIfNeededTo(res.Block.Height)
l, err := c.updateLightClientIfNeededTo(ctx, res.Block.Height)
if err != nil {
return nil, err
}
@ -278,8 +278,8 @@ func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
}
// BlockByHash calls rpcclient#BlockByHash and then verifies the result.
func (c *Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
res, err := c.next.BlockByHash(hash)
func (c *Client) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) {
res, err := c.next.BlockByHash(ctx, hash)
if err != nil {
return nil, err
}
@ -297,7 +297,7 @@ func (c *Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
}
// Update the light client if we're behind.
l, err := c.updateLightClientIfNeededTo(res.Block.Height)
l, err := c.updateLightClientIfNeededTo(ctx, res.Block.Height)
if err != nil {
return nil, err
}
@ -313,10 +313,10 @@ func (c *Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
// BlockResults returns the block results for the given height. If no height is
// provided, the results of the block preceding the latest are returned.
func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
func (c *Client) BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error) {
var h int64
if height == nil {
res, err := c.next.Status()
res, err := c.next.Status(ctx)
if err != nil {
return nil, fmt.Errorf("can't get latest height: %w", err)
}
@ -327,7 +327,7 @@ func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error)
h = *height
}
res, err := c.next.BlockResults(&h)
res, err := c.next.BlockResults(ctx, &h)
if err != nil {
return nil, err
}
@ -338,7 +338,7 @@ func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error)
}
// Update the light client if we're behind.
trustedBlock, err := c.updateLightClientIfNeededTo(h + 1)
trustedBlock, err := c.updateLightClientIfNeededTo(ctx, h+1)
if err != nil {
return nil, err
}
@ -374,8 +374,8 @@ func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error)
return res, nil
}
func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
res, err := c.next.Commit(height)
func (c *Client) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
res, err := c.next.Commit(ctx, height)
if err != nil {
return nil, err
}
@ -389,7 +389,7 @@ func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
}
// Update the light client if we're behind.
l, err := c.updateLightClientIfNeededTo(res.Height)
l, err := c.updateLightClientIfNeededTo(ctx, res.Height)
if err != nil {
return nil, err
}
@ -405,8 +405,8 @@ func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
// Tx calls rpcclient#Tx method and then verifies the proof if such was
// requested.
func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
res, err := c.next.Tx(hash, prove)
func (c *Client) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
res, err := c.next.Tx(ctx, hash, prove)
if err != nil || !prove {
return res, err
}
@ -417,7 +417,7 @@ func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
}
// Update the light client if we're behind.
l, err := c.updateLightClientIfNeededTo(res.Height)
l, err := c.updateLightClientIfNeededTo(ctx, res.Height)
if err != nil {
return nil, err
}
@ -426,17 +426,17 @@ func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
return res, res.Proof.Validate(l.DataHash)
}
func (c *Client) TxSearch(query string, prove bool, page, perPage *int, orderBy string) (
func (c *Client) TxSearch(ctx context.Context, query string, prove bool, page, perPage *int, orderBy string) (
*ctypes.ResultTxSearch, error) {
return c.next.TxSearch(query, prove, page, perPage, orderBy)
return c.next.TxSearch(ctx, query, prove, page, perPage, orderBy)
}
// Validators fetches and verifies validators.
//
// WARNING: only full validator sets are verified (when length of validators is
// less than +perPage+. +perPage+ default is 30, max is 100).
func (c *Client) Validators(height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
res, err := c.next.Validators(height, page, perPage)
func (c *Client) Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
res, err := c.next.Validators(ctx, height, page, perPage)
if err != nil {
return nil, err
}
@ -454,7 +454,7 @@ func (c *Client) Validators(height *int64, page, perPage *int) (*ctypes.ResultVa
}
// Update the light client if we're behind.
l, err := c.updateLightClientIfNeededTo(updateHeight)
l, err := c.updateLightClientIfNeededTo(ctx, updateHeight)
if err != nil {
return nil, err
}
@ -480,8 +480,8 @@ func (c *Client) Validators(height *int64, page, perPage *int) (*ctypes.ResultVa
return res, nil
}
func (c *Client) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
return c.next.BroadcastEvidence(ev)
func (c *Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
return c.next.BroadcastEvidence(ctx, ev)
}
func (c *Client) Subscribe(ctx context.Context, subscriber, query string,
@ -497,8 +497,8 @@ func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error {
return c.next.UnsubscribeAll(ctx, subscriber)
}
func (c *Client) updateLightClientIfNeededTo(height int64) (*types.LightBlock, error) {
l, err := c.lc.VerifyLightBlockAtHeight(height, time.Now())
func (c *Client) updateLightClientIfNeededTo(ctx context.Context, height int64) (*types.LightBlock, error) {
l, err := c.lc.VerifyLightBlockAtHeight(ctx, height, time.Now())
if err != nil {
return nil, fmt.Errorf("failed to update light client to %d: %w", height, err)
}


+ 3
- 0
light/setup.go View File

@ -1,6 +1,7 @@
package light
import (
"context"
"time"
"github.com/tendermint/tendermint/light/provider"
@ -15,6 +16,7 @@ import (
// See all Option(s) for the additional configuration.
// See NewClient.
func NewHTTPClient(
ctx context.Context,
chainID string,
trustOptions TrustOptions,
primaryAddress string,
@ -28,6 +30,7 @@ func NewHTTPClient(
}
return NewClient(
ctx,
chainID,
trustOptions,
providers[len(providers)-1],


+ 3
- 0
node/node.go View File

@ -564,7 +564,10 @@ func startStateSync(ssR *statesync.Reactor, bcR fastSyncReactor, conR *cs.Reacto
if stateProvider == nil {
var err error
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
stateProvider, err = statesync.NewLightClientStateProvider(
ctx,
state.ChainID, state.Version, state.InitialHeight,
config.RPCServers, light.TrustOptions{
Period: config.TrustPeriod,


+ 3
- 2
rpc/client/event_test.go View File

@ -126,12 +126,13 @@ func testTxEventsSent(t *testing.T, broadcastMethod string) {
var (
txres *ctypes.ResultBroadcastTx
err error
ctx = context.Background()
)
switch broadcastMethod {
case "async":
txres, err = c.BroadcastTxAsync(tx)
txres, err = c.BroadcastTxAsync(ctx, tx)
case "sync":
txres, err = c.BroadcastTxSync(tx)
txres, err = c.BroadcastTxSync(ctx, tx)
default:
panic(fmt.Sprintf("Unknown broadcastMethod %s", broadcastMethod))
}


+ 6
- 5
rpc/client/evidence_test.go View File

@ -2,6 +2,7 @@ package client_test
import (
"bytes"
"context"
"testing"
"time"
@ -119,18 +120,18 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) {
correct, fakes := makeEvidences(t, pv, chainID)
t.Logf("client %d", i)
result, err := c.BroadcastEvidence(correct)
result, err := c.BroadcastEvidence(context.Background(), correct)
require.NoError(t, err, "BroadcastEvidence(%s) failed", correct)
assert.Equal(t, correct.Hash(), result.Hash, "expected result hash to match evidence hash")
status, err := c.Status()
status, err := c.Status(context.Background())
require.NoError(t, err)
err = client.WaitForHeight(c, status.SyncInfo.LatestBlockHeight+2, nil)
require.NoError(t, err)
ed25519pub := pv.Key.PubKey.(ed25519.PubKey)
rawpub := ed25519pub.Bytes()
result2, err := c.ABCIQuery("/val", rawpub)
result2, err := c.ABCIQuery(context.Background(), "/val", rawpub)
require.NoError(t, err)
qres := result2.Response
require.True(t, qres.IsOK())
@ -146,7 +147,7 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) {
require.Equal(t, int64(9), v.Power, "Stored Power not equal with expected, value %v", string(qres.Value))
for _, fake := range fakes {
_, err := c.BroadcastEvidence(fake)
_, err := c.BroadcastEvidence(context.Background(), fake)
require.Error(t, err, "BroadcastEvidence(%s) succeeded, but the evidence was fake", fake)
}
}
@ -154,7 +155,7 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) {
func TestBroadcastEmptyEvidence(t *testing.T) {
for _, c := range GetClients() {
_, err := c.BroadcastEvidence(nil)
_, err := c.BroadcastEvidence(context.Background(), nil)
assert.Error(t, err)
}
}

+ 7
- 6
rpc/client/examples_test.go View File

@ -2,6 +2,7 @@ package client_test
import (
"bytes"
"context"
"fmt"
"log"
@ -31,7 +32,7 @@ func ExampleHTTP_simple() {
// Broadcast the transaction and wait for it to commit (rather use
// c.BroadcastTxSync though in production).
bres, err := c.BroadcastTxCommit(tx)
bres, err := c.BroadcastTxCommit(context.Background(), tx)
if err != nil {
log.Fatal(err)
}
@ -40,7 +41,7 @@ func ExampleHTTP_simple() {
}
// Now try to fetch the value for the key
qres, err := c.ABCIQuery("/key", k)
qres, err := c.ABCIQuery(context.Background(), "/key", k)
if err != nil {
log.Fatal(err)
}
@ -95,26 +96,26 @@ func ExampleHTTP_batching() {
for _, tx := range txs {
// Broadcast the transaction and wait for it to commit (rather use
// c.BroadcastTxSync though in production).
if _, err := batch.BroadcastTxCommit(tx); err != nil {
if _, err := batch.BroadcastTxCommit(context.Background(), tx); err != nil {
log.Fatal(err)
}
}
// Send the batch of 2 transactions
if _, err := batch.Send(); err != nil {
if _, err := batch.Send(context.Background()); err != nil {
log.Fatal(err)
}
// Now let's query for the original results as a batch
keys := [][]byte{k1, k2}
for _, key := range keys {
if _, err := batch.ABCIQuery("/key", key); err != nil {
if _, err := batch.ABCIQuery(context.Background(), "/key", key); err != nil {
log.Fatal(err)
}
}
// Send the 2 queries and keep the results
results, err := batch.Send()
results, err := batch.Send(context.Background())
if err != nil {
log.Fatal(err)
}


+ 1
- 1
rpc/client/helpers.go View File

@ -38,7 +38,7 @@ func WaitForHeight(c StatusClient, h int64, waiter Waiter) error {
}
delta := int64(1)
for delta > 0 {
s, err := c.Status()
s, err := c.Status(context.Background())
if err != nil {
return err
}


+ 99
- 53
rpc/client/http/http.go View File

@ -183,8 +183,8 @@ func (c *HTTP) NewBatch() *BatchHTTP {
// compilation of the batched requests and send them off using the client as a
// single request. On success, this returns a list of the deserialized results
// from each request in the sent batch.
func (b *BatchHTTP) Send() ([]interface{}, error) {
return b.rpcBatch.Send()
func (b *BatchHTTP) Send(ctx context.Context) ([]interface{}, error) {
return b.rpcBatch.Send(ctx)
}
// Clear will empty out this batch of requests and return the number of requests
@ -201,9 +201,9 @@ func (b *BatchHTTP) Count() int {
//-----------------------------------------------------------------------------
// baseRPCClient
func (c *baseRPCClient) Status() (*ctypes.ResultStatus, error) {
func (c *baseRPCClient) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
result := new(ctypes.ResultStatus)
_, err := c.caller.Call("status", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "status", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
@ -211,9 +211,9 @@ func (c *baseRPCClient) Status() (*ctypes.ResultStatus, error) {
return result, nil
}
func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
func (c *baseRPCClient) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
result := new(ctypes.ResultABCIInfo)
_, err := c.caller.Call("abci_info", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "abci_info", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
@ -221,16 +221,21 @@ func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
return result, nil
}
func (c *baseRPCClient) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
func (c *baseRPCClient) ABCIQuery(
ctx context.Context,
path string,
data bytes.HexBytes,
) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions)
}
func (c *baseRPCClient) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
result := new(ctypes.ResultABCIQuery)
_, err := c.caller.Call("abci_query",
_, err := c.caller.Call(ctx, "abci_query",
map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove},
result)
if err != nil {
@ -240,115 +245,138 @@ func (c *baseRPCClient) ABCIQueryWithOptions(
return result, nil
}
func (c *baseRPCClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
func (c *baseRPCClient) BroadcastTxCommit(
ctx context.Context,
tx types.Tx,
) (*ctypes.ResultBroadcastTxCommit, error) {
result := new(ctypes.ResultBroadcastTxCommit)
_, err := c.caller.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
_, err := c.caller.Call(ctx, "broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.broadcastTX("broadcast_tx_async", tx)
func (c *baseRPCClient) BroadcastTxAsync(
ctx context.Context,
tx types.Tx,
) (*ctypes.ResultBroadcastTx, error) {
return c.broadcastTX(ctx, "broadcast_tx_async", tx)
}
func (c *baseRPCClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return c.broadcastTX("broadcast_tx_sync", tx)
func (c *baseRPCClient) BroadcastTxSync(
ctx context.Context,
tx types.Tx,
) (*ctypes.ResultBroadcastTx, error) {
return c.broadcastTX(ctx, "broadcast_tx_sync", tx)
}
func (c *baseRPCClient) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (c *baseRPCClient) broadcastTX(
ctx context.Context,
route string,
tx types.Tx,
) (*ctypes.ResultBroadcastTx, error) {
result := new(ctypes.ResultBroadcastTx)
_, err := c.caller.Call(route, map[string]interface{}{"tx": tx}, result)
_, err := c.caller.Call(ctx, route, map[string]interface{}{"tx": tx}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) UnconfirmedTxs(limit *int) (*ctypes.ResultUnconfirmedTxs, error) {
func (c *baseRPCClient) UnconfirmedTxs(
ctx context.Context,
limit *int,
) (*ctypes.ResultUnconfirmedTxs, error) {
result := new(ctypes.ResultUnconfirmedTxs)
params := make(map[string]interface{})
if limit != nil {
params["limit"] = limit
}
_, err := c.caller.Call("unconfirmed_txs", params, result)
_, err := c.caller.Call(ctx, "unconfirmed_txs", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
func (c *baseRPCClient) NumUnconfirmedTxs(ctx context.Context) (*ctypes.ResultUnconfirmedTxs, error) {
result := new(ctypes.ResultUnconfirmedTxs)
_, err := c.caller.Call("num_unconfirmed_txs", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "num_unconfirmed_txs", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
func (c *baseRPCClient) CheckTx(ctx context.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
result := new(ctypes.ResultCheckTx)
_, err := c.caller.Call("check_tx", map[string]interface{}{"tx": tx}, result)
_, err := c.caller.Call(ctx, "check_tx", map[string]interface{}{"tx": tx}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) {
func (c *baseRPCClient) NetInfo(ctx context.Context) (*ctypes.ResultNetInfo, error) {
result := new(ctypes.ResultNetInfo)
_, err := c.caller.Call("net_info", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "net_info", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
func (c *baseRPCClient) DumpConsensusState(ctx context.Context) (*ctypes.ResultDumpConsensusState, error) {
result := new(ctypes.ResultDumpConsensusState)
_, err := c.caller.Call("dump_consensus_state", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "dump_consensus_state", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) ConsensusState() (*ctypes.ResultConsensusState, error) {
func (c *baseRPCClient) ConsensusState(ctx context.Context) (*ctypes.ResultConsensusState, error) {
result := new(ctypes.ResultConsensusState)
_, err := c.caller.Call("consensus_state", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "consensus_state", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
func (c *baseRPCClient) ConsensusParams(
ctx context.Context,
height *int64,
) (*ctypes.ResultConsensusParams, error) {
result := new(ctypes.ResultConsensusParams)
params := make(map[string]interface{})
if height != nil {
params["height"] = height
}
_, err := c.caller.Call("consensus_params", params, result)
_, err := c.caller.Call(ctx, "consensus_params", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) Health() (*ctypes.ResultHealth, error) {
func (c *baseRPCClient) Health(ctx context.Context) (*ctypes.ResultHealth, error) {
result := new(ctypes.ResultHealth)
_, err := c.caller.Call("health", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "health", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
func (c *baseRPCClient) BlockchainInfo(
ctx context.Context,
minHeight,
maxHeight int64,
) (*ctypes.ResultBlockchainInfo, error) {
result := new(ctypes.ResultBlockchainInfo)
_, err := c.caller.Call("blockchain",
_, err := c.caller.Call(ctx, "blockchain",
map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight},
result)
if err != nil {
@ -357,80 +385,90 @@ func (c *baseRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.Resu
return result, nil
}
func (c *baseRPCClient) Genesis() (*ctypes.ResultGenesis, error) {
func (c *baseRPCClient) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) {
result := new(ctypes.ResultGenesis)
_, err := c.caller.Call("genesis", map[string]interface{}{}, result)
_, err := c.caller.Call(ctx, "genesis", map[string]interface{}{}, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) {
func (c *baseRPCClient) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) {
result := new(ctypes.ResultBlock)
params := make(map[string]interface{})
if height != nil {
params["height"] = height
}
_, err := c.caller.Call("block", params, result)
_, err := c.caller.Call(ctx, "block", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
func (c *baseRPCClient) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) {
result := new(ctypes.ResultBlock)
params := map[string]interface{}{
"hash": hash,
}
_, err := c.caller.Call("block_by_hash", params, result)
_, err := c.caller.Call(ctx, "block_by_hash", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
func (c *baseRPCClient) BlockResults(
ctx context.Context,
height *int64,
) (*ctypes.ResultBlockResults, error) {
result := new(ctypes.ResultBlockResults)
params := make(map[string]interface{})
if height != nil {
params["height"] = height
}
_, err := c.caller.Call("block_results", params, result)
_, err := c.caller.Call(ctx, "block_results", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) Commit(height *int64) (*ctypes.ResultCommit, error) {
func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
result := new(ctypes.ResultCommit)
params := make(map[string]interface{})
if height != nil {
params["height"] = height
}
_, err := c.caller.Call("commit", params, result)
_, err := c.caller.Call(ctx, "commit", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
func (c *baseRPCClient) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
result := new(ctypes.ResultTx)
params := map[string]interface{}{
"hash": hash,
"prove": prove,
}
_, err := c.caller.Call("tx", params, result)
_, err := c.caller.Call(ctx, "tx", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage *int, orderBy string) (
func (c *baseRPCClient) TxSearch(
ctx context.Context,
query string,
prove bool,
page,
perPage *int,
orderBy string,
) (
*ctypes.ResultTxSearch, error) {
result := new(ctypes.ResultTxSearch)
params := map[string]interface{}{
@ -444,14 +482,19 @@ func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage *int, o
if perPage != nil {
params["per_page"] = perPage
}
_, err := c.caller.Call("tx_search", params, result)
_, err := c.caller.Call(ctx, "tx_search", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) Validators(height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
func (c *baseRPCClient) Validators(
ctx context.Context,
height *int64,
page,
perPage *int,
) (*ctypes.ResultValidators, error) {
result := new(ctypes.ResultValidators)
params := make(map[string]interface{})
if page != nil {
@ -463,16 +506,19 @@ func (c *baseRPCClient) Validators(height *int64, page, perPage *int) (*ctypes.R
if height != nil {
params["height"] = height
}
_, err := c.caller.Call("validators", params, result)
_, err := c.caller.Call(ctx, "validators", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
func (c *baseRPCClient) BroadcastEvidence(
ctx context.Context,
ev types.Evidence,
) (*ctypes.ResultBroadcastEvidence, error) {
result := new(ctypes.ResultBroadcastEvidence)
_, err := c.caller.Call("broadcast_evidence", map[string]interface{}{"evidence": ev}, result)
_, err := c.caller.Call(ctx, "broadcast_evidence", map[string]interface{}{"evidence": ev}, result)
if err != nil {
return nil, err
}


+ 26
- 25
rpc/client/interface.go View File

@ -50,48 +50,49 @@ type Client interface {
// is easier to mock.
type ABCIClient interface {
// Reading from abci app
ABCIInfo() (*ctypes.ResultABCIInfo, error)
ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error)
ABCIQueryWithOptions(path string, data bytes.HexBytes,
ABCIInfo(context.Context) (*ctypes.ResultABCIInfo, error)
ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error)
ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes,
opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error)
// Writing to abci app
BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error)
BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
BroadcastTxCommit(context.Context, types.Tx) (*ctypes.ResultBroadcastTxCommit, error)
BroadcastTxAsync(context.Context, types.Tx) (*ctypes.ResultBroadcastTx, error)
BroadcastTxSync(context.Context, types.Tx) (*ctypes.ResultBroadcastTx, error)
}
// SignClient groups together the functionality needed to get valid signatures
// and prove anything about the chain.
type SignClient interface {
Block(height *int64) (*ctypes.ResultBlock, error)
BlockByHash(hash []byte) (*ctypes.ResultBlock, error)
BlockResults(height *int64) (*ctypes.ResultBlockResults, error)
Commit(height *int64) (*ctypes.ResultCommit, error)
Validators(height *int64, page, perPage *int) (*ctypes.ResultValidators, error)
Tx(hash []byte, prove bool) (*ctypes.ResultTx, error)
TxSearch(query string, prove bool, page, perPage *int, orderBy string) (*ctypes.ResultTxSearch, error)
Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error)
BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error)
BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error)
Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error)
Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error)
Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error)
TxSearch(ctx context.Context, query string, prove bool, page, perPage *int,
orderBy string) (*ctypes.ResultTxSearch, error)
}
// HistoryClient provides access to data from genesis to now in large chunks.
type HistoryClient interface {
Genesis() (*ctypes.ResultGenesis, error)
BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error)
Genesis(context.Context) (*ctypes.ResultGenesis, error)
BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error)
}
// StatusClient provides access to general chain info.
type StatusClient interface {
Status() (*ctypes.ResultStatus, error)
Status(context.Context) (*ctypes.ResultStatus, error)
}
// NetworkClient is general info about the network state. May not be needed
// usually.
type NetworkClient interface {
NetInfo() (*ctypes.ResultNetInfo, error)
DumpConsensusState() (*ctypes.ResultDumpConsensusState, error)
ConsensusState() (*ctypes.ResultConsensusState, error)
ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error)
Health() (*ctypes.ResultHealth, error)
NetInfo(context.Context) (*ctypes.ResultNetInfo, error)
DumpConsensusState(context.Context) (*ctypes.ResultDumpConsensusState, error)
ConsensusState(context.Context) (*ctypes.ResultConsensusState, error)
ConsensusParams(ctx context.Context, height *int64) (*ctypes.ResultConsensusParams, error)
Health(context.Context) (*ctypes.ResultHealth, error)
}
// EventsClient is reactive, you can subscribe to any message, given the proper
@ -113,15 +114,15 @@ type EventsClient interface {
// MempoolClient shows us data about current mempool state.
type MempoolClient interface {
UnconfirmedTxs(limit *int) (*ctypes.ResultUnconfirmedTxs, error)
NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error)
CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error)
UnconfirmedTxs(ctx context.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error)
NumUnconfirmedTxs(context.Context) (*ctypes.ResultUnconfirmedTxs, error)
CheckTx(context.Context, types.Tx) (*ctypes.ResultCheckTx, error)
}
// EvidenceClient is used for submitting an evidence of the malicious
// behaviour.
type EvidenceClient interface {
BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error)
BroadcastEvidence(context.Context, types.Evidence) (*ctypes.ResultBroadcastEvidence, error)
}
// RemoteClient is a Client, which can also return the remote network address.


+ 41
- 28
rpc/client/local/local.go View File

@ -67,115 +67,128 @@ func (c *Local) SetLogger(l log.Logger) {
c.Logger = l
}
func (c *Local) Status() (*ctypes.ResultStatus, error) {
func (c *Local) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
return core.Status(c.ctx)
}
func (c *Local) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
func (c *Local) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
return core.ABCIInfo(c.ctx)
}
func (c *Local) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
func (c *Local) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions)
}
func (c *Local) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
return core.ABCIQuery(c.ctx, path, data, opts.Height, opts.Prove)
}
func (c *Local) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
func (c *Local) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
return core.BroadcastTxCommit(c.ctx, tx)
}
func (c *Local) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (c *Local) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return core.BroadcastTxAsync(c.ctx, tx)
}
func (c *Local) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (c *Local) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return core.BroadcastTxSync(c.ctx, tx)
}
func (c *Local) UnconfirmedTxs(limit *int) (*ctypes.ResultUnconfirmedTxs, error) {
func (c *Local) UnconfirmedTxs(ctx context.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error) {
return core.UnconfirmedTxs(c.ctx, limit)
}
func (c *Local) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
func (c *Local) NumUnconfirmedTxs(ctx context.Context) (*ctypes.ResultUnconfirmedTxs, error) {
return core.NumUnconfirmedTxs(c.ctx)
}
func (c *Local) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
func (c *Local) CheckTx(ctx context.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
return core.CheckTx(c.ctx, tx)
}
func (c *Local) NetInfo() (*ctypes.ResultNetInfo, error) {
func (c *Local) NetInfo(ctx context.Context) (*ctypes.ResultNetInfo, error) {
return core.NetInfo(c.ctx)
}
func (c *Local) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
func (c *Local) DumpConsensusState(ctx context.Context) (*ctypes.ResultDumpConsensusState, error) {
return core.DumpConsensusState(c.ctx)
}
func (c *Local) ConsensusState() (*ctypes.ResultConsensusState, error) {
func (c *Local) ConsensusState(ctx context.Context) (*ctypes.ResultConsensusState, error) {
return core.ConsensusState(c.ctx)
}
func (c *Local) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
func (c *Local) ConsensusParams(ctx context.Context, height *int64) (*ctypes.ResultConsensusParams, error) {
return core.ConsensusParams(c.ctx, height)
}
func (c *Local) Health() (*ctypes.ResultHealth, error) {
func (c *Local) Health(ctx context.Context) (*ctypes.ResultHealth, error) {
return core.Health(c.ctx)
}
func (c *Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
func (c *Local) DialSeeds(ctx context.Context, seeds []string) (*ctypes.ResultDialSeeds, error) {
return core.UnsafeDialSeeds(c.ctx, seeds)
}
func (c *Local) DialPeers(peers []string, persistent, unconditional, private bool) (*ctypes.ResultDialPeers, error) {
func (c *Local) DialPeers(
ctx context.Context,
peers []string,
persistent,
unconditional,
private bool,
) (*ctypes.ResultDialPeers, error) {
return core.UnsafeDialPeers(c.ctx, peers, persistent, unconditional, private)
}
func (c *Local) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
func (c *Local) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
return core.BlockchainInfo(c.ctx, minHeight, maxHeight)
}
func (c *Local) Genesis() (*ctypes.ResultGenesis, error) {
func (c *Local) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) {
return core.Genesis(c.ctx)
}
func (c *Local) Block(height *int64) (*ctypes.ResultBlock, error) {
func (c *Local) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) {
return core.Block(c.ctx, height)
}
func (c *Local) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
func (c *Local) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) {
return core.BlockByHash(c.ctx, hash)
}
func (c *Local) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
func (c *Local) BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error) {
return core.BlockResults(c.ctx, height)
}
func (c *Local) Commit(height *int64) (*ctypes.ResultCommit, error) {
func (c *Local) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
return core.Commit(c.ctx, height)
}
func (c *Local) Validators(height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
func (c *Local) Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
return core.Validators(c.ctx, height, page, perPage)
}
func (c *Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
func (c *Local) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
return core.Tx(c.ctx, hash, prove)
}
func (c *Local) TxSearch(query string, prove bool, page, perPage *int, orderBy string) (
*ctypes.ResultTxSearch, error) {
func (c *Local) TxSearch(
ctx context.Context,
query string,
prove bool,
page,
perPage *int,
orderBy string,
) (*ctypes.ResultTxSearch, error) {
return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy)
}
func (c *Local) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
return core.BroadcastEvidence(c.ctx, ev)
}


+ 32
- 23
rpc/client/mock/abci.go View File

@ -1,6 +1,8 @@
package mock
import (
"context"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/proxy"
@ -22,15 +24,16 @@ var (
_ client.ABCIClient = (*ABCIRecorder)(nil)
)
func (a ABCIApp) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
func (a ABCIApp) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
return &ctypes.ResultABCIInfo{Response: a.App.Info(proxy.RequestInfo)}, nil
}
func (a ABCIApp) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return a.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
func (a ABCIApp) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return a.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions)
}
func (a ABCIApp) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
@ -46,7 +49,7 @@ func (a ABCIApp) ABCIQueryWithOptions(
// NOTE: Caller should call a.App.Commit() separately,
// this function does not actually wait for a commit.
// TODO: Make it wait for a commit and set res.Height appropriately.
func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
func (a ABCIApp) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res := ctypes.ResultBroadcastTxCommit{}
res.CheckTx = a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
if res.CheckTx.IsErr() {
@ -57,7 +60,7 @@ func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit
return &res, nil
}
func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (a ABCIApp) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
// and this gets written in a background thread...
if !c.IsErr() {
@ -72,7 +75,7 @@ func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error
}, nil
}
func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (a ABCIApp) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
// and this gets written in a background thread...
if !c.IsErr() {
@ -97,7 +100,7 @@ type ABCIMock struct {
Broadcast Call
}
func (m ABCIMock) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
func (m ABCIMock) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
res, err := m.Info.GetResponse(nil)
if err != nil {
return nil, err
@ -105,11 +108,12 @@ func (m ABCIMock) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
return &ctypes.ResultABCIInfo{Response: res.(abci.ResponseInfo)}, nil
}
func (m ABCIMock) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return m.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
func (m ABCIMock) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return m.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions)
}
func (m ABCIMock) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
@ -121,7 +125,7 @@ func (m ABCIMock) ABCIQueryWithOptions(
return &ctypes.ResultABCIQuery{Response: resQuery}, nil
}
func (m ABCIMock) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
func (m ABCIMock) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res, err := m.BroadcastCommit.GetResponse(tx)
if err != nil {
return nil, err
@ -129,7 +133,7 @@ func (m ABCIMock) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommi
return res.(*ctypes.ResultBroadcastTxCommit), nil
}
func (m ABCIMock) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (m ABCIMock) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := m.Broadcast.GetResponse(tx)
if err != nil {
return nil, err
@ -137,7 +141,7 @@ func (m ABCIMock) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, erro
return res.(*ctypes.ResultBroadcastTx), nil
}
func (m ABCIMock) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (m ABCIMock) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := m.Broadcast.GetResponse(tx)
if err != nil {
return nil, err
@ -170,8 +174,8 @@ func (r *ABCIRecorder) addCall(call Call) {
r.Calls = append(r.Calls, call)
}
func (r *ABCIRecorder) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
res, err := r.Client.ABCIInfo()
func (r *ABCIRecorder) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
res, err := r.Client.ABCIInfo(ctx)
r.addCall(Call{
Name: "abci_info",
Response: res,
@ -180,15 +184,20 @@ func (r *ABCIRecorder) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
return res, err
}
func (r *ABCIRecorder) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return r.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
func (r *ABCIRecorder) ABCIQuery(
ctx context.Context,
path string,
data bytes.HexBytes,
) (*ctypes.ResultABCIQuery, error) {
return r.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions)
}
func (r *ABCIRecorder) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
res, err := r.Client.ABCIQueryWithOptions(path, data, opts)
res, err := r.Client.ABCIQueryWithOptions(ctx, path, data, opts)
r.addCall(Call{
Name: "abci_query",
Args: QueryArgs{path, data, opts.Height, opts.Prove},
@ -198,8 +207,8 @@ func (r *ABCIRecorder) ABCIQueryWithOptions(
return res, err
}
func (r *ABCIRecorder) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res, err := r.Client.BroadcastTxCommit(tx)
func (r *ABCIRecorder) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res, err := r.Client.BroadcastTxCommit(ctx, tx)
r.addCall(Call{
Name: "broadcast_tx_commit",
Args: tx,
@ -209,8 +218,8 @@ func (r *ABCIRecorder) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTx
return res, err
}
func (r *ABCIRecorder) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := r.Client.BroadcastTxAsync(tx)
func (r *ABCIRecorder) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := r.Client.BroadcastTxAsync(ctx, tx)
r.addCall(Call{
Name: "broadcast_tx_async",
Args: tx,
@ -220,8 +229,8 @@ func (r *ABCIRecorder) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx,
return res, err
}
func (r *ABCIRecorder) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := r.Client.BroadcastTxSync(tx)
func (r *ABCIRecorder) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := r.Client.BroadcastTxSync(ctx, tx)
r.addCall(Call{
Name: "broadcast_tx_sync",
Args: tx,


+ 25
- 14
rpc/client/mock/abci_test.go View File

@ -1,6 +1,7 @@
package mock_test
import (
"context"
"errors"
"fmt"
"testing"
@ -45,12 +46,12 @@ func TestABCIMock(t *testing.T) {
}
// now, let's try to make some calls
_, err := m.ABCIInfo()
_, err := m.ABCIInfo(context.Background())
require.NotNil(err)
assert.Equal("foobar", err.Error())
// query always returns the response
_query, err := m.ABCIQueryWithOptions("/", nil, client.ABCIQueryOptions{Prove: false})
_query, err := m.ABCIQueryWithOptions(context.Background(), "/", nil, client.ABCIQueryOptions{Prove: false})
query := _query.Response
require.Nil(err)
require.NotNil(query)
@ -59,18 +60,18 @@ func TestABCIMock(t *testing.T) {
assert.Equal(height, query.Height)
// non-commit calls always return errors
_, err = m.BroadcastTxSync(goodTx)
_, err = m.BroadcastTxSync(context.Background(), goodTx)
require.NotNil(err)
assert.Equal("must commit", err.Error())
_, err = m.BroadcastTxAsync(goodTx)
_, err = m.BroadcastTxAsync(context.Background(), goodTx)
require.NotNil(err)
assert.Equal("must commit", err.Error())
// commit depends on the input
_, err = m.BroadcastTxCommit(badTx)
_, err = m.BroadcastTxCommit(context.Background(), badTx)
require.NotNil(err)
assert.Equal("bad tx", err.Error())
bres, err := m.BroadcastTxCommit(goodTx)
bres, err := m.BroadcastTxCommit(context.Background(), goodTx)
require.Nil(err, "%+v", err)
assert.EqualValues(0, bres.CheckTx.Code)
assert.EqualValues("stand", bres.CheckTx.Data)
@ -94,10 +95,15 @@ func TestABCIRecorder(t *testing.T) {
require.Equal(0, len(r.Calls))
_, err := r.ABCIInfo()
_, err := r.ABCIInfo(context.Background())
assert.Nil(err, "expected no err on info")
_, err = r.ABCIQueryWithOptions("path", bytes.HexBytes("data"), client.ABCIQueryOptions{Prove: false})
_, err = r.ABCIQueryWithOptions(
context.Background(),
"path",
bytes.HexBytes("data"),
client.ABCIQueryOptions{Prove: false},
)
assert.NotNil(err, "expected error on query")
require.Equal(2, len(r.Calls))
@ -125,11 +131,11 @@ func TestABCIRecorder(t *testing.T) {
// now add some broadcasts (should all err)
txs := []types.Tx{{1}, {2}, {3}}
_, err = r.BroadcastTxCommit(txs[0])
_, err = r.BroadcastTxCommit(context.Background(), txs[0])
assert.NotNil(err, "expected err on broadcast")
_, err = r.BroadcastTxSync(txs[1])
_, err = r.BroadcastTxSync(context.Background(), txs[1])
assert.NotNil(err, "expected err on broadcast")
_, err = r.BroadcastTxAsync(txs[2])
_, err = r.BroadcastTxAsync(context.Background(), txs[2])
assert.NotNil(err, "expected err on broadcast")
require.Equal(5, len(r.Calls))
@ -159,14 +165,14 @@ func TestABCIApp(t *testing.T) {
m := mock.ABCIApp{app}
// get some info
info, err := m.ABCIInfo()
info, err := m.ABCIInfo(context.Background())
require.Nil(err)
assert.Equal(`{"size":0}`, info.Response.GetData())
// add a key
key, value := "foo", "bar"
tx := fmt.Sprintf("%s=%s", key, value)
res, err := m.BroadcastTxCommit(types.Tx(tx))
res, err := m.BroadcastTxCommit(context.Background(), types.Tx(tx))
require.Nil(err)
assert.True(res.CheckTx.IsOK())
require.NotNil(res.DeliverTx)
@ -179,7 +185,12 @@ func TestABCIApp(t *testing.T) {
}
// check the key
_qres, err := m.ABCIQueryWithOptions("/key", bytes.HexBytes(key), client.ABCIQueryOptions{Prove: true})
_qres, err := m.ABCIQueryWithOptions(
context.Background(),
"/key",
bytes.HexBytes(key),
client.ABCIQueryOptions{Prove: true},
)
qres := _qres.Response
require.Nil(err)
assert.EqualValues(value, qres.Value)


+ 30
- 22
rpc/client/mock/client.go View File

@ -15,6 +15,7 @@ want to directly call a tendermint node in process, you can use the
*/
import (
"context"
"reflect"
"github.com/tendermint/tendermint/libs/bytes"
@ -79,93 +80,100 @@ func (c Call) GetResponse(args interface{}) (interface{}, error) {
return nil, c.Error
}
func (c Client) Status() (*ctypes.ResultStatus, error) {
func (c Client) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
return core.Status(&rpctypes.Context{})
}
func (c Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
func (c Client) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
return core.ABCIInfo(&rpctypes.Context{})
}
func (c Client) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(path, data, client.DefaultABCIQueryOptions)
func (c Client) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return c.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions)
}
func (c Client) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
return core.ABCIQuery(&rpctypes.Context{}, path, data, opts.Height, opts.Prove)
}
func (c Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
func (c Client) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
return core.BroadcastTxCommit(&rpctypes.Context{}, tx)
}
func (c Client) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (c Client) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return core.BroadcastTxAsync(&rpctypes.Context{}, tx)
}
func (c Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
func (c Client) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
return core.BroadcastTxSync(&rpctypes.Context{}, tx)
}
func (c Client) CheckTx(tx types.Tx) (*ctypes.ResultCheckTx, error) {
func (c Client) CheckTx(ctx context.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
return core.CheckTx(&rpctypes.Context{}, tx)
}
func (c Client) NetInfo() (*ctypes.ResultNetInfo, error) {
func (c Client) NetInfo(ctx context.Context) (*ctypes.ResultNetInfo, error) {
return core.NetInfo(&rpctypes.Context{})
}
func (c Client) ConsensusState() (*ctypes.ResultConsensusState, error) {
func (c Client) ConsensusState(ctx context.Context) (*ctypes.ResultConsensusState, error) {
return core.ConsensusState(&rpctypes.Context{})
}
func (c Client) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
func (c Client) DumpConsensusState(ctx context.Context) (*ctypes.ResultDumpConsensusState, error) {
return core.DumpConsensusState(&rpctypes.Context{})
}
func (c Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
func (c Client) ConsensusParams(ctx context.Context, height *int64) (*ctypes.ResultConsensusParams, error) {
return core.ConsensusParams(&rpctypes.Context{}, height)
}
func (c Client) Health() (*ctypes.ResultHealth, error) {
func (c Client) Health(ctx context.Context) (*ctypes.ResultHealth, error) {
return core.Health(&rpctypes.Context{})
}
func (c Client) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
func (c Client) DialSeeds(ctx context.Context, seeds []string) (*ctypes.ResultDialSeeds, error) {
return core.UnsafeDialSeeds(&rpctypes.Context{}, seeds)
}
func (c Client) DialPeers(peers []string, persistent, unconditional, private bool) (*ctypes.ResultDialPeers, error) {
func (c Client) DialPeers(
ctx context.Context,
peers []string,
persistent,
unconditional,
private bool,
) (*ctypes.ResultDialPeers, error) {
return core.UnsafeDialPeers(&rpctypes.Context{}, peers, persistent, unconditional, private)
}
func (c Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
func (c Client) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
return core.BlockchainInfo(&rpctypes.Context{}, minHeight, maxHeight)
}
func (c Client) Genesis() (*ctypes.ResultGenesis, error) {
func (c Client) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) {
return core.Genesis(&rpctypes.Context{})
}
func (c Client) Block(height *int64) (*ctypes.ResultBlock, error) {
func (c Client) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) {
return core.Block(&rpctypes.Context{}, height)
}
func (c Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
func (c Client) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) {
return core.BlockByHash(&rpctypes.Context{}, hash)
}
func (c Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
func (c Client) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
return core.Commit(&rpctypes.Context{}, height)
}
func (c Client) Validators(height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
func (c Client) Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) {
return core.Validators(&rpctypes.Context{}, height, page, perPage)
}
func (c Client) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
func (c Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
return core.BroadcastEvidence(&rpctypes.Context{}, ev)
}

+ 5
- 3
rpc/client/mock/status.go View File

@ -1,6 +1,8 @@
package mock
import (
"context"
"github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
@ -15,7 +17,7 @@ var (
_ client.StatusClient = (*StatusRecorder)(nil)
)
func (m *StatusMock) Status() (*ctypes.ResultStatus, error) {
func (m *StatusMock) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
res, err := m.GetResponse(nil)
if err != nil {
return nil, err
@ -41,8 +43,8 @@ func (r *StatusRecorder) addCall(call Call) {
r.Calls = append(r.Calls, call)
}
func (r *StatusRecorder) Status() (*ctypes.ResultStatus, error) {
res, err := r.Client.Status()
func (r *StatusRecorder) Status(ctx context.Context) (*ctypes.ResultStatus, error) {
res, err := r.Client.Status(ctx)
r.addCall(Call{
Name: "status",
Response: res,


+ 2
- 1
rpc/client/mock/status_test.go View File

@ -1,6 +1,7 @@
package mock_test
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
@ -29,7 +30,7 @@ func TestStatus(t *testing.T) {
require.Equal(0, len(r.Calls))
// make sure response works proper
status, err := r.Status()
status, err := r.Status(context.Background())
require.Nil(err, "%+v", err)
assert.EqualValues("block", status.SyncInfo.LatestBlockHash)
assert.EqualValues(10, status.SyncInfo.LatestBlockHeight)


+ 60
- 54
rpc/client/rpc_test.go View File

@ -1,6 +1,7 @@
package client_test
import (
"context"
"fmt"
"math"
"net/http"
@ -25,6 +26,10 @@ import (
"github.com/tendermint/tendermint/types"
)
var (
ctx = context.Background()
)
func getHTTPClient() *rpchttp.HTTP {
rpcAddr := rpctest.GetConfig().RPC.ListenAddress
c, err := rpchttp.New(rpcAddr, "/websocket")
@ -70,7 +75,7 @@ func TestCustomHTTPClient(t *testing.T) {
remote := rpctest.GetConfig().RPC.ListenAddress
c, err := rpchttp.NewWithClient(remote, "/websocket", http.DefaultClient)
require.Nil(t, err)
status, err := c.Status()
status, err := c.Status(context.Background())
require.NoError(t, err)
require.NotNil(t, status)
}
@ -94,7 +99,7 @@ func TestCorsEnabled(t *testing.T) {
func TestStatus(t *testing.T) {
for i, c := range GetClients() {
moniker := rpctest.GetConfig().Moniker
status, err := c.Status()
status, err := c.Status(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
assert.Equal(t, moniker, status.NodeInfo.Moniker)
}
@ -105,7 +110,7 @@ func TestInfo(t *testing.T) {
for i, c := range GetClients() {
// status, err := c.Status()
// require.Nil(t, err, "%+v", err)
info, err := c.ABCIInfo()
info, err := c.ABCIInfo(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
// TODO: this is not correct - fix merkleeyes!
// assert.EqualValues(t, status.SyncInfo.LatestBlockHeight, info.Response.LastBlockHeight)
@ -117,7 +122,7 @@ func TestNetInfo(t *testing.T) {
for i, c := range GetClients() {
nc, ok := c.(client.NetworkClient)
require.True(t, ok, "%d", i)
netinfo, err := nc.NetInfo()
netinfo, err := nc.NetInfo(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
assert.True(t, netinfo.Listening)
assert.Equal(t, 0, len(netinfo.Peers))
@ -129,7 +134,7 @@ func TestDumpConsensusState(t *testing.T) {
// FIXME: fix server so it doesn't panic on invalid input
nc, ok := c.(client.NetworkClient)
require.True(t, ok, "%d", i)
cons, err := nc.DumpConsensusState()
cons, err := nc.DumpConsensusState(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
assert.NotEmpty(t, cons.RoundState)
assert.Empty(t, cons.Peers)
@ -141,7 +146,7 @@ func TestConsensusState(t *testing.T) {
// FIXME: fix server so it doesn't panic on invalid input
nc, ok := c.(client.NetworkClient)
require.True(t, ok, "%d", i)
cons, err := nc.ConsensusState()
cons, err := nc.ConsensusState(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
assert.NotEmpty(t, cons.RoundState)
}
@ -151,7 +156,7 @@ func TestHealth(t *testing.T) {
for i, c := range GetClients() {
nc, ok := c.(client.NetworkClient)
require.True(t, ok, "%d", i)
_, err := nc.Health()
_, err := nc.Health(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
}
}
@ -160,7 +165,7 @@ func TestGenesisAndValidators(t *testing.T) {
for i, c := range GetClients() {
// make sure this is the right genesis file
gen, err := c.Genesis()
gen, err := c.Genesis(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
// get the genesis validator
require.Equal(t, 1, len(gen.Genesis.Validators))
@ -168,7 +173,7 @@ func TestGenesisAndValidators(t *testing.T) {
// get the current validators
h := int64(1)
vals, err := c.Validators(&h, nil, nil)
vals, err := c.Validators(context.Background(), &h, nil, nil)
require.Nil(t, err, "%d: %+v", i, err)
require.Equal(t, 1, len(vals.Validators))
require.Equal(t, 1, vals.Count)
@ -185,14 +190,14 @@ func TestABCIQuery(t *testing.T) {
for i, c := range GetClients() {
// write something
k, v, tx := MakeTxKV()
bres, err := c.BroadcastTxCommit(tx)
bres, err := c.BroadcastTxCommit(context.Background(), tx)
require.Nil(t, err, "%d: %+v", i, err)
apph := bres.Height + 1 // this is where the tx will be applied to the state
// wait before querying
err = client.WaitForHeight(c, apph, nil)
require.NoError(t, err)
res, err := c.ABCIQuery("/key", k)
res, err := c.ABCIQuery(context.Background(), "/key", k)
qres := res.Response
if assert.Nil(t, err) && assert.True(t, qres.IsOK()) {
assert.EqualValues(t, v, qres.Value)
@ -206,19 +211,19 @@ func TestAppCalls(t *testing.T) {
for i, c := range GetClients() {
// get an offset of height to avoid racing and guessing
s, err := c.Status()
s, err := c.Status(context.Background())
require.NoError(err)
// sh is start height or status height
sh := s.SyncInfo.LatestBlockHeight
// look for the future
h := sh + 20
_, err = c.Block(&h)
_, err = c.Block(context.Background(), &h)
require.Error(err) // no block yet
// write something
k, v, tx := MakeTxKV()
bres, err := c.BroadcastTxCommit(tx)
bres, err := c.BroadcastTxCommit(context.Background(), tx)
require.NoError(err)
require.True(bres.DeliverTx.IsOK())
txh := bres.Height
@ -228,7 +233,7 @@ func TestAppCalls(t *testing.T) {
err = client.WaitForHeight(c, apph, nil)
require.NoError(err)
_qres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: false})
_qres, err := c.ABCIQueryWithOptions(context.Background(), "/key", k, client.ABCIQueryOptions{Prove: false})
require.NoError(err)
qres := _qres.Response
if assert.True(qres.IsOK()) {
@ -237,24 +242,24 @@ func TestAppCalls(t *testing.T) {
}
// make sure we can lookup the tx with proof
ptx, err := c.Tx(bres.Hash, true)
ptx, err := c.Tx(context.Background(), bres.Hash, true)
require.NoError(err)
assert.EqualValues(txh, ptx.Height)
assert.EqualValues(tx, ptx.Tx)
// and we can even check the block is added
block, err := c.Block(&apph)
block, err := c.Block(context.Background(), &apph)
require.NoError(err)
appHash := block.Block.Header.AppHash
assert.True(len(appHash) > 0)
assert.EqualValues(apph, block.Block.Header.Height)
blockByHash, err := c.BlockByHash(block.BlockID.Hash)
blockByHash, err := c.BlockByHash(context.Background(), block.BlockID.Hash)
require.NoError(err)
require.Equal(block, blockByHash)
// now check the results
blockResults, err := c.BlockResults(&txh)
blockResults, err := c.BlockResults(context.Background(), &txh)
require.Nil(err, "%d: %+v", i, err)
assert.Equal(txh, blockResults.Height)
if assert.Equal(1, len(blockResults.TxsResults)) {
@ -263,7 +268,7 @@ func TestAppCalls(t *testing.T) {
}
// check blockchain info, now that we know there is info
info, err := c.BlockchainInfo(apph, apph)
info, err := c.BlockchainInfo(context.Background(), apph, apph)
require.NoError(err)
assert.True(info.LastHeight >= apph)
if assert.Equal(1, len(info.BlockMetas)) {
@ -275,7 +280,7 @@ func TestAppCalls(t *testing.T) {
}
// and get the corresponding commit with the same apphash
commit, err := c.Commit(&apph)
commit, err := c.Commit(context.Background(), &apph)
require.NoError(err)
cappHash := commit.Header.AppHash
assert.Equal(appHash, cappHash)
@ -283,12 +288,12 @@ func TestAppCalls(t *testing.T) {
// compare the commits (note Commit(2) has commit from Block(3))
h = apph - 1
commit2, err := c.Commit(&h)
commit2, err := c.Commit(context.Background(), &h)
require.NoError(err)
assert.Equal(block.Block.LastCommit, commit2.Commit)
// and we got a proof that works!
_pres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: true})
_pres, err := c.ABCIQueryWithOptions(context.Background(), "/key", k, client.ABCIQueryOptions{Prove: true})
require.NoError(err)
pres := _pres.Response
assert.True(pres.IsOK())
@ -306,7 +311,7 @@ func TestBroadcastTxSync(t *testing.T) {
for i, c := range GetClients() {
_, _, tx := MakeTxKV()
bres, err := c.BroadcastTxSync(tx)
bres, err := c.BroadcastTxSync(context.Background(), tx)
require.Nil(err, "%d: %+v", i, err)
require.Equal(bres.Code, abci.CodeTypeOK) // FIXME
@ -324,7 +329,7 @@ func TestBroadcastTxCommit(t *testing.T) {
mempool := node.Mempool()
for i, c := range GetClients() {
_, _, tx := MakeTxKV()
bres, err := c.BroadcastTxCommit(tx)
bres, err := c.BroadcastTxCommit(context.Background(), tx)
require.Nil(err, "%d: %+v", i, err)
require.True(bres.CheckTx.IsOK())
require.True(bres.DeliverTx.IsOK())
@ -351,7 +356,7 @@ func TestUnconfirmedTxs(t *testing.T) {
for _, c := range GetClients() {
mc := c.(client.MempoolClient)
limit := 1
res, err := mc.UnconfirmedTxs(&limit)
res, err := mc.UnconfirmedTxs(context.Background(), &limit)
require.NoError(t, err)
assert.Equal(t, 1, res.Count)
@ -382,7 +387,7 @@ func TestNumUnconfirmedTxs(t *testing.T) {
for i, c := range GetClients() {
mc, ok := c.(client.MempoolClient)
require.True(t, ok, "%d", i)
res, err := mc.NumUnconfirmedTxs()
res, err := mc.NumUnconfirmedTxs(context.Background())
require.Nil(t, err, "%d: %+v", i, err)
assert.Equal(t, mempoolSize, res.Count)
@ -399,7 +404,7 @@ func TestCheckTx(t *testing.T) {
for _, c := range GetClients() {
_, _, tx := MakeTxKV()
res, err := c.CheckTx(tx)
res, err := c.CheckTx(context.Background(), tx)
require.NoError(t, err)
assert.Equal(t, abci.CodeTypeOK, res.Code)
@ -411,7 +416,7 @@ func TestTx(t *testing.T) {
// first we broadcast a tx
c := getHTTPClient()
_, _, tx := MakeTxKV()
bres, err := c.BroadcastTxCommit(tx)
bres, err := c.BroadcastTxCommit(context.Background(), tx)
require.Nil(t, err, "%+v", err)
txHeight := bres.Height
@ -439,7 +444,7 @@ func TestTx(t *testing.T) {
// now we query for the tx.
// since there's only one tx, we know index=0.
ptx, err := c.Tx(tc.hash, tc.prove)
ptx, err := c.Tx(context.Background(), tc.hash, tc.prove)
if !tc.valid {
require.NotNil(t, err)
@ -466,11 +471,11 @@ func TestTxSearchWithTimeout(t *testing.T) {
timeoutClient := getHTTPClientWithTimeout(10)
_, _, tx := MakeTxKV()
_, err := timeoutClient.BroadcastTxCommit(tx)
_, err := timeoutClient.BroadcastTxCommit(context.Background(), tx)
require.NoError(t, err)
// query using a compositeKey (see kvstore application)
result, err := timeoutClient.TxSearch("app.creator='Cosmoshi Netowoko'", false, nil, nil, "asc")
result, err := timeoutClient.TxSearch(context.Background(), "app.creator='Cosmoshi Netowoko'", false, nil, nil, "asc")
require.Nil(t, err)
require.Greater(t, len(result.Txs), 0, "expected a lot of transactions")
}
@ -481,13 +486,13 @@ func TestTxSearch(t *testing.T) {
// first we broadcast a few txs
for i := 0; i < 10; i++ {
_, _, tx := MakeTxKV()
_, err := c.BroadcastTxCommit(tx)
_, err := c.BroadcastTxCommit(context.Background(), tx)
require.NoError(t, err)
}
// since we're not using an isolated test server, we'll have lingering transactions
// from other tests as well
result, err := c.TxSearch("tx.height >= 0", true, nil, nil, "asc")
result, err := c.TxSearch(context.Background(), "tx.height >= 0", true, nil, nil, "asc")
require.NoError(t, err)
txCount := len(result.Txs)
@ -499,7 +504,7 @@ func TestTxSearch(t *testing.T) {
t.Logf("client %d", i)
// now we query for the tx.
result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", find.Hash), true, nil, nil, "asc")
result, err := c.TxSearch(context.Background(), fmt.Sprintf("tx.hash='%v'", find.Hash), true, nil, nil, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 1)
require.Equal(t, find.Hash, result.Txs[0].Hash)
@ -517,50 +522,51 @@ func TestTxSearch(t *testing.T) {
}
// query by height
result, err = c.TxSearch(fmt.Sprintf("tx.height=%d", find.Height), true, nil, nil, "asc")
result, err = c.TxSearch(context.Background(), fmt.Sprintf("tx.height=%d", find.Height), true, nil, nil, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 1)
// query for non existing tx
result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, nil, nil, "asc")
result, err = c.TxSearch(context.Background(), fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, nil, nil, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 0)
// query using a compositeKey (see kvstore application)
result, err = c.TxSearch("app.creator='Cosmoshi Netowoko'", false, nil, nil, "asc")
result, err = c.TxSearch(context.Background(), "app.creator='Cosmoshi Netowoko'", false, nil, nil, "asc")
require.Nil(t, err)
require.Greater(t, len(result.Txs), 0, "expected a lot of transactions")
// query using an index key
result, err = c.TxSearch("app.index_key='index is working'", false, nil, nil, "asc")
result, err = c.TxSearch(context.Background(), "app.index_key='index is working'", false, nil, nil, "asc")
require.Nil(t, err)
require.Greater(t, len(result.Txs), 0, "expected a lot of transactions")
// query using an noindex key
result, err = c.TxSearch("app.noindex_key='index is working'", false, nil, nil, "asc")
result, err = c.TxSearch(context.Background(), "app.noindex_key='index is working'", false, nil, nil, "asc")
require.Nil(t, err)
require.Equal(t, len(result.Txs), 0, "expected a lot of transactions")
// query using a compositeKey (see kvstore application) and height
result, err = c.TxSearch("app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, nil, nil, "asc")
result, err = c.TxSearch(context.Background(),
"app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, nil, nil, "asc")
require.Nil(t, err)
require.Greater(t, len(result.Txs), 0, "expected a lot of transactions")
// query a non existing tx with page 1 and txsPerPage 1
perPage := 1
result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, nil, &perPage, "asc")
result, err = c.TxSearch(context.Background(), "app.creator='Cosmoshi Neetowoko'", true, nil, &perPage, "asc")
require.Nil(t, err)
require.Len(t, result.Txs, 0)
// check sorting
result, err = c.TxSearch("tx.height >= 1", false, nil, nil, "asc")
result, err = c.TxSearch(context.Background(), "tx.height >= 1", false, nil, nil, "asc")
require.Nil(t, err)
for k := 0; k < len(result.Txs)-1; k++ {
require.LessOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height)
require.LessOrEqual(t, result.Txs[k].Index, result.Txs[k+1].Index)
}
result, err = c.TxSearch("tx.height >= 1", false, nil, nil, "desc")
result, err = c.TxSearch(context.Background(), "tx.height >= 1", false, nil, nil, "desc")
require.Nil(t, err)
for k := 0; k < len(result.Txs)-1; k++ {
require.GreaterOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height)
@ -576,7 +582,7 @@ func TestTxSearch(t *testing.T) {
for page := 1; page <= pages; page++ {
page := page
result, err := c.TxSearch("tx.height >= 1", false, &page, &perPage, "asc")
result, err := c.TxSearch(context.Background(), "tx.height >= 1", false, &page, &perPage, "asc")
require.NoError(t, err)
if page < pages {
require.Len(t, result.Txs, perPage)
@ -607,12 +613,12 @@ func testBatchedJSONRPCCalls(t *testing.T, c *rpchttp.HTTP) {
k2, v2, tx2 := MakeTxKV()
batch := c.NewBatch()
r1, err := batch.BroadcastTxCommit(tx1)
r1, err := batch.BroadcastTxCommit(context.Background(), tx1)
require.NoError(t, err)
r2, err := batch.BroadcastTxCommit(tx2)
r2, err := batch.BroadcastTxCommit(context.Background(), tx2)
require.NoError(t, err)
require.Equal(t, 2, batch.Count())
bresults, err := batch.Send()
bresults, err := batch.Send(ctx)
require.NoError(t, err)
require.Len(t, bresults, 2)
require.Equal(t, 0, batch.Count())
@ -628,12 +634,12 @@ func testBatchedJSONRPCCalls(t *testing.T, c *rpchttp.HTTP) {
err = client.WaitForHeight(c, apph, nil)
require.NoError(t, err)
q1, err := batch.ABCIQuery("/key", k1)
q1, err := batch.ABCIQuery(context.Background(), "/key", k1)
require.NoError(t, err)
q2, err := batch.ABCIQuery("/key", k2)
q2, err := batch.ABCIQuery(context.Background(), "/key", k2)
require.NoError(t, err)
require.Equal(t, 2, batch.Count())
qresults, err := batch.Send()
qresults, err := batch.Send(ctx)
require.NoError(t, err)
require.Len(t, qresults, 2)
require.Equal(t, 0, batch.Count())
@ -657,9 +663,9 @@ func TestBatchedJSONRPCCallsCancellation(t *testing.T) {
_, _, tx2 := MakeTxKV()
batch := c.NewBatch()
_, err := batch.BroadcastTxCommit(tx1)
_, err := batch.BroadcastTxCommit(context.Background(), tx1)
require.NoError(t, err)
_, err = batch.BroadcastTxCommit(tx2)
_, err = batch.BroadcastTxCommit(context.Background(), tx2)
require.NoError(t, err)
// we should have 2 requests waiting
require.Equal(t, 2, batch.Count())
@ -672,7 +678,7 @@ func TestBatchedJSONRPCCallsCancellation(t *testing.T) {
func TestSendingEmptyRequestBatch(t *testing.T) {
c := getHTTPClient()
batch := c.NewBatch()
_, err := batch.Send()
_, err := batch.Send(ctx)
require.Error(t, err, "sending an empty batch of JSON RPC requests should result in an error")
}


+ 16
- 12
rpc/jsonrpc/client/http_json_client.go View File

@ -2,6 +2,7 @@ package client
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
@ -78,12 +79,12 @@ func (u parsedURL) GetTrimmedURL() string {
// HTTPClient is a common interface for JSON-RPC HTTP clients.
type HTTPClient interface {
// Call calls the given method with the params and returns a result.
Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
Call(ctx context.Context, method string, params map[string]interface{}, result interface{}) (interface{}, error)
}
// Caller implementers can facilitate calling the JSON-RPC endpoint.
type Caller interface {
Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
Call(ctx context.Context, method string, params map[string]interface{}, result interface{}) (interface{}, error)
}
//-------------------------------------------------------------
@ -151,7 +152,9 @@ func NewWithHTTPClient(remote string, client *http.Client) (*Client, error) {
// Call issues a POST HTTP request. Requests are JSON encoded. Content-Type:
// text/json.
func (c *Client) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
func (c *Client) Call(ctx context.Context, method string,
params map[string]interface{}, result interface{}) (interface{}, error) {
id := c.nextRequestID()
request, err := types.MapToRequest(id, method, params)
@ -165,7 +168,7 @@ func (c *Client) Call(method string, params map[string]interface{}, result inter
}
requestBuf := bytes.NewBuffer(requestBytes)
httpRequest, err := http.NewRequest(http.MethodPost, c.address, requestBuf)
httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, c.address, requestBuf)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
@ -195,7 +198,7 @@ func (c *Client) NewRequestBatch() *RequestBatch {
}
}
func (c *Client) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, error) {
func (c *Client) sendBatch(ctx context.Context, requests []*jsonRPCBufferedRequest) ([]interface{}, error) {
reqs := make([]types.RPCRequest, 0, len(requests))
results := make([]interface{}, 0, len(requests))
for _, req := range requests {
@ -206,12 +209,12 @@ func (c *Client) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, e
// serialize the array of requests into a single JSON object
requestBytes, err := json.Marshal(reqs)
if err != nil {
return nil, fmt.Errorf("failed to marshal requests: %w", err)
return nil, fmt.Errorf("json marshal: %w", err)
}
httpRequest, err := http.NewRequest(http.MethodPost, c.address, bytes.NewBuffer(requestBytes))
httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, c.address, bytes.NewBuffer(requestBytes))
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
return nil, fmt.Errorf("new request: %w", err)
}
httpRequest.Header.Set("Content-Type", "text/json")
if c.username != "" || c.password != "" {
@ -219,13 +222,13 @@ func (c *Client) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, e
}
httpResponse, err := c.client.Do(httpRequest)
if err != nil {
return nil, fmt.Errorf("post failed: %w", err)
return nil, fmt.Errorf("post: %w", err)
}
defer httpResponse.Body.Close()
responseBytes, err := ioutil.ReadAll(httpResponse.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
return nil, fmt.Errorf("read response body: %w", err)
}
// collect ids to check responses IDs in unmarshalResponseBytesArray
@ -293,18 +296,19 @@ func (b *RequestBatch) clear() int {
// Send will attempt to send the current batch of enqueued requests, and then
// will clear out the requests once done. On success, this returns the
// deserialized list of results from each of the enqueued requests.
func (b *RequestBatch) Send() ([]interface{}, error) {
func (b *RequestBatch) Send(ctx context.Context) ([]interface{}, error) {
b.mtx.Lock()
defer func() {
b.clear()
b.mtx.Unlock()
}()
return b.client.sendBatch(b.requests)
return b.client.sendBatch(ctx, b.requests)
}
// Call enqueues a request to call the given RPC method with the specified
// parameters, in the same way that the `Client.Call` function would.
func (b *RequestBatch) Call(
_ context.Context,
method string,
params map[string]interface{},
result interface{},


+ 19
- 4
rpc/jsonrpc/client/http_uri_client.go View File

@ -1,9 +1,11 @@
package client
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"strings"
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
)
@ -49,21 +51,34 @@ func NewURI(remote string) (*URIClient, error) {
}
// Call issues a POST form HTTP request.
func (c *URIClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
func (c *URIClient) Call(ctx context.Context, method string,
params map[string]interface{}, result interface{}) (interface{}, error) {
values, err := argsToURLValues(params)
if err != nil {
return nil, fmt.Errorf("failed to encode params: %w", err)
}
resp, err := c.client.PostForm(c.address+"/"+method, values)
req, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.address+"/"+method,
strings.NewReader(values.Encode()),
)
if err != nil {
return nil, fmt.Errorf("new request: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("post form failed: %w", err)
return nil, fmt.Errorf("post: %w", err)
}
defer resp.Body.Close()
responseBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
return nil, fmt.Errorf("read response body: %w", err)
}
return unmarshalResponseBytes(responseBytes, URIClientRequestID, result)


+ 8
- 4
rpc/jsonrpc/jsonrpc_test.go View File

@ -37,6 +37,10 @@ const (
testVal = "acbd"
)
var (
ctx = context.Background()
)
type ResultEcho struct {
Value string `json:"value"`
}
@ -156,7 +160,7 @@ func echoViaHTTP(cl client.Caller, val string) (string, error) {
"arg": val,
}
result := new(ResultEcho)
if _, err := cl.Call("echo", params, result); err != nil {
if _, err := cl.Call(ctx, "echo", params, result); err != nil {
return "", err
}
return result.Value, nil
@ -167,7 +171,7 @@ func echoIntViaHTTP(cl client.Caller, val int) (int, error) {
"arg": val,
}
result := new(ResultEchoInt)
if _, err := cl.Call("echo_int", params, result); err != nil {
if _, err := cl.Call(ctx, "echo_int", params, result); err != nil {
return 0, err
}
return result.Value, nil
@ -178,7 +182,7 @@ func echoBytesViaHTTP(cl client.Caller, bytes []byte) ([]byte, error) {
"arg": bytes,
}
result := new(ResultEchoBytes)
if _, err := cl.Call("echo_bytes", params, result); err != nil {
if _, err := cl.Call(ctx, "echo_bytes", params, result); err != nil {
return []byte{}, err
}
return result.Value, nil
@ -189,7 +193,7 @@ func echoDataBytesViaHTTP(cl client.Caller, bytes tmbytes.HexBytes) (tmbytes.Hex
"arg": bytes,
}
result := new(ResultEchoDataBytes)
if _, err := cl.Call("echo_data_bytes", params, result); err != nil {
if _, err := cl.Call(ctx, "echo_data_bytes", params, result); err != nil {
return []byte{}, err
}
return result.Value, nil


+ 1
- 1
rpc/test/helpers.go View File

@ -43,7 +43,7 @@ func waitForRPC() {
}
result := new(ctypes.ResultStatus)
for {
_, err := client.Call("status", map[string]interface{}{}, result)
_, err := client.Call(context.Background(), "status", map[string]interface{}{}, result)
if err == nil {
return
}


+ 23
- 21
statesync/mocks/state_provider.go View File

@ -3,6 +3,8 @@
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/state"
@ -14,13 +16,13 @@ type StateProvider struct {
mock.Mock
}
// AppHash provides a mock function with given fields: height
func (_m *StateProvider) AppHash(height uint64) ([]byte, error) {
ret := _m.Called(height)
// AppHash provides a mock function with given fields: ctx, height
func (_m *StateProvider) AppHash(ctx context.Context, height uint64) ([]byte, error) {
ret := _m.Called(ctx, height)
var r0 []byte
if rf, ok := ret.Get(0).(func(uint64) []byte); ok {
r0 = rf(height)
if rf, ok := ret.Get(0).(func(context.Context, uint64) []byte); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
@ -28,8 +30,8 @@ func (_m *StateProvider) AppHash(height uint64) ([]byte, error) {
}
var r1 error
if rf, ok := ret.Get(1).(func(uint64) error); ok {
r1 = rf(height)
if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
@ -37,13 +39,13 @@ func (_m *StateProvider) AppHash(height uint64) ([]byte, error) {
return r0, r1
}
// Commit provides a mock function with given fields: height
func (_m *StateProvider) Commit(height uint64) (*types.Commit, error) {
ret := _m.Called(height)
// Commit provides a mock function with given fields: ctx, height
func (_m *StateProvider) Commit(ctx context.Context, height uint64) (*types.Commit, error) {
ret := _m.Called(ctx, height)
var r0 *types.Commit
if rf, ok := ret.Get(0).(func(uint64) *types.Commit); ok {
r0 = rf(height)
if rf, ok := ret.Get(0).(func(context.Context, uint64) *types.Commit); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.Commit)
@ -51,8 +53,8 @@ func (_m *StateProvider) Commit(height uint64) (*types.Commit, error) {
}
var r1 error
if rf, ok := ret.Get(1).(func(uint64) error); ok {
r1 = rf(height)
if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
@ -60,20 +62,20 @@ func (_m *StateProvider) Commit(height uint64) (*types.Commit, error) {
return r0, r1
}
// State provides a mock function with given fields: height
func (_m *StateProvider) State(height uint64) (state.State, error) {
ret := _m.Called(height)
// State provides a mock function with given fields: ctx, height
func (_m *StateProvider) State(ctx context.Context, height uint64) (state.State, error) {
ret := _m.Called(ctx, height)
var r0 state.State
if rf, ok := ret.Get(0).(func(uint64) state.State); ok {
r0 = rf(height)
if rf, ok := ret.Get(0).(func(context.Context, uint64) state.State); ok {
r0 = rf(ctx, height)
} else {
r0 = ret.Get(0).(state.State)
}
var r1 error
if rf, ok := ret.Get(1).(func(uint64) error); ok {
r1 = rf(height)
if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}


+ 6
- 1
statesync/snapshots.go View File

@ -1,10 +1,12 @@
package statesync
import (
"context"
"crypto/sha256"
"fmt"
"math/rand"
"sort"
"time"
tmsync "github.com/tendermint/tendermint/libs/sync"
"github.com/tendermint/tendermint/p2p"
@ -76,7 +78,10 @@ func newSnapshotPool(stateProvider StateProvider) *snapshotPool {
// returns true if this was a new, non-blacklisted snapshot. The snapshot height is verified using
// the light client, and the expected app hash is set for the snapshot.
func (p *snapshotPool) Add(peer p2p.Peer, snapshot *snapshot) (bool, error) {
appHash, err := p.stateProvider.AppHash(snapshot.Height)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
appHash, err := p.stateProvider.AppHash(ctx, snapshot.Height)
if err != nil {
return false, err
}


+ 8
- 8
statesync/snapshots_test.go View File

@ -42,7 +42,7 @@ func TestSnapshot_Key(t *testing.T) {
func TestSnapshotPool_Add(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", uint64(1)).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, uint64(1)).Return([]byte("app_hash"), nil)
peer := &p2pmocks.Peer{}
peer.On("ID").Return(p2p.ID("id"))
@ -80,7 +80,7 @@ func TestSnapshotPool_Add(t *testing.T) {
func TestSnapshotPool_GetPeer(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
pool := newSnapshotPool(stateProvider)
s := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}}
@ -116,7 +116,7 @@ func TestSnapshotPool_GetPeer(t *testing.T) {
func TestSnapshotPool_GetPeers(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
pool := newSnapshotPool(stateProvider)
s := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}}
@ -140,7 +140,7 @@ func TestSnapshotPool_GetPeers(t *testing.T) {
func TestSnapshotPool_Ranked_Best(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
pool := newSnapshotPool(stateProvider)
// snapshots in expected order (best to worst). Highest height wins, then highest format.
@ -185,7 +185,7 @@ func TestSnapshotPool_Ranked_Best(t *testing.T) {
func TestSnapshotPool_Reject(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
pool := newSnapshotPool(stateProvider)
peer := &p2pmocks.Peer{}
peer.On("ID").Return(p2p.ID("id"))
@ -215,7 +215,7 @@ func TestSnapshotPool_Reject(t *testing.T) {
func TestSnapshotPool_RejectFormat(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
pool := newSnapshotPool(stateProvider)
peer := &p2pmocks.Peer{}
peer.On("ID").Return(p2p.ID("id"))
@ -246,7 +246,7 @@ func TestSnapshotPool_RejectFormat(t *testing.T) {
func TestSnapshotPool_RejectPeer(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
pool := newSnapshotPool(stateProvider)
peerA := &p2pmocks.Peer{}
@ -288,7 +288,7 @@ func TestSnapshotPool_RejectPeer(t *testing.T) {
func TestSnapshotPool_RemovePeer(t *testing.T) {
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
pool := newSnapshotPool(stateProvider)
peerA := &p2pmocks.Peer{}


+ 15
- 13
statesync/stateprovider.go View File

@ -1,6 +1,7 @@
package statesync
import (
"context"
"fmt"
"strings"
"time"
@ -26,11 +27,11 @@ import (
// to the state.State object, not the state machine.
type StateProvider interface {
// AppHash returns the app hash after the given height has been committed.
AppHash(height uint64) ([]byte, error)
AppHash(ctx context.Context, height uint64) ([]byte, error)
// Commit returns the commit at the given height.
Commit(height uint64) (*types.Commit, error)
Commit(ctx context.Context, height uint64) (*types.Commit, error)
// State returns a state object at the given height.
State(height uint64) (sm.State, error)
State(ctx context.Context, height uint64) (sm.State, error)
}
// lightClientStateProvider is a state provider using the light client.
@ -44,6 +45,7 @@ type lightClientStateProvider struct {
// NewLightClientStateProvider creates a new StateProvider using a light client and RPC clients.
func NewLightClientStateProvider(
ctx context.Context,
chainID string,
version tmstate.Version,
initialHeight int64,
@ -69,7 +71,7 @@ func NewLightClientStateProvider(
providerRemotes[provider] = server
}
lc, err := light.NewClient(chainID, trustOptions, providers[0], providers[1:],
lc, err := light.NewClient(ctx, chainID, trustOptions, providers[0], providers[1:],
lightdb.New(dbm.NewMemDB(), ""), light.Logger(logger), light.MaxRetryAttempts(5))
if err != nil {
return nil, err
@ -83,12 +85,12 @@ func NewLightClientStateProvider(
}
// AppHash implements StateProvider.
func (s *lightClientStateProvider) AppHash(height uint64) ([]byte, error) {
func (s *lightClientStateProvider) AppHash(ctx context.Context, height uint64) ([]byte, error) {
s.Lock()
defer s.Unlock()
// We have to fetch the next height, which contains the app hash for the previous height.
header, err := s.lc.VerifyLightBlockAtHeight(int64(height+1), time.Now())
header, err := s.lc.VerifyLightBlockAtHeight(ctx, int64(height+1), time.Now())
if err != nil {
return nil, err
}
@ -96,10 +98,10 @@ func (s *lightClientStateProvider) AppHash(height uint64) ([]byte, error) {
}
// Commit implements StateProvider.
func (s *lightClientStateProvider) Commit(height uint64) (*types.Commit, error) {
func (s *lightClientStateProvider) Commit(ctx context.Context, height uint64) (*types.Commit, error) {
s.Lock()
defer s.Unlock()
header, err := s.lc.VerifyLightBlockAtHeight(int64(height), time.Now())
header, err := s.lc.VerifyLightBlockAtHeight(ctx, int64(height), time.Now())
if err != nil {
return nil, err
}
@ -107,7 +109,7 @@ func (s *lightClientStateProvider) Commit(height uint64) (*types.Commit, error)
}
// State implements StateProvider.
func (s *lightClientStateProvider) State(height uint64) (sm.State, error) {
func (s *lightClientStateProvider) State(ctx context.Context, height uint64) (sm.State, error) {
s.Lock()
defer s.Unlock()
@ -128,15 +130,15 @@ func (s *lightClientStateProvider) State(height uint64) (sm.State, error) {
//
// We need to fetch the NextValidators from height+2 because if the application changed
// the validator set at the snapshot height then this only takes effect at height+2.
lastLightBlock, err := s.lc.VerifyLightBlockAtHeight(int64(height), time.Now())
lastLightBlock, err := s.lc.VerifyLightBlockAtHeight(ctx, int64(height), time.Now())
if err != nil {
return sm.State{}, err
}
curLightBlock, err := s.lc.VerifyLightBlockAtHeight(int64(height+1), time.Now())
curLightBlock, err := s.lc.VerifyLightBlockAtHeight(ctx, int64(height+1), time.Now())
if err != nil {
return sm.State{}, err
}
nextLightBlock, err := s.lc.VerifyLightBlockAtHeight(int64(height+2), time.Now())
nextLightBlock, err := s.lc.VerifyLightBlockAtHeight(ctx, int64(height+2), time.Now())
if err != nil {
return sm.State{}, err
}
@ -161,7 +163,7 @@ func (s *lightClientStateProvider) State(height uint64) (sm.State, error) {
return sm.State{}, fmt.Errorf("unable to create RPC client: %w", err)
}
rpcclient := lightrpc.NewClient(primaryRPC, s.lc)
result, err := rpcclient.ConsensusParams(&nextLightBlock.Height)
result, err := rpcclient.ConsensusParams(ctx, &nextLightBlock.Height)
if err != nil {
return sm.State{}, fmt.Errorf("unable to fetch consensus parameters for height %v: %w",
nextLightBlock.Height, err)


+ 5
- 2
statesync/syncer.go View File

@ -241,12 +241,15 @@ func (s *syncer) Sync(snapshot *snapshot, chunks *chunkQueue) (sm.State, *types.
go s.fetchChunks(ctx, snapshot, chunks)
}
pctx, pcancel := context.WithTimeout(context.Background(), 10*time.Second)
defer pcancel()
// Optimistically build new state, so we don't discover any light client failures at the end.
state, err := s.stateProvider.State(snapshot.Height)
state, err := s.stateProvider.State(pctx, snapshot.Height)
if err != nil {
return sm.State{}, nil, fmt.Errorf("failed to build new state: %w", err)
}
commit, err := s.stateProvider.Commit(snapshot.Height)
commit, err := s.stateProvider.Commit(pctx, snapshot.Height)
if err != nil {
return sm.State{}, nil, fmt.Errorf("failed to fetch commit: %w", err)
}


+ 8
- 8
statesync/syncer_test.go View File

@ -30,7 +30,7 @@ func setupOfferSyncer(t *testing.T) (*syncer, *proxymocks.AppConnSnapshot) {
connQuery := &proxymocks.AppConnQuery{}
connSnapshot := &proxymocks.AppConnSnapshot{}
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
syncer := newSyncer(log.NewNopLogger(), connSnapshot, connQuery, stateProvider, "")
return syncer, connSnapshot
}
@ -77,10 +77,10 @@ func TestSyncer_SyncAny(t *testing.T) {
s := &snapshot{Height: 1, Format: 1, Chunks: 3, Hash: []byte{1, 2, 3}}
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", uint64(1)).Return(state.AppHash, nil)
stateProvider.On("AppHash", uint64(2)).Return([]byte("app_hash_2"), nil)
stateProvider.On("Commit", uint64(1)).Return(commit, nil)
stateProvider.On("State", uint64(1)).Return(state, nil)
stateProvider.On("AppHash", mock.Anything, uint64(1)).Return(state.AppHash, nil)
stateProvider.On("AppHash", mock.Anything, uint64(2)).Return([]byte("app_hash_2"), nil)
stateProvider.On("Commit", mock.Anything, uint64(1)).Return(commit, nil)
stateProvider.On("State", mock.Anything, uint64(1)).Return(state, nil)
connSnapshot := &proxymocks.AppConnSnapshot{}
connQuery := &proxymocks.AppConnQuery{}
@ -406,7 +406,7 @@ func TestSyncer_applyChunks_Results(t *testing.T) {
connQuery := &proxymocks.AppConnQuery{}
connSnapshot := &proxymocks.AppConnSnapshot{}
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
syncer := newSyncer(log.NewNopLogger(), connSnapshot, connQuery, stateProvider, "")
body := []byte{1, 2, 3}
@ -457,7 +457,7 @@ func TestSyncer_applyChunks_RefetchChunks(t *testing.T) {
connQuery := &proxymocks.AppConnQuery{}
connSnapshot := &proxymocks.AppConnSnapshot{}
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
syncer := newSyncer(log.NewNopLogger(), connSnapshot, connQuery, stateProvider, "")
chunks, err := newChunkQueue(&snapshot{Height: 1, Format: 1, Chunks: 3}, "")
@ -520,7 +520,7 @@ func TestSyncer_applyChunks_RejectSenders(t *testing.T) {
connQuery := &proxymocks.AppConnQuery{}
connSnapshot := &proxymocks.AppConnSnapshot{}
stateProvider := &mocks.StateProvider{}
stateProvider.On("AppHash", mock.Anything).Return([]byte("app_hash"), nil)
stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil)
syncer := newSyncer(log.NewNopLogger(), connSnapshot, connQuery, stateProvider, "")
// Set up three peers across two snapshots, and ask for one of them to be banned.


Loading…
Cancel
Save