From 85a4be87a73c00aefc66ee382c1b1c1fb8d6f400 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 23 Sep 2020 09:21:57 +0400 Subject: [PATCH] rpc/client: take context as first param (#5347) Closes #5145 also applies to light/client --- CHANGELOG_PENDING.md | 2 + cmd/tendermint/commands/debug/util.go | 7 +- cmd/tendermint/commands/lite.go | 4 +- light/client.go | 68 ++++++----- light/client_benchmark_test.go | 14 ++- light/client_test.go | 72 ++++++++---- light/detector.go | 40 ++++--- light/detector_test.go | 14 ++- light/example_test.go | 11 +- light/provider/http/http.go | 19 ++-- light/provider/http/http_test.go | 9 +- light/provider/mock/deadmock.go | 5 +- light/provider/mock/mock.go | 5 +- light/provider/provider.go | 6 +- light/proxy/routes.go | 46 ++++---- light/rpc/client.go | 124 ++++++++++---------- light/setup.go | 3 + node/node.go | 3 + rpc/client/event_test.go | 5 +- rpc/client/evidence_test.go | 11 +- rpc/client/examples_test.go | 13 ++- rpc/client/helpers.go | 2 +- rpc/client/http/http.go | 152 ++++++++++++++++--------- rpc/client/interface.go | 51 +++++---- rpc/client/local/local.go | 69 ++++++----- rpc/client/mock/abci.go | 55 +++++---- rpc/client/mock/abci_test.go | 39 ++++--- rpc/client/mock/client.go | 52 +++++---- rpc/client/mock/status.go | 8 +- rpc/client/mock/status_test.go | 3 +- rpc/client/rpc_test.go | 114 ++++++++++--------- rpc/jsonrpc/client/http_json_client.go | 28 +++-- rpc/jsonrpc/client/http_uri_client.go | 23 +++- rpc/jsonrpc/jsonrpc_test.go | 12 +- rpc/test/helpers.go | 2 +- statesync/mocks/state_provider.go | 44 +++---- statesync/snapshots.go | 7 +- statesync/snapshots_test.go | 16 +-- statesync/stateprovider.go | 28 ++--- statesync/syncer.go | 7 +- statesync/syncer_test.go | 16 +-- 41 files changed, 706 insertions(+), 503 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 055fdb149..f8be7a547 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -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 diff --git a/cmd/tendermint/commands/debug/util.go b/cmd/tendermint/commands/debug/util.go index 6bbafaa0f..226bfadc7 100644 --- a/cmd/tendermint/commands/debug/util.go +++ b/cmd/tendermint/commands/debug/util.go @@ -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) } diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index 10f013070..7884c3a54 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -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, diff --git a/light/client.go b/light/client.go index 8e6e3d4d8..49858c2bb 100644 --- a/light/client.go +++ b/light/client.go @@ -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 } diff --git a/light/client_benchmark_test.go b/light/client_benchmark_test.go index 8daab76a8..eb02686b8 100644 --- a/light/client_benchmark_test.go +++ b/light/client_benchmark_test.go @@ -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) } diff --git a/light/client_test.go b/light/client_test.go index 05c0c8e6f..a41315dc3 100644 --- a/light/client_test.go +++ b/light/client_test.go @@ -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 { diff --git a/light/detector.go b/light/detector.go index e528bb866..2517fa766 100644 --- a/light/detector.go +++ b/light/detector.go @@ -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) } diff --git a/light/detector_test.go b/light/detector_test.go index 2e95f5f55..4777d0c2d 100644 --- a/light/detector_test.go +++ b/light/detector_test.go @@ -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())) } diff --git a/light/example_test.go b/light/example_test.go index 1e01bcb86..7b48616d5 100644 --- a/light/example_test.go +++ b/light/example_test.go @@ -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) } diff --git a/light/provider/http/http.go b/light/provider/http/http.go index a46e961ff..fd42d75d7 100644 --- a/light/provider/http/http.go +++ b/light/provider/http/http.go @@ -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()) { diff --git a/light/provider/http/http_test.go b/light/provider/http/http_test.go index 082de01bb..b6b3989a8 100644 --- a/light/provider/http/http_test.go +++ b/light/provider/http/http_test.go @@ -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) } diff --git a/light/provider/mock/deadmock.go b/light/provider/mock/deadmock.go index e4dc35f91..e32e6372a 100644 --- a/light/provider/mock/deadmock.go +++ b/light/provider/mock/deadmock.go @@ -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 } diff --git a/light/provider/mock/mock.go b/light/provider/mock/mock.go index 176bf392b..cf28846ef 100644 --- a/light/provider/mock/mock.go +++ b/light/provider/mock/mock.go @@ -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 } diff --git a/light/provider/provider.go b/light/provider/provider.go index 1cf60da26..80e8dbc15 100644 --- a/light/provider/provider.go +++ b/light/provider/provider.go @@ -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 } diff --git a/light/proxy/routes.go b/light/proxy/routes.go index 843f32ee1..cb60118cf 100644 --- a/light/proxy/routes.go +++ b/light/proxy/routes.go @@ -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) } } diff --git a/light/rpc/client.go b/light/rpc/client.go index 0790a862e..df1615e6c 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -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) } diff --git a/light/setup.go b/light/setup.go index a4ad89741..af72301b0 100644 --- a/light/setup.go +++ b/light/setup.go @@ -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], diff --git a/node/node.go b/node/node.go index 455ad5285..5de3873c6 100644 --- a/node/node.go +++ b/node/node.go @@ -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, diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go index da4f7e851..5734d6c1b 100644 --- a/rpc/client/event_test.go +++ b/rpc/client/event_test.go @@ -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)) } diff --git a/rpc/client/evidence_test.go b/rpc/client/evidence_test.go index f32610aea..351c51bc9 100644 --- a/rpc/client/evidence_test.go +++ b/rpc/client/evidence_test.go @@ -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) } } diff --git a/rpc/client/examples_test.go b/rpc/client/examples_test.go index 5d87a3a98..dc2792ea2 100644 --- a/rpc/client/examples_test.go +++ b/rpc/client/examples_test.go @@ -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) } diff --git a/rpc/client/helpers.go b/rpc/client/helpers.go index 9e7a3862d..78579b8a3 100644 --- a/rpc/client/helpers.go +++ b/rpc/client/helpers.go @@ -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 } diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 43a362a46..292609ccb 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -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 } diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 340a483e1..c6ff0fee2 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -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. diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index e259c23bd..eb9cc485b 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -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) } diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index 106827da0..0737deec0 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -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, diff --git a/rpc/client/mock/abci_test.go b/rpc/client/mock/abci_test.go index cbabcb3c7..d164b275a 100644 --- a/rpc/client/mock/abci_test.go +++ b/rpc/client/mock/abci_test.go @@ -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) diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index 7344eb383..ed911ec20 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -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) } diff --git a/rpc/client/mock/status.go b/rpc/client/mock/status.go index 58b29d573..6dd6a8d44 100644 --- a/rpc/client/mock/status.go +++ b/rpc/client/mock/status.go @@ -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, diff --git a/rpc/client/mock/status_test.go b/rpc/client/mock/status_test.go index d252a54eb..4c2112b9c 100644 --- a/rpc/client/mock/status_test.go +++ b/rpc/client/mock/status_test.go @@ -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) diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 001d154e5..0ecc03db0 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -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") } diff --git a/rpc/jsonrpc/client/http_json_client.go b/rpc/jsonrpc/client/http_json_client.go index 0a38332a1..1cd704d7b 100644 --- a/rpc/jsonrpc/client/http_json_client.go +++ b/rpc/jsonrpc/client/http_json_client.go @@ -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{}, diff --git a/rpc/jsonrpc/client/http_uri_client.go b/rpc/jsonrpc/client/http_uri_client.go index 8c4b2a463..3f376ddb0 100644 --- a/rpc/jsonrpc/client/http_uri_client.go +++ b/rpc/jsonrpc/client/http_uri_client.go @@ -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("post form failed: %w", err) + 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: %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) diff --git a/rpc/jsonrpc/jsonrpc_test.go b/rpc/jsonrpc/jsonrpc_test.go index eaf36111a..24269df8e 100644 --- a/rpc/jsonrpc/jsonrpc_test.go +++ b/rpc/jsonrpc/jsonrpc_test.go @@ -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 diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 2879a7819..5e212ec42 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -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 } diff --git a/statesync/mocks/state_provider.go b/statesync/mocks/state_provider.go index 57de55c20..47dbb86d2 100644 --- a/statesync/mocks/state_provider.go +++ b/statesync/mocks/state_provider.go @@ -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) } diff --git a/statesync/snapshots.go b/statesync/snapshots.go index e151bed02..3eca5c0be 100644 --- a/statesync/snapshots.go +++ b/statesync/snapshots.go @@ -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 } diff --git a/statesync/snapshots_test.go b/statesync/snapshots_test.go index ebbb29717..588c0ac31 100644 --- a/statesync/snapshots_test.go +++ b/statesync/snapshots_test.go @@ -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{} diff --git a/statesync/stateprovider.go b/statesync/stateprovider.go index 1abe5b92a..1e21b0aaf 100644 --- a/statesync/stateprovider.go +++ b/statesync/stateprovider.go @@ -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) diff --git a/statesync/syncer.go b/statesync/syncer.go index 45bd0fe63..3152e3236 100644 --- a/statesync/syncer.go +++ b/statesync/syncer.go @@ -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) } diff --git a/statesync/syncer_test.go b/statesync/syncer_test.go index d0bf2aaf5..c78509bfe 100644 --- a/statesync/syncer_test.go +++ b/statesync/syncer_test.go @@ -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.