Skip to content

Commit

Permalink
fill in all the missing fields of status rpc (cosmos#599)
Browse files Browse the repository at this point in the history
Fill in all the missing fields of Status() ROC for compatibility with Tendermint RPC.
Fixes cosmos#241.
  • Loading branch information
gupadhyaya authored Nov 23, 2022
1 parent 8a3780e commit 3a518be
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 18 deletions.
1 change: 1 addition & 0 deletions config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions p2p/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
66 changes: 49 additions & 17 deletions rpc/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
123 changes: 122 additions & 1 deletion rpc/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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)
}
}

0 comments on commit 3a518be

Please sign in to comment.