diff --git a/rpc/core/status.go b/rpc/core/status.go index 63e62b2c7..739e67b86 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -4,10 +4,10 @@ import ( "bytes" "time" + cmn "github.com/tendermint/tendermint/libs/common" ctypes "github.com/tendermint/tendermint/rpc/core/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - cmn "github.com/tendermint/tendermint/libs/common" ) // Get Tendermint status including node info, pubkey, latest block @@ -104,8 +104,17 @@ func Status() (*ctypes.ResultStatus, error) { return result, nil } +const consensusTimeout = time.Second + func validatorAtHeight(h int64) *types.Validator { - lastBlockHeight, vals := consensusState.GetValidators() + lastBlockHeight, vals := getValidatorsWithTimeout( + consensusState, + consensusTimeout, + ) + + if lastBlockHeight == -1 { + return nil + } privValAddress := pubKey.Address() @@ -131,3 +140,32 @@ func validatorAtHeight(h int64) *types.Validator { return nil } + +type validatorRetriever interface { + GetValidators() (int64, []*types.Validator) +} + +// NOTE: Consensus might halt, but we still need to process RPC requests (at +// least for endpoints whole output does not depend on consensus state). +func getValidatorsWithTimeout( + vr validatorRetriever, + t time.Duration, +) (int64, []*types.Validator) { + resultCh := make(chan struct { + lastBlockHeight int64 + vals []*types.Validator + }) + go func() { + h, v := vr.GetValidators() + resultCh <- struct { + lastBlockHeight int64 + vals []*types.Validator + }{h, v} + }() + select { + case res := <-resultCh: + return res.lastBlockHeight, res.vals + case <-time.After(t): + return -1, []*types.Validator{} + } +} diff --git a/rpc/core/status_test.go b/rpc/core/status_test.go new file mode 100644 index 000000000..e44ffed0f --- /dev/null +++ b/rpc/core/status_test.go @@ -0,0 +1,39 @@ +package core + +import ( + "testing" + "time" + + "github.com/tendermint/tendermint/types" +) + +func TestGetValidatorsWithTimeout(t *testing.T) { + height, vs := getValidatorsWithTimeout( + testValidatorReceiver{}, + time.Millisecond, + ) + + if height != -1 { + t.Errorf("expected negative height") + } + + if len(vs) != 0 { + t.Errorf("expected no validators") + } +} + +type testValidatorReceiver struct{} + +func (tr testValidatorReceiver) GetValidators() (int64, []*types.Validator) { + vs := []*types.Validator{} + + for i := 0; i < 3; i++ { + v, _ := types.RandValidator(true, 10) + + vs = append(vs, v) + } + + time.Sleep(time.Millisecond) + + return 10, vs +}