From 3a518be702b3d4183deb888fbc7958fcc2c3b2bc Mon Sep 17 00:00:00 2001 From: Ganesha Upadhyaya Date: Wed, 23 Nov 2022 16:26:13 +0530 Subject: [PATCH] fill in all the missing fields of status rpc (#599) Fill in all the missing fields of Status() ROC for compatibility with Tendermint RPC. Fixes #241. --- config/defaults.go | 1 + p2p/client.go | 5 ++ rpc/client/client.go | 66 ++++++++++++++------ rpc/client/client_test.go | 123 +++++++++++++++++++++++++++++++++++++- 4 files changed, 177 insertions(+), 18 deletions(-) diff --git a/config/defaults.go b/config/defaults.go index 484dddb85e4..8211c2886c5 100644 --- a/config/defaults.go +++ b/config/defaults.go @@ -9,6 +9,7 @@ import ( const ( // DefaultListenAddress is a default listen address for P2P client. DefaultListenAddress = "/ip4/0.0.0.0/tcp/7676" + Version = "0.4.0" ) // DefaultNodeConfig keeps default values of NodeConfig diff --git a/p2p/client.go b/p2p/client.go index 17f8c431419..ed64827762f 100644 --- a/p2p/client.go +++ b/p2p/client.go @@ -204,6 +204,11 @@ func (c *Client) Addrs() []multiaddr.Multiaddr { return c.host.Addrs() } +// Info returns client ID, ListenAddr, and Network info +func (c *Client) Info() (p2p.ID, string, string) { + return p2p.ID(c.host.ID().String()), c.conf.ListenAddress, c.chainID +} + // PeerConnection describe basic information about P2P connection. // TODO(tzdybal): move it somewhere type PeerConnection struct { diff --git a/rpc/client/client.go b/rpc/client/client.go index c101d623e9d..c51c127c72d 100644 --- a/rpc/client/client.go +++ b/rpc/client/client.go @@ -14,12 +14,15 @@ import ( tmmath "github.com/tendermint/tendermint/libs/math" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" + corep2p "github.com/tendermint/tendermint/p2p" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/version" + rconfig "github.com/celestiaorg/rollmint/config" abciconv "github.com/celestiaorg/rollmint/conv/abci" "github.com/celestiaorg/rollmint/mempool" "github.com/celestiaorg/rollmint/node" @@ -692,31 +695,60 @@ func (c *Client) BlockSearch(ctx context.Context, query string, page, perPage *i func (c *Client) Status(ctx context.Context) (*ctypes.ResultStatus, error) { latest, err := c.node.Store.LoadBlock(c.node.Store.Height()) if err != nil { - // TODO(tzdybal): extract error return nil, fmt.Errorf("failed to find latest block: %w", err) } - latestBlockHash := latest.Header.DataHash - latestAppHash := latest.Header.AppHash - latestHeight := latest.Header.Height - latestBlockTimeNano := latest.Header.Time + initial, err := c.node.Store.LoadBlock(uint64(c.node.GetGenesis().InitialHeight)) + if err != nil { + return nil, fmt.Errorf("failed to find earliest block: %w", err) + } + + validators, err := c.node.Store.LoadValidators(latest.Header.Height) + if err != nil { + return nil, fmt.Errorf("failed to fetch the validator info at latest block: %w", err) + } + _, validator := validators.GetByAddress(latest.Header.ProposerAddress) + + state, err := c.node.Store.LoadState() + if err != nil { + return nil, fmt.Errorf("failed to load the last saved state: %w", err) + } + defaultProtocolVersion := corep2p.NewProtocolVersion( + version.P2PProtocol, + state.Version.Consensus.Block, + state.Version.Consensus.App, + ) + id, addr, network := c.node.P2P.Info() + txIndexerStatus := "on" result := &ctypes.ResultStatus{ - // TODO(tzdybal): NodeInfo + NodeInfo: corep2p.DefaultNodeInfo{ + ProtocolVersion: defaultProtocolVersion, + DefaultNodeID: id, + ListenAddr: addr, + Network: network, + Version: rconfig.Version, + Moniker: config.DefaultBaseConfig().Moniker, + Other: corep2p.DefaultNodeInfoOther{ + TxIndex: txIndexerStatus, + RPCAddress: c.config.ListenAddress, + }, + }, SyncInfo: ctypes.SyncInfo{ - LatestBlockHash: latestBlockHash[:], - LatestAppHash: latestAppHash[:], - LatestBlockHeight: int64(latestHeight), - LatestBlockTime: time.Unix(0, int64(latestBlockTimeNano)), - // TODO(tzdybal): add missing fields - //EarliestBlockHash: earliestBlockHash, - //EarliestAppHash: earliestAppHash, - //EarliestBlockHeight: earliestBlockHeight, - //EarliestBlockTime: time.Unix(0, earliestBlockTimeNano), - //CatchingUp: env.ConsensusReactor.WaitSync(), + LatestBlockHash: latest.Header.DataHash[:], + LatestAppHash: latest.Header.AppHash[:], + LatestBlockHeight: int64(latest.Header.Height), + LatestBlockTime: time.Unix(0, int64(latest.Header.Time)), + EarliestBlockHash: initial.Header.DataHash[:], + EarliestAppHash: initial.Header.AppHash[:], + EarliestBlockHeight: int64(initial.Header.Height), + EarliestBlockTime: time.Unix(0, int64(initial.Header.Time)), + CatchingUp: true, // the client is always syncing in the background to the latest height }, ValidatorInfo: ctypes.ValidatorInfo{ - Address: latest.Header.ProposerAddress, + Address: validator.Address, + PubKey: validator.PubKey, + VotingPower: validator.VotingPower, }, } return result, nil diff --git a/rpc/client/client_test.go b/rpc/client/client_test.go index fcb1dea9f1f..5667e4d417a 100644 --- a/rpc/client/client_test.go +++ b/rpc/client/client_test.go @@ -17,13 +17,16 @@ import ( "github.com/libp2p/go-libp2p/core/peer" abcicli "github.com/tendermint/tendermint/abci/client" abci "github.com/tendermint/tendermint/abci/types" + tconfig "github.com/tendermint/tendermint/config" tmcrypto "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/encoding" "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/p2p" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/version" "github.com/celestiaorg/rollmint/config" abciconv "github.com/celestiaorg/rollmint/conv/abci" @@ -712,11 +715,15 @@ func TestValidatorSetHandling(t *testing.T) { // copy-pasted from store/store_test.go func getRandomBlock(height uint64, nTxs int) *types.Block { + return getRandomBlockWithProposer(height, nTxs, getRandomBytes(20)) +} + +func getRandomBlockWithProposer(height uint64, nTxs int, proposerAddr []byte) *types.Block { block := &types.Block{ Header: types.Header{ Height: height, Version: types.Version{Block: types.InitStateVersion.Consensus.Block}, - ProposerAddress: getRandomBytes(20), + ProposerAddress: proposerAddr, }, Data: types.Data{ Txs: make(types.Txs, nTxs), @@ -898,3 +905,117 @@ func TestMempool2Nodes(t *testing.T) { assert.Equal(node2.Mempool.SizeBytes(), int64(len("good"))) } + +func TestStatus(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + app := &mocks.Application{} + app.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{}) + key, _, _ := crypto.GenerateEd25519Key(crand.Reader) + signingKey, _, _ := crypto.GenerateEd25519Key(crand.Reader) + + vKeys := make([]tmcrypto.PrivKey, 2) + validators := make([]*tmtypes.Validator, len(vKeys)) + genesisValidators := make([]tmtypes.GenesisValidator, len(vKeys)) + for i := 0; i < len(vKeys); i++ { + vKeys[i] = ed25519.GenPrivKey() + validators[i] = &tmtypes.Validator{ + Address: vKeys[i].PubKey().Address(), + PubKey: vKeys[i].PubKey(), + VotingPower: int64(i + 100), + ProposerPriority: int64(i), + } + genesisValidators[i] = tmtypes.GenesisValidator{ + Address: vKeys[i].PubKey().Address(), + PubKey: vKeys[i].PubKey(), + Power: int64(i + 100), + Name: "one", + } + } + + node, err := node.NewNode( + context.Background(), + config.NodeConfig{ + DALayer: "mock", + P2P: config.P2PConfig{ + ListenAddress: "/ip4/0.0.0.0/tcp/26656", + }, + Aggregator: true, + BlockManagerConfig: config.BlockManagerConfig{ + BlockTime: 10 * time.Millisecond, + }, + }, + key, + signingKey, + abcicli.NewLocalClient(nil, app), + &tmtypes.GenesisDoc{ + ChainID: "test", + Validators: genesisValidators, + }, + log.TestingLogger(), + ) + require.NoError(err) + require.NotNil(node) + + validatorSet := tmtypes.NewValidatorSet(validators) + err = node.Store.SaveValidators(1, validatorSet) + require.NoError(err) + err = node.Store.SaveValidators(2, validatorSet) + require.NoError(err) + err = node.Store.UpdateState(types.State{LastValidators: validatorSet, NextValidators: validatorSet, Validators: validatorSet}) + assert.NoError(err) + + rpc := NewClient(node) + assert.NotNil(rpc) + + earliestBlock := getRandomBlockWithProposer(1, 1, validators[0].Address.Bytes()) + err = rpc.node.Store.SaveBlock(earliestBlock, &types.Commit{Height: earliestBlock.Header.Height}) + rpc.node.Store.SetHeight(earliestBlock.Header.Height) + require.NoError(err) + + latestBlock := getRandomBlockWithProposer(2, 1, validators[1].Address.Bytes()) + err = rpc.node.Store.SaveBlock(latestBlock, &types.Commit{Height: latestBlock.Header.Height}) + rpc.node.Store.SetHeight(latestBlock.Header.Height) + require.NoError(err) + + err = node.Start() + require.NoError(err) + + resp, err := rpc.Status(context.Background()) + assert.NoError(err) + + assert.Equal(int64(1), resp.SyncInfo.EarliestBlockHeight) + assert.Equal(int64(2), resp.SyncInfo.LatestBlockHeight) + + assert.Equal(validators[1].Address, resp.ValidatorInfo.Address) + assert.Equal(validators[1].PubKey, resp.ValidatorInfo.PubKey) + assert.Equal(validators[1].VotingPower, resp.ValidatorInfo.VotingPower) + + // specific validation + assert.Equal(tconfig.DefaultBaseConfig().Moniker, resp.NodeInfo.Moniker) + state, err := rpc.node.Store.LoadState() + assert.NoError(err) + defaultProtocolVersion := p2p.NewProtocolVersion( + version.P2PProtocol, + state.Version.Consensus.Block, + state.Version.Consensus.App, + ) + assert.Equal(defaultProtocolVersion, resp.NodeInfo.ProtocolVersion) + + assert.NotNil(resp.NodeInfo.Other.TxIndex) + cases := []struct { + expected bool + other p2p.DefaultNodeInfoOther + }{ + + {false, p2p.DefaultNodeInfoOther{}}, + {false, p2p.DefaultNodeInfoOther{TxIndex: "aa"}}, + {false, p2p.DefaultNodeInfoOther{TxIndex: "off"}}, + {true, p2p.DefaultNodeInfoOther{TxIndex: "on"}}, + } + for _, tc := range cases { + res := resp.NodeInfo.Other.TxIndex == tc.other.TxIndex + assert.Equal(tc.expected, res, tc) + } +}