Merge remote-tracking branch 'origin/jasmina/4457-blocksync-verification_part1' into jasmina/4457_block_sync_verification

This commit is contained in:
Jasmina Malicevic
2022-05-11 15:39:39 +02:00
274 changed files with 10131 additions and 6187 deletions
+21 -33
View File
@@ -28,7 +28,7 @@ eg, L = latency = 0.1s
*/
const (
requestIntervalMS = 2
requestInterval = 2 * time.Millisecond
maxTotalRequesters = 600
maxPeerErrBuffer = 1000
maxPendingRequests = maxTotalRequesters
@@ -121,7 +121,6 @@ func NewBlockPool(
peers: make(map[types.NodeID]*bpPeer),
requesters: make(map[int64]*bpRequester),
witnessRequesters: make(map[int64]*witnessRequester),
// verificationRequesters: make(map[int64]*bpRequester),
height: start,
startHeight: start,
numPending: 0,
@@ -148,27 +147,23 @@ func (*BlockPool) OnStop() {}
// spawns requesters as needed
func (pool *BlockPool) makeRequestersRoutine(ctx context.Context) {
for {
if !pool.IsRunning() {
break
for pool.IsRunning() {
if ctx.Err() != nil {
return
}
_, numPending, lenRequesters := pool.GetStatus()
switch {
case numPending >= maxPendingRequests:
// sleep for a bit.
time.Sleep(requestIntervalMS * time.Millisecond)
// check for timed out peers
if numPending >= maxPendingRequests || lenRequesters >= maxTotalRequesters {
// This is preferable to using a timer because the request interval
// is so small. Larger request intervals may necessitate using a
// timer/ticker.
time.Sleep(requestInterval)
pool.removeTimedoutPeers()
case lenRequesters >= maxTotalRequesters:
// sleep for a bit.
time.Sleep(requestIntervalMS * time.Millisecond)
// check for timed out peers
pool.removeTimedoutPeers()
default:
// request for more blocks.
pool.makeNextRequester(ctx)
continue
}
// request for more blocks.
pool.makeNextRequester(ctx)
}
}
@@ -222,16 +217,6 @@ func (pool *BlockPool) IsCaughtUp() bool {
return pool.height >= (pool.maxPeerHeight - 1)
}
func (pool *BlockPool) PeekBlock() (first *types.Block) {
pool.mtx.RLock()
defer pool.mtx.RUnlock()
if r := pool.requesters[pool.height]; r != nil {
first = r.getBlock()
}
return
}
// PeekTwoBlocks returns blocks at pool.height and pool.height+1.
// We need to see the second block's Commit to validate the first block.
// So we peek two blocks at a time.
@@ -277,10 +262,6 @@ func (pool *BlockPool) PopRequest() {
} else {
panic(fmt.Sprintf("Expected requester to pop, got nothing at height %v", pool.height))
}
// if r := pool.verificationRequesters[pool.height]; r != nil {
// r.Stop()
// delete(pool.verificationRequesters, pool.height)
// }
}
// RedoRequest invalidates the block at pool.height,
@@ -856,9 +837,16 @@ OUTER_LOOP:
if !bpr.IsRunning() || !bpr.pool.IsRunning() {
return
}
if ctx.Err() != nil {
return
}
peer = bpr.pool.pickIncrAvailablePeer(bpr.height)
if peer == nil {
time.Sleep(requestIntervalMS * time.Millisecond)
// This is preferable to using a timer because the request
// interval is so small. Larger request intervals may
// necessitate using a timer/ticker.
time.Sleep(requestInterval)
continue PICK_PEER_LOOP
}
break PICK_PEER_LOOP
+4 -4
View File
@@ -111,8 +111,8 @@ func TestBlockPoolBasic(t *testing.T) {
if !pool.IsRunning() {
return
}
first := pool.PeekBlock()
if first != nil {
first, second := pool.PeekTwoBlocks()
if first != nil && second != nil {
pool.PopRequest()
} else {
time.Sleep(1 * time.Second)
@@ -166,8 +166,8 @@ func TestBlockPoolTimeout(t *testing.T) {
if !pool.IsRunning() {
return
}
first := pool.PeekBlock()
if first != nil {
first, second := pool.PeekTwoBlocks()
if first != nil && second != nil {
pool.PopRequest()
} else {
time.Sleep(1 * time.Second)
+65 -29
View File
@@ -214,13 +214,10 @@ func (r *Reactor) sendHeaderToPeer(ctx context.Context, msg *bcproto.HeaderReque
func (r *Reactor) respondToPeer(ctx context.Context, msg *bcproto.BlockRequest, peerID types.NodeID, blockSyncCh *p2p.Channel) error {
block := r.store.LoadBlockProto(msg.Height)
if block != nil {
blockCommit := r.store.LoadBlockCommitProto(msg.Height)
if blockCommit != nil {
return blockSyncCh.Send(ctx, p2p.Envelope{
To: peerID,
Message: &bcproto.BlockResponse{Block: block},
})
}
return blockSyncCh.Send(ctx, p2p.Envelope{
To: peerID,
Message: &bcproto.BlockResponse{Block: block},
})
}
r.logger.Info("peer requesting a block we do not have", "peer", peerID, "height", msg.Height)
@@ -234,7 +231,7 @@ func (r *Reactor) respondToPeer(ctx context.Context, msg *bcproto.BlockRequest,
// handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
// It will handle errors and any possible panics gracefully. A caller can handle
// any error returned by sending a PeerError on the respective channel.
func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelope *p2p.Envelope, blockSyncCh *p2p.Channel) (err error) {
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, blockSyncCh *p2p.Channel) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("panic in processing message: %v", e)
@@ -248,7 +245,7 @@ func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelop
r.logger.Debug("received message", "message", envelope.Message, "peer", envelope.From)
switch chID {
switch envelope.ChannelID {
case BlockSyncChannel:
switch msg := envelope.Message.(type) {
case *bcproto.HeaderRequest:
@@ -292,7 +289,7 @@ func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelop
}
default:
err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", chID, envelope)
err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", envelope.ChannelID, envelope)
}
return err
@@ -307,12 +304,12 @@ func (r *Reactor) processBlockSyncCh(ctx context.Context, blockSyncCh *p2p.Chann
iter := blockSyncCh.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
if err := r.handleMessage(ctx, blockSyncCh.ID, envelope, blockSyncCh); err != nil {
if err := r.handleMessage(ctx, envelope, blockSyncCh); err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return
}
r.logger.Error("failed to process message", "ch_id", blockSyncCh.ID, "envelope", envelope, "err", err)
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
@@ -554,7 +551,7 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
newBlockParts, err2 := newBlock.MakePartSet(types.BlockPartSizeBytes)
if err2 != nil {
r.logger.Error("failed to make ",
r.logger.Error("failed to make block at ",
"height", newBlock.Height,
"err", err2.Error())
return
@@ -564,11 +561,21 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
newBlockPartSetHeader = newBlockParts.Header()
newBlockID = types.BlockID{Hash: newBlock.Hash(), PartSetHeader: newBlockPartSetHeader}
)
// Finally, verify the first block using the second's commit.
//
// NOTE: We can probably make this more efficient, but note that calling
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
// currently necessary.
if r.lastTrustedBlock != nil {
err := VerifyNextBlock(newBlock, newBlockID, verifyBlock, r.lastTrustedBlock.block, r.lastTrustedBlock.commit, state.NextValidators)
if err != nil {
r.logger.Error(
err.Error(),
"last_commit", verifyBlock.LastCommit,
"block_id", newBlock,
"height", newBlock.Height,
)
switch err.(type) {
case ErrBlockIDDiff:
case ErrValidationFailed:
@@ -579,12 +586,13 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
}); serr != nil {
return
}
case ErrInvalidVerifyBlock:
r.logger.Error(
err.Error(),
"last_commit", verifyBlock.LastCommit,
"block_id", newBlockID,
"height", r.lastTrustedBlock.block.Height,
"verify_block_id", newBlockID,
"verify_block_height", newBlock.Height,
)
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 2)
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
@@ -593,10 +601,19 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
}); serr != nil {
return
}
}
}
continue // was return previously
}
// // Finally, verify the first block using the second's commit.
// //
// // NOTE: We can probably make this more efficient, but note that calling
// // first.Hash() doesn't verify the tx contents, so MakePartSet() is
// // currently necessary.
// err = state.Validators.VerifyCommitLight(chainID, firstID, first.Height, second.LastCommit)
r.lastTrustedBlock.block = newBlock
r.lastTrustedBlock.commit = verifyBlock.LastCommit
} else {
// we need to load the last block we trusted
if r.initialState.LastBlockHeight != 0 {
@@ -607,36 +624,55 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
}
oldHash := r.initialState.Validators.Hash()
if !bytes.Equal(oldHash, newBlock.ValidatorsHash) {
fmt.Println(
r.logger.Error("The validator set provided by the new block does not match the expected validator set",
"initial hash ", r.initialState.Validators.Hash(),
"new hash ", newBlock.ValidatorsHash,
)
return
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 1)
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
NodeID: peerID,
Err: ErrValidationFailed{},
}); serr != nil {
return
}
continue // was return previously
}
}
if err := r.verifyWithWitnesses(newBlock); err != nil {
r.logger.Debug("Witness verificatio nfailed")
}
if r.lastTrustedBlock == nil {
r.lastTrustedBlock = &BlockResponse{block: newBlock, commit: verifyBlock.LastCommit}
} else {
r.lastTrustedBlock.block = newBlock
r.lastTrustedBlock.commit = verifyBlock.LastCommit
}
var err error
// validate the block before we persist it
err = r.blockExec.ValidateBlock(ctx, state, newBlock)
if err != nil {
r.logger.Error("The validator set provided by the new block does not match the expected validator set",
"initial hash ", r.initialState.Validators.Hash(),
"new hash ", newBlock.ValidatorsHash,
)
peerID := r.pool.RedoRequest(r.lastTrustedBlock.block.Height + 1)
if serr := blockSyncCh.SendError(ctx, p2p.PeerError{
NodeID: peerID,
Err: ErrValidationFailed{},
}); serr != nil {
return
}
continue // was return previously
}
r.pool.PopRequest()
// TODO: batch saves so we do not persist to disk every block
r.store.SaveBlock(newBlock, newBlockParts, verifyBlock.LastCommit)
var err error
// TODO: Same thing for app - but we would need a way to get the hash
// without persisting the state.
state, err = r.blockExec.ApplyBlock(ctx, state, newBlockID, newBlock)
if err != nil {
panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", newBlock.Height, newBlock.Hash(), err))
}
if err != nil {
// TODO: This is bad, are we zombie?
panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", newBlock.Height, newBlock.Hash(), err))
+220 -29
View File
@@ -48,7 +48,7 @@ func setup(
ctx context.Context,
t *testing.T,
genDoc *types.GenesisDoc,
privVal types.PrivValidator,
privValArray []types.PrivValidator,
maxBlockHeights []int64,
) *reactorTestSuite {
t.Helper()
@@ -75,10 +75,14 @@ func setup(
chDesc := &p2p.ChannelDescriptor{ID: BlockSyncChannel, MessageType: new(bcproto.Message)}
rts.blockSyncChannels = rts.network.MakeChannelsNoCleanup(ctx, t, chDesc)
i := 0
for nodeID := range rts.network.Nodes {
rts.addNode(ctx, t, nodeID, genDoc, privVal, maxBlockHeights[i])
i++
if maxBlockHeights[1] != 0 {
rts.addMultipleNodes(ctx, t, rts.network.NodeIDs(), genDoc, privValArray, maxBlockHeights, 0)
} else {
i := 0
for nodeID := range rts.network.Nodes {
rts.addNode(ctx, t, nodeID, genDoc, privValArray, maxBlockHeights[i])
i++
}
}
t.Cleanup(func() {
@@ -97,12 +101,151 @@ func setup(
return rts
}
// We add multiple nodes with varying initial heights
// Allows us to test whether block sync works when a node
// has previous state
// maxBlockHeightPerNode - the heights for which the node already has state
// maxBlockHeightIdx - the index of the node with maximum height
func (rts *reactorTestSuite) addMultipleNodes(
ctx context.Context,
t *testing.T,
nodeIDs []types.NodeID,
genDoc *types.GenesisDoc,
privValArray []types.PrivValidator,
maxBlockHeightPerNode []int64,
maxBlockHeightIdx int64,
) {
t.Helper()
logger := log.NewNopLogger()
blockDB := make([]*dbm.MemDB, len(nodeIDs))
stateDB := make([]*dbm.MemDB, len(nodeIDs))
blockExecutors := make([]*sm.BlockExecutor, len(nodeIDs))
blockStores := make([]*store.BlockStore, len(nodeIDs))
stateStores := make([]sm.Store, len(nodeIDs))
state, err := sm.MakeGenesisState(genDoc)
require.NoError(t, err)
for idx, nodeID := range nodeIDs {
rts.nodes = append(rts.nodes, nodeID)
rts.app[nodeID] = proxy.New(abciclient.NewLocalClient(logger, &abci.BaseApplication{}), logger, proxy.NopMetrics())
require.NoError(t, rts.app[nodeID].Start(ctx))
stateDB[idx] = dbm.NewMemDB()
stateStores[idx] = sm.NewStore(stateDB[idx])
blockDB[idx] = dbm.NewMemDB()
blockStores[idx] = store.NewBlockStore(blockDB[idx])
require.NoError(t, stateStores[idx].Save(state))
mp := &mpmocks.Mempool{}
mp.On("Lock").Return()
mp.On("Unlock").Return()
mp.On("FlushAppConn", mock.Anything).Return(nil)
mp.On("Update",
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything).Return(nil)
eventbus := eventbus.NewDefault(logger)
require.NoError(t, eventbus.Start(ctx))
blockExecutors[idx] = sm.NewBlockExecutor(stateStores[idx],
log.NewNopLogger(),
rts.app[nodeID],
mp,
sm.EmptyEvidencePool{},
blockStores[idx],
eventbus,
sm.NopMetrics(),
)
}
for blockHeight := int64(1); blockHeight <= maxBlockHeightPerNode[maxBlockHeightIdx]; blockHeight++ {
lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil)
if blockHeight > 1 {
lastBlockMeta := blockStores[maxBlockHeightIdx].LoadBlockMeta(blockHeight - 1)
lastBlock := blockStores[maxBlockHeightIdx].LoadBlock(blockHeight - 1)
commitSigs := make([]types.CommitSig, len(privValArray))
votes := make([]types.Vote, len(privValArray))
for i, val := range privValArray {
vote, err := factory.MakeVote(
ctx,
val,
lastBlock.Header.ChainID, 0,
lastBlock.Header.Height, 0, 2,
lastBlockMeta.BlockID,
time.Now(),
)
require.NoError(t, err)
votes[i] = *vote
commitSigs[i] = vote.CommitSig()
}
lastCommit = types.NewCommit(
votes[0].Height,
votes[0].Round,
lastBlockMeta.BlockID,
commitSigs,
)
}
thisBlock := sf.MakeBlock(state, blockHeight, lastCommit)
thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes)
require.NoError(t, err)
blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
for idx := range nodeIDs {
if blockHeight <= maxBlockHeightPerNode[idx] {
lastState, err := stateStores[idx].Load()
require.NoError(t, err)
state, err = blockExecutors[idx].ApplyBlock(ctx, lastState, blockID, thisBlock)
require.NoError(t, err)
blockStores[idx].SaveBlock(thisBlock, thisParts, lastCommit)
}
}
}
for idx, nodeID := range nodeIDs {
rts.peerChans[nodeID] = make(chan p2p.PeerUpdate)
rts.peerUpdates[nodeID] = p2p.NewPeerUpdates(rts.peerChans[nodeID], 1)
rts.network.Nodes[nodeID].PeerManager.Register(ctx, rts.peerUpdates[nodeID])
chCreator := func(ctx context.Context, chdesc *p2p.ChannelDescriptor) (*p2p.Channel, error) {
return rts.blockSyncChannels[nodeID], nil
}
rts.reactors[nodeID] = NewReactor(
rts.logger.With("nodeID", nodeID),
stateStores[idx],
blockExecutors[idx],
blockStores[idx],
nil,
chCreator,
func(ctx context.Context) *p2p.PeerUpdates { return rts.peerUpdates[nodeID] },
rts.blockSync,
consensus.NopMetrics(),
nil, // eventbus, can be nil
)
require.NoError(t, rts.reactors[nodeID].Start(ctx))
require.True(t, rts.reactors[nodeID].IsRunning())
}
}
func (rts *reactorTestSuite) addNode(
ctx context.Context,
t *testing.T,
nodeID types.NodeID,
genDoc *types.GenesisDoc,
privVal types.PrivValidator,
privValArray []types.PrivValidator,
maxBlockHeight int64,
) {
t.Helper()
@@ -154,21 +297,28 @@ func (rts *reactorTestSuite) addNode(
lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
lastBlock := blockStore.LoadBlock(blockHeight - 1)
vote, err := factory.MakeVote(
ctx,
privVal,
lastBlock.Header.ChainID, 0,
lastBlock.Header.Height, 0, 2,
lastBlockMeta.BlockID,
time.Now(),
)
require.NoError(t, err)
commitSigs := make([]types.CommitSig, len(privValArray))
votes := make([]types.Vote, len(privValArray))
for i, val := range privValArray {
vote, err := factory.MakeVote(
ctx,
val,
lastBlock.Header.ChainID, 0,
lastBlock.Header.Height, 0, 2,
lastBlockMeta.BlockID,
time.Now(),
)
require.NoError(t, err)
votes[i] = *vote
commitSigs[i] = vote.CommitSig()
}
lastCommit = types.NewCommit(
vote.Height,
vote.Round,
votes[0].Height,
votes[0].Round,
lastBlockMeta.BlockID,
[]types.CommitSig{vote.CommitSig()},
commitSigs,
)
}
thisBlock := sf.MakeBlock(state, blockHeight, lastCommit)
@@ -227,7 +377,7 @@ func TestReactor_AbruptDisconnect(t *testing.T) {
genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams())
maxBlockHeight := int64(64)
rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0})
rts := setup(ctx, t, genDoc, privVals, []int64{maxBlockHeight, 0})
require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height())
@@ -255,6 +405,51 @@ func TestReactor_AbruptDisconnect(t *testing.T) {
rts.network.Nodes[rts.nodes[1]].PeerManager.Disconnected(ctx, rts.nodes[0])
}
//@jmalicevic ToDO
// a) Add tests that verify whether faulty peer is properly detected
// 1. block at H + 1 is faulty
// 2. block at H + 2 is faulty (the validator set does not match)
// b) Add test to verify we replace a peer with a new one if we detect misbehavior
func TestReactor_NonGenesisSync(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cfg, err := config.ResetTestRoot(t.TempDir(), "block_sync_reactor_test")
require.NoError(t, err)
defer os.RemoveAll(cfg.RootDir)
valSet, privVals := factory.ValidatorSet(ctx, t, 4, 30)
genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams())
maxBlockHeight := int64(101)
rts := setup(ctx, t, genDoc, privVals, []int64{maxBlockHeight, 2, 0}) //50, 4, 0})
require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height())
rts.start(ctx, t)
require.Eventually(
t,
func() bool {
matching := true
for idx := range rts.nodes {
if idx == 0 {
continue
}
matching = matching && rts.reactors[rts.nodes[idx]].GetRemainingSyncTime() > time.Nanosecond &&
rts.reactors[rts.nodes[idx]].pool.getLastSyncRate() > 0.001
if !matching {
height, _, _ := rts.reactors[rts.nodes[idx]].pool.GetStatus()
t.Logf("%d %d %s %f", height, idx, rts.reactors[rts.nodes[idx]].GetRemainingSyncTime(), rts.reactors[rts.nodes[idx]].pool.getLastSyncRate())
}
}
return matching
},
10*time.Second,
10*time.Millisecond,
"expected node to be partially synced",
)
}
func TestReactor_SyncTime(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -263,18 +458,17 @@ func TestReactor_SyncTime(t *testing.T) {
require.NoError(t, err)
defer os.RemoveAll(cfg.RootDir)
valSet, privVals := factory.ValidatorSet(ctx, t, 1, 30)
valSet, privVals := factory.ValidatorSet(ctx, t, 4, 30)
genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams())
maxBlockHeight := int64(101)
rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0})
rts := setup(ctx, t, genDoc, privVals, []int64{maxBlockHeight, 0})
require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height())
rts.start(ctx, t)
require.Eventually(
t,
func() bool {
//t.Logf("%d %d %s", rts.reactors[rts.nodes[1]].pool.height, rts.reactors[rts.nodes[0]].pool.height, rts.reactors[rts.nodes[1]].GetRemainingSyncTime())
return rts.reactors[rts.nodes[1]].GetRemainingSyncTime() > time.Nanosecond &&
rts.reactors[rts.nodes[1]].pool.getLastSyncRate() > 0.001
},
@@ -296,7 +490,7 @@ func TestReactor_NoBlockResponse(t *testing.T) {
genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams())
maxBlockHeight := int64(65)
rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0})
rts := setup(ctx, t, genDoc, privVals, []int64{maxBlockHeight, 0})
require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height())
@@ -315,10 +509,7 @@ func TestReactor_NoBlockResponse(t *testing.T) {
secondaryPool := rts.reactors[rts.nodes[1]].pool
require.Eventually(
t,
func() bool {
t.Logf("%d %d", secondaryPool.MaxPeerHeight(), secondaryPool.height)
return secondaryPool.MaxPeerHeight() > 0 && secondaryPool.IsCaughtUp()
},
func() bool { return secondaryPool.MaxPeerHeight() > 0 && secondaryPool.IsCaughtUp() },
10*time.Second,
10*time.Millisecond,
"expected node to be fully synced",
@@ -351,7 +542,7 @@ func TestReactor_BadBlockStopsPeer(t *testing.T) {
valSet, privVals := factory.ValidatorSet(ctx, t, 1, 30)
genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams())
rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0, 0, 0, 0})
rts := setup(ctx, t, genDoc, privVals, []int64{maxBlockHeight, 0, 0, 0, 0})
require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height())
@@ -389,7 +580,7 @@ func TestReactor_BadBlockStopsPeer(t *testing.T) {
MaxPeers: uint16(len(rts.nodes) + 1),
MaxConnected: uint16(len(rts.nodes) + 1),
})
rts.addNode(ctx, t, newNode.NodeID, otherGenDoc, otherPrivVals[0], maxBlockHeight)
rts.addNode(ctx, t, newNode.NodeID, otherGenDoc, otherPrivVals, maxBlockHeight)
// add a fake peer just so we do not wait for the consensus ticker to timeout
rts.reactors[newNode.NodeID].pool.SetPeerRange("00ff", 10, 10)
+4 -2
View File
@@ -82,7 +82,8 @@ func (e ErrValidationFailed) Error() string {
return "failed to verify next block"
}
func VerifyNextBlock(newBlock *types.Block, newBlockID types.BlockID, verifyBlock *types.Block, trustedBlock *types.Block, trustedCommit *types.Commit, validators *types.ValidatorSet) error {
func VerifyNextBlock(newBlock *types.Block, newBlockID types.BlockID, verifyBlock *types.Block, trustedBlock *types.Block,
trustedCommit *types.Commit, validators *types.ValidatorSet) error {
// If the blockID in LastCommit of NewBlock does not match the trusted block
// we can assume NewBlock is not correct
@@ -99,7 +100,8 @@ func VerifyNextBlock(newBlock *types.Block, newBlockID types.BlockID, verifyBloc
// Verify NewBlock usign the validator set obtained after applying the last block
// Note: VerifyAdjacent in the LightClient relies on a trusting period which is not applicable here
// ToDo: We need witness verification here as well and backwards verification from a state where we can trust validators
if err := VerifyAdjacent(&types.SignedHeader{Header: &trustedBlock.Header, Commit: trustedCommit}, &types.SignedHeader{Header: &newBlock.Header, Commit: verifyBlock.LastCommit}, validators); err != nil {
if err := VerifyAdjacent(&types.SignedHeader{Header: &trustedBlock.Header, Commit: trustedCommit},
&types.SignedHeader{Header: &newBlock.Header, Commit: verifyBlock.LastCommit}, validators); err != nil {
return ErrValidationFailed{Reason: err}
}
+3 -2
View File
@@ -68,7 +68,8 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
ensureDir(t, path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
app := kvstore.NewApplication()
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
_, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
blockDB := dbm.NewMemDB()
blockStore := store.NewBlockStore(blockDB)
@@ -203,7 +204,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
proposerAddr := lazyNodeState.privValidatorPubKey.Address()
block, err := lazyNodeState.blockExec.CreateProposalBlock(
ctx, lazyNodeState.Height, lazyNodeState.state, commit, proposerAddr, nil)
ctx, lazyNodeState.Height, lazyNodeState.state, commit, proposerAddr, lazyNodeState.LastCommit.GetVotes())
require.NoError(t, err)
blockParts, err := block.MakePartSet(types.BlockPartSizeBytes)
require.NoError(t, err)
+37 -30
View File
@@ -12,6 +12,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
@@ -112,7 +113,8 @@ func (vs *validatorStub) signVote(
ctx context.Context,
voteType tmproto.SignedMsgType,
chainID string,
blockID types.BlockID) (*types.Vote, error) {
blockID types.BlockID,
voteExtension []byte) (*types.Vote, error) {
pubKey, err := vs.PrivValidator.GetPubKey(ctx)
if err != nil {
@@ -120,28 +122,30 @@ func (vs *validatorStub) signVote(
}
vote := &types.Vote{
ValidatorIndex: vs.Index,
ValidatorAddress: pubKey.Address(),
Type: voteType,
Height: vs.Height,
Round: vs.Round,
Timestamp: vs.clock.Now(),
Type: voteType,
BlockID: blockID,
VoteExtension: types.VoteExtensionFromProto(kvstore.ConstructVoteExtension(pubKey.Address())),
Timestamp: vs.clock.Now(),
ValidatorAddress: pubKey.Address(),
ValidatorIndex: vs.Index,
Extension: voteExtension,
}
v := vote.ToProto()
if err := vs.PrivValidator.SignVote(ctx, chainID, v); err != nil {
if err = vs.PrivValidator.SignVote(ctx, chainID, v); err != nil {
return nil, fmt.Errorf("sign vote failed: %w", err)
}
// ref: signVote in FilePV, the vote should use the privious vote info when the sign data is the same.
// ref: signVote in FilePV, the vote should use the previous vote info when the sign data is the same.
if signDataIsEqual(vs.lastVote, v) {
v.Signature = vs.lastVote.Signature
v.Timestamp = vs.lastVote.Timestamp
v.ExtensionSignature = vs.lastVote.ExtensionSignature
}
vote.Signature = v.Signature
vote.Timestamp = v.Timestamp
vote.ExtensionSignature = v.ExtensionSignature
return vote, err
}
@@ -155,13 +159,13 @@ func signVote(
chainID string,
blockID types.BlockID) *types.Vote {
v, err := vs.signVote(ctx, voteType, chainID, blockID)
var ext []byte
if voteType == tmproto.PrecommitType {
ext = []byte("extension")
}
v, err := vs.signVote(ctx, voteType, chainID, blockID, ext)
require.NoError(t, err, "failed to sign vote")
// TODO: remove hardcoded vote extension.
// currently set for abci/examples/kvstore/persistent_kvstore.go
v.VoteExtension = types.VoteExtensionFromProto(kvstore.ConstructVoteExtension(v.ValidatorAddress))
vs.lastVote = v
return v
@@ -351,18 +355,19 @@ func validatePrecommit(
require.True(t, bytes.Equal(vote.BlockID.Hash, votedBlockHash), "Expected precommit to be for proposal block")
}
rs := cs.GetRoundState()
if lockedBlockHash == nil {
require.False(t, cs.LockedRound != lockRound || cs.LockedBlock != nil,
require.False(t, rs.LockedRound != lockRound || rs.LockedBlock != nil,
"Expected to be locked on nil at round %d. Got locked at round %d with block %v",
lockRound,
cs.LockedRound,
cs.LockedBlock)
rs.LockedRound,
rs.LockedBlock)
} else {
require.False(t, cs.LockedRound != lockRound || !bytes.Equal(cs.LockedBlock.Hash(), lockedBlockHash),
require.False(t, rs.LockedRound != lockRound || !bytes.Equal(rs.LockedBlock.Hash(), lockedBlockHash),
"Expected block to be locked on round %d, got %d. Got locked block %X, expected %X",
lockRound,
cs.LockedRound,
cs.LockedBlock.Hash(),
rs.LockedRound,
rs.LockedBlock.Hash(),
lockedBlockHash)
}
}
@@ -730,10 +735,9 @@ func ensureVoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64,
msg.Data())
vote := voteEvent.Vote
require.Equal(t, height, vote.Height)
require.Equal(t, round, vote.Round)
require.Equal(t, voteType, vote.Type)
assert.Equal(t, height, vote.Height, "expected height %d, but got %d", height, vote.Height)
assert.Equal(t, round, vote.Round, "expected round %d, but got %d", round, vote.Round)
assert.Equal(t, voteType, vote.Type, "expected type %s, but got %s", voteType, vote.Type)
if hash == nil {
require.Nil(t, vote.BlockID.Hash, "Expected prevote to be for nil, got %X", vote.BlockID.Hash)
} else {
@@ -741,6 +745,7 @@ func ensureVoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64,
}
}
}
func ensureVote(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32, voteType tmproto.SignedMsgType) {
t.Helper()
msg := ensureMessageBeforeTimeout(t, voteCh, ensureTimeout)
@@ -749,10 +754,9 @@ func ensureVote(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, roun
msg.Data())
vote := voteEvent.Vote
require.Equal(t, height, vote.Height)
require.Equal(t, round, vote.Round)
require.Equal(t, voteType, vote.Type)
require.Equal(t, height, vote.Height, "expected height %d, but got %d", height, vote.Height)
require.Equal(t, round, vote.Round, "expected round %d, but got %d", round, vote.Round)
require.Equal(t, voteType, vote.Type, "expected type %s, but got %s", voteType, vote.Type)
}
func ensureNewEventOnChannel(t *testing.T, ch <-chan tmpubsub.Message) {
@@ -821,7 +825,8 @@ func makeConsensusState(
closeFuncs = append(closeFuncs, app.Close)
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
_, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
l := logger.With("validator", i, "module", "consensus")
css[i] = newStateWithConfigAndBlockStore(ctx, t, l, thisConfig, state, privVals[i], app, blockStore)
@@ -892,7 +897,8 @@ func randConsensusNetWithPeers(
case *kvstore.Application:
state.Version.Consensus.App = kvstore.ProtocolVersion
}
app.InitChain(abci.RequestInitChain{Validators: vals})
_, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
// sm.SaveState(stateDB,state) //height 1's validatorsInfo already saved in LoadStateFromDBOrGenesisDoc above
css[i] = newStateWithConfig(ctx, t, logger.With("validator", i, "module", "consensus"), thisConfig, state, privVal, app)
@@ -986,5 +992,6 @@ func signDataIsEqual(v1 *types.Vote, v2 *tmproto.Vote) bool {
v1.Height == v2.GetHeight() &&
v1.Round == v2.Round &&
bytes.Equal(v1.ValidatorAddress.Bytes(), v2.GetValidatorAddress()) &&
v1.ValidatorIndex == v2.GetValidatorIndex()
v1.ValidatorIndex == v2.GetValidatorIndex() &&
bytes.Equal(v1.Extension, v2.Extension)
}
+20 -20
View File
@@ -199,10 +199,12 @@ func TestMempoolRmBadTx(t *testing.T) {
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(0))
resFinalize := app.FinalizeBlock(abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}})
resFinalize, err := app.FinalizeBlock(ctx, &abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}})
require.NoError(t, err)
assert.False(t, resFinalize.TxResults[0].IsErr(), fmt.Sprintf("expected no error. got %v", resFinalize))
resCommit := app.Commit()
resCommit, err := app.Commit(ctx)
require.NoError(t, err)
assert.True(t, len(resCommit.Data) > 0)
emptyMempoolCh := make(chan struct{})
@@ -267,11 +269,11 @@ func NewCounterApplication() *CounterApplication {
return &CounterApplication{}
}
func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo {
return abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}
func (app *CounterApplication) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) {
return &abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}, nil
}
func (app *CounterApplication) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock {
func (app *CounterApplication) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
respTxs := make([]*abci.ExecTxResult, len(req.Txs))
for i, tx := range req.Txs {
txValue := txAsUint64(tx)
@@ -285,18 +287,19 @@ func (app *CounterApplication) FinalizeBlock(req abci.RequestFinalizeBlock) abci
app.txCount++
respTxs[i] = &abci.ExecTxResult{Code: code.CodeTypeOK}
}
return abci.ResponseFinalizeBlock{TxResults: respTxs}
return &abci.ResponseFinalizeBlock{TxResults: respTxs}, nil
}
func (app *CounterApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
func (app *CounterApplication) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) {
txValue := txAsUint64(req.Tx)
if txValue != uint64(app.mempoolTxCount) {
return abci.ResponseCheckTx{
return &abci.ResponseCheckTx{
Code: code.CodeTypeBadNonce,
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue)}
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue),
}, nil
}
app.mempoolTxCount++
return abci.ResponseCheckTx{Code: code.CodeTypeOK}
return &abci.ResponseCheckTx{Code: code.CodeTypeOK}, nil
}
func txAsUint64(tx []byte) uint64 {
@@ -305,19 +308,17 @@ func txAsUint64(tx []byte) uint64 {
return binary.BigEndian.Uint64(tx8)
}
func (app *CounterApplication) Commit() abci.ResponseCommit {
func (app *CounterApplication) Commit(context.Context) (*abci.ResponseCommit, error) {
app.mempoolTxCount = app.txCount
if app.txCount == 0 {
return abci.ResponseCommit{}
return &abci.ResponseCommit{}, nil
}
hash := make([]byte, 8)
binary.BigEndian.PutUint64(hash, uint64(app.txCount))
return abci.ResponseCommit{Data: hash}
return &abci.ResponseCommit{Data: hash}, nil
}
func (app *CounterApplication) PrepareProposal(
req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
func (app *CounterApplication) PrepareProposal(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
trs := make([]*abci.TxRecord, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
@@ -330,10 +331,9 @@ func (app *CounterApplication) PrepareProposal(
Tx: tx,
})
}
return abci.ResponsePrepareProposal{TxRecords: trs}
return &abci.ResponsePrepareProposal{TxRecords: trs}, nil
}
func (app *CounterApplication) ProcessProposal(
req abci.RequestProcessProposal) abci.ResponseProcessProposal {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
func (app *CounterApplication) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}
+105
View File
@@ -8,6 +8,7 @@ import (
"github.com/go-kit/kit/metrics/discard"
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types"
prometheus "github.com/go-kit/kit/metrics/prometheus"
@@ -103,6 +104,33 @@ type Metrics struct {
// the proposal message and the local time of the validator at the time
// that the validator received the message.
ProposalTimestampDifference metrics.Histogram
// VoteExtensionReceiveCount is the number of vote extensions received by this
// node. The metric is annotated by the status of the vote extension from the
// application, either 'accepted' or 'rejected'.
VoteExtensionReceiveCount metrics.Counter
// ProposalReceiveCount is the total number of proposals received by this node
// since process start.
// The metric is annotated by the status of the proposal from the application,
// either 'accepted' or 'rejected'.
ProposalReceiveCount metrics.Counter
// ProposalCreationCount is the total number of proposals created by this node
// since process start.
// The metric is annotated by the status of the proposal from the application,
// either 'accepted' or 'rejected'.
ProposalCreateCount metrics.Counter
// RoundVotingPowerPercent is the percentage of the total voting power received
// with a round. The value begins at 0 for each round and approaches 1.0 as
// additional voting power is observed. The metric is labeled by vote type.
RoundVotingPowerPercent metrics.Gauge
// LateVotes stores the number of votes that were received by this node that
// correspond to earlier heights and rounds than this node is currently
// in.
LateVotes metrics.Counter
}
// PrometheusMetrics returns Metrics build using Prometheus client library.
@@ -280,6 +308,43 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
"Only calculated when a new block is proposed.",
Buckets: []float64{-10, -.5, -.025, 0, .1, .5, 1, 1.5, 2, 10},
}, append(labels, "is_timely")).With(labelsAndValues...),
VoteExtensionReceiveCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "vote_extension_receive_count",
Help: "Number of vote extensions received by the node since process start, labeled by " +
"the application's response to VerifyVoteExtension, either accept or reject.",
}, append(labels, "status")).With(labelsAndValues...),
ProposalReceiveCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "proposal_receive_count",
Help: "Number of vote proposals received by the node since process start, labeled by " +
"the application's response to ProcessProposal, either accept or reject.",
}, append(labels, "status")).With(labelsAndValues...),
ProposalCreateCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "proposal_create_count",
Help: "Number of proposals created by the node since process start.",
}, labels).With(labelsAndValues...),
RoundVotingPowerPercent: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "round_voting_power_percent",
Help: "Percentage of the total voting power received with a round. " +
"The value begins at 0 for each round and approaches 1.0 as additional " +
"voting power is observed.",
}, append(labels, "vote_type")).With(labelsAndValues...),
LateVotes: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "late_votes",
Help: "Number of votes received by the node since process start that correspond to earlier heights and rounds than this node is currently in.",
}, append(labels, "vote_type")).With(labelsAndValues...),
}
}
@@ -317,6 +382,11 @@ func NopMetrics() *Metrics {
QuorumPrevoteDelay: discard.NewGauge(),
FullPrevoteDelay: discard.NewGauge(),
ProposalTimestampDifference: discard.NewHistogram(),
VoteExtensionReceiveCount: discard.NewCounter(),
ProposalReceiveCount: discard.NewCounter(),
ProposalCreateCount: discard.NewCounter(),
RoundVotingPowerPercent: discard.NewGauge(),
LateVotes: discard.NewCounter(),
}
}
@@ -336,10 +406,45 @@ func (m *Metrics) MarkBlockGossipComplete() {
m.BlockGossipReceiveLatency.Observe(time.Since(m.blockGossipStart).Seconds())
}
func (m *Metrics) MarkProposalProcessed(accepted bool) {
status := "accepted"
if !accepted {
status = "rejected"
}
m.ProposalReceiveCount.With("status", status).Add(1)
}
func (m *Metrics) MarkVoteExtensionReceived(accepted bool) {
status := "accepted"
if !accepted {
status = "rejected"
}
m.VoteExtensionReceiveCount.With("status", status).Add(1)
}
func (m *Metrics) MarkVoteReceived(vt tmproto.SignedMsgType, power, totalPower int64) {
p := float64(power) / float64(totalPower)
n := strings.ToLower(strings.TrimPrefix(vt.String(), "SIGNED_MSG_TYPE_"))
m.RoundVotingPowerPercent.With("vote_type", n).Add(p)
}
func (m *Metrics) MarkRound(r int32, st time.Time) {
m.Rounds.Set(float64(r))
roundTime := time.Since(st).Seconds()
m.RoundDuration.Observe(roundTime)
pvt := tmproto.PrevoteType
pvn := strings.ToLower(strings.TrimPrefix(pvt.String(), "SIGNED_MSG_TYPE_"))
m.RoundVotingPowerPercent.With("vote_type", pvn).Set(0)
pct := tmproto.PrecommitType
pcn := strings.ToLower(strings.TrimPrefix(pct.String(), "SIGNED_MSG_TYPE_"))
m.RoundVotingPowerPercent.With("vote_type", pcn).Set(0)
}
func (m *Metrics) MarkLateVote(vt tmproto.SignedMsgType) {
n := strings.ToLower(strings.TrimPrefix(vt.String(), "SIGNED_MSG_TYPE_"))
m.LateVotes.With("vote_type", n).Add(1)
}
func (m *Metrics) MarkStep(s cstypes.RoundStepType) {
@@ -3,6 +3,8 @@
package mocks
import (
testing "testing"
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/internal/state"
)
@@ -26,3 +28,13 @@ func (_m *ConsSyncReactor) SetStateSyncingMetrics(_a0 float64) {
func (_m *ConsSyncReactor) SwitchToConsensus(_a0 state.State, _a1 bool) {
_m.Called(_a0, _a1)
}
// NewConsSyncReactor creates a new instance of ConsSyncReactor. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewConsSyncReactor(t testing.TB) *ConsSyncReactor {
mock := &ConsSyncReactor{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+8 -2
View File
@@ -220,9 +220,13 @@ type VoteMessage struct {
func (*VoteMessage) TypeTag() string { return "tendermint/Vote" }
// ValidateBasic performs basic validation.
// ValidateBasic checks whether the vote within the message is well-formed.
func (m *VoteMessage) ValidateBasic() error {
return m.Vote.ValidateBasic()
// Here we validate votes with vote extensions, since we require vote
// extensions to be sent in precommit messages during consensus. Prevote
// messages should never have vote extensions, and this is also validated
// here.
return m.Vote.ValidateWithExtension()
}
// String returns a string representation.
@@ -534,6 +538,8 @@ func MsgFromProto(msg *tmcons.Message) (Message, error) {
Part: parts,
}
case *tmcons.Message_Vote:
// Vote validation will be handled in the vote message ValidateBasic
// call below.
vote, err := types.VoteFromProto(msg.Vote.Vote)
if err != nil {
return nil, fmt.Errorf("vote msg to proto error: %w", err)
+4 -9
View File
@@ -12,8 +12,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/crypto/tmhash"
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
"github.com/tendermint/tendermint/internal/test/factory"
"github.com/tendermint/tendermint/libs/bits"
@@ -357,11 +357,6 @@ func TestConsMsgsVectors(t *testing.T) {
}
pbProposal := proposal.ToProto()
ext := types.VoteExtension{
AppDataToSign: []byte("signed"),
AppDataSelfAuthenticating: []byte("auth"),
}
v := &types.Vote{
ValidatorAddress: []byte("add_more_exclamation"),
ValidatorIndex: 1,
@@ -370,7 +365,7 @@ func TestConsMsgsVectors(t *testing.T) {
Timestamp: date,
Type: tmproto.PrecommitType,
BlockID: bi,
VoteExtension: ext,
Extension: []byte("extension"),
}
vpb := v.ToProto()
@@ -407,7 +402,7 @@ func TestConsMsgsVectors(t *testing.T) {
"2a36080110011a3008011204746573741a26080110011a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d"},
{"Vote", &tmcons.Message{Sum: &tmcons.Message_Vote{
Vote: &tmcons.Vote{Vote: vpb}}},
"3280010a7e0802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a0608c0b89fdc0532146164645f6d6f72655f6578636c616d6174696f6e38014a0e0a067369676e6564120461757468"},
"327b0a790802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a0608c0b89fdc0532146164645f6d6f72655f6578636c616d6174696f6e38014a09657874656e73696f6e"},
{"HasVote", &tmcons.Message{Sum: &tmcons.Message_HasVote{
HasVote: &tmcons.HasVote{Height: 1, Round: 1, Type: tmproto.PrevoteType, Index: 1}}},
"3a080801100118012001"},
@@ -672,7 +667,7 @@ func TestProposalPOLMessageValidateBasic(t *testing.T) {
func TestBlockPartMessageValidateBasic(t *testing.T) {
testPart := new(types.Part)
testPart.Proof.LeafHash = tmhash.Sum([]byte("leaf"))
testPart.Proof.LeafHash = crypto.Checksum([]byte("leaf"))
testCases := []struct {
testName string
messageHeight int64
+15 -14
View File
@@ -1266,7 +1266,7 @@ func (r *Reactor) handleVoteSetBitsMessage(ctx context.Context, envelope *p2p.En
// the p2p channel.
//
// NOTE: We block on consensus state for proposals, block parts, and votes.
func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelope *p2p.Envelope, chans channelBundle) (err error) {
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, chans channelBundle) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("panic in processing message: %v", e)
@@ -1284,18 +1284,19 @@ func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelop
// and because a large part of the core business logic depends on these
// domain types opposed to simply working with the Proto types.
protoMsg := new(tmcons.Message)
if err := protoMsg.Wrap(envelope.Message); err != nil {
if err = protoMsg.Wrap(envelope.Message); err != nil {
return err
}
msgI, err := MsgFromProto(protoMsg)
var msgI Message
msgI, err = MsgFromProto(protoMsg)
if err != nil {
return err
}
r.logger.Debug("received message", "ch_id", chID, "message", msgI, "peer", envelope.From)
r.logger.Debug("received message", "ch_id", envelope.ChannelID, "message", msgI, "peer", envelope.From)
switch chID {
switch envelope.ChannelID {
case StateChannel:
err = r.handleStateMessage(ctx, envelope, msgI, chans.votSet)
case DataChannel:
@@ -1305,7 +1306,7 @@ func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelop
case VoteSetBitsChannel:
err = r.handleVoteSetBitsMessage(ctx, envelope, msgI)
default:
err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", chID, envelope)
err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", envelope.ChannelID, envelope)
}
return err
@@ -1320,8 +1321,8 @@ func (r *Reactor) processStateCh(ctx context.Context, chans channelBundle) {
iter := chans.state.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
if err := r.handleMessage(ctx, chans.state.ID, envelope, chans); err != nil {
r.logger.Error("failed to process message", "ch_id", chans.state.ID, "envelope", envelope, "err", err)
if err := r.handleMessage(ctx, envelope, chans); err != nil {
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := chans.state.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
@@ -1341,8 +1342,8 @@ func (r *Reactor) processDataCh(ctx context.Context, chans channelBundle) {
iter := chans.data.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
if err := r.handleMessage(ctx, chans.data.ID, envelope, chans); err != nil {
r.logger.Error("failed to process message", "ch_id", chans.data.ID, "envelope", envelope, "err", err)
if err := r.handleMessage(ctx, envelope, chans); err != nil {
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := chans.data.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
@@ -1362,8 +1363,8 @@ func (r *Reactor) processVoteCh(ctx context.Context, chans channelBundle) {
iter := chans.vote.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
if err := r.handleMessage(ctx, chans.vote.ID, envelope, chans); err != nil {
r.logger.Error("failed to process message", "ch_id", chans.vote.ID, "envelope", envelope, "err", err)
if err := r.handleMessage(ctx, envelope, chans); err != nil {
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := chans.vote.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
@@ -1384,12 +1385,12 @@ func (r *Reactor) processVoteSetBitsCh(ctx context.Context, chans channelBundle)
for iter.Next(ctx) {
envelope := iter.Envelope()
if err := r.handleMessage(ctx, chans.votSet.ID, envelope, chans); err != nil {
if err := r.handleMessage(ctx, envelope, chans); err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return
}
r.logger.Error("failed to process message", "ch_id", chans.votSet.ID, "envelope", envelope, "err", err)
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := chans.votSet.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
+24 -55
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"math/rand"
"os"
"sync"
"testing"
@@ -466,7 +467,8 @@ func TestReactorWithEvidence(t *testing.T) {
app := kvstore.NewApplication()
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
_, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
pv := privVals[i]
blockDB := dbm.NewMemDB()
@@ -775,8 +777,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
cfg := configSetup(t)
nPeers := 7
nVals := 4
nPeers := 4
nVals := 2
states, _, _, cleanup := randConsensusNetWithPeers(
ctx,
t,
@@ -869,60 +871,27 @@ func TestReactorValidatorSetChanges(t *testing.T) {
// it includes the commit for block 4, which should have the updated validator set
waitForBlockWithUpdatedValsAndValidateIt(ctx, t, nPeers, activeVals, blocksSubs, states)
updateValidatorPubKey1, err := states[nVals].privValidator.GetPubKey(ctx)
require.NoError(t, err)
for i := 2; i <= 32; i *= 2 {
useState := rand.Intn(nVals)
t.Log(useState)
updateValidatorPubKey1, err := states[useState].privValidator.GetPubKey(ctx)
require.NoError(t, err)
updatePubKey1ABCI, err := encoding.PubKeyToProto(updateValidatorPubKey1)
require.NoError(t, err)
updatePubKey1ABCI, err := encoding.PubKeyToProto(updateValidatorPubKey1)
require.NoError(t, err)
updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25)
previousTotalVotingPower := states[nVals].GetRoundState().LastValidators.TotalVotingPower()
previousTotalVotingPower := states[useState].GetRoundState().LastValidators.TotalVotingPower()
updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, int64(i))
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states, updateValidatorTx1)
waitForAndValidateBlockWithTx(ctx, t, nPeers, activeVals, blocksSubs, states, updateValidatorTx1)
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states)
waitForBlockWithUpdatedValsAndValidateIt(ctx, t, nPeers, activeVals, blocksSubs, states)
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states, updateValidatorTx1)
waitForAndValidateBlockWithTx(ctx, t, nPeers, activeVals, blocksSubs, states, updateValidatorTx1)
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states)
waitForBlockWithUpdatedValsAndValidateIt(ctx, t, nPeers, activeVals, blocksSubs, states)
require.NotEqualf(
t, states[nVals].GetRoundState().LastValidators.TotalVotingPower(), previousTotalVotingPower,
"expected voting power to change (before: %d, after: %d)",
previousTotalVotingPower, states[nVals].GetRoundState().LastValidators.TotalVotingPower(),
)
newValidatorPubKey2, err := states[nVals+1].privValidator.GetPubKey(ctx)
require.NoError(t, err)
newVal2ABCI, err := encoding.PubKeyToProto(newValidatorPubKey2)
require.NoError(t, err)
newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower)
newValidatorPubKey3, err := states[nVals+2].privValidator.GetPubKey(ctx)
require.NoError(t, err)
newVal3ABCI, err := encoding.PubKeyToProto(newValidatorPubKey3)
require.NoError(t, err)
newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower)
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states, newValidatorTx2, newValidatorTx3)
waitForAndValidateBlockWithTx(ctx, t, nPeers, activeVals, blocksSubs, states, newValidatorTx2, newValidatorTx3)
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states)
activeVals[string(newValidatorPubKey2.Address())] = struct{}{}
activeVals[string(newValidatorPubKey3.Address())] = struct{}{}
waitForBlockWithUpdatedValsAndValidateIt(ctx, t, nPeers, activeVals, blocksSubs, states)
removeValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, 0)
removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0)
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states, removeValidatorTx2, removeValidatorTx3)
waitForAndValidateBlockWithTx(ctx, t, nPeers, activeVals, blocksSubs, states, removeValidatorTx2, removeValidatorTx3)
waitForAndValidateBlock(ctx, t, nPeers, activeVals, blocksSubs, states)
delete(activeVals, string(newValidatorPubKey2.Address()))
delete(activeVals, string(newValidatorPubKey3.Address()))
waitForBlockWithUpdatedValsAndValidateIt(ctx, t, nPeers, activeVals, blocksSubs, states)
require.NotEqualf(
t, states[useState].GetRoundState().LastValidators.TotalVotingPower(), previousTotalVotingPower,
"expected voting power to change (before: %d, after: %d)",
previousTotalVotingPower, states[useState].GetRoundState().LastValidators.TotalVotingPower(),
)
}
}
+3 -4
View File
@@ -239,7 +239,7 @@ func (h *Handshaker) NBlocks() int {
func (h *Handshaker) Handshake(ctx context.Context, appClient abciclient.Client) error {
// Handshake is done via ABCI Info on the query conn.
res, err := appClient.Info(ctx, proxy.RequestInfo)
res, err := appClient.Info(ctx, &proxy.RequestInfo)
if err != nil {
return fmt.Errorf("error calling Info: %w", err)
}
@@ -307,15 +307,14 @@ func (h *Handshaker) ReplayBlocks(
validatorSet := types.NewValidatorSet(validators)
nextVals := types.TM2PB.ValidatorUpdates(validatorSet)
pbParams := h.genDoc.ConsensusParams.ToProto()
req := abci.RequestInitChain{
res, err := appClient.InitChain(ctx, &abci.RequestInitChain{
Time: h.genDoc.GenesisTime,
ChainId: h.genDoc.ChainID,
InitialHeight: h.genDoc.InitialHeight,
ConsensusParams: &pbParams,
Validators: nextVals,
AppStateBytes: h.genDoc.AppState,
}
res, err := appClient.InitChain(ctx, req)
})
if err != nil {
return nil, err
}
+5 -5
View File
@@ -75,15 +75,15 @@ type mockProxyApp struct {
abciResponses *tmstate.ABCIResponses
}
func (mock *mockProxyApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock {
func (mock *mockProxyApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
r := mock.abciResponses.FinalizeBlock
mock.txCount++
if r == nil {
return abci.ResponseFinalizeBlock{}
return &abci.ResponseFinalizeBlock{}, nil
}
return *r
return r, nil
}
func (mock *mockProxyApp) Commit() abci.ResponseCommit {
return abci.ResponseCommit{Data: mock.appHash}
func (mock *mockProxyApp) Commit(context.Context) (*abci.ResponseCommit, error) {
return &abci.ResponseCommit{Data: mock.appHash}, nil
}
+12 -14
View File
@@ -118,9 +118,6 @@ func sendTxs(ctx context.Context, t *testing.T, cs *State) {
// TestWALCrash uses crashing WAL to test we can recover from any WAL failure.
func TestWALCrash(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testCases := []struct {
name string
initFn func(dbm.DB, *State, context.Context)
@@ -139,6 +136,9 @@ func TestWALCrash(t *testing.T) {
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
consensusReplayConfig, err := ResetConfig(t.TempDir(), tc.name)
require.NoError(t, err)
crashWALandCheckLiveness(ctx, t, consensusReplayConfig, tc.initFn, tc.heightToStop)
@@ -788,7 +788,7 @@ func testHandshakeReplay(
require.NoError(t, err, "Error on abci handshake")
// get the latest app hash from the app
res, err := proxyApp.Info(ctx, abci.RequestInfo{Version: ""})
res, err := proxyApp.Info(ctx, &abci.RequestInfo{Version: ""})
if err != nil {
t.Fatal(err)
}
@@ -856,7 +856,7 @@ func buildAppStateFromChain(
state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version
validators := types.TM2PB.ValidatorUpdates(state.Validators)
_, err := appClient.InitChain(ctx, abci.RequestInitChain{
_, err := appClient.InitChain(ctx, &abci.RequestInitChain{
Validators: validators,
})
require.NoError(t, err)
@@ -910,7 +910,7 @@ func buildTMStateFromChain(
state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version
validators := types.TM2PB.ValidatorUpdates(state.Validators)
_, err := proxyApp.InitChain(ctx, abci.RequestInitChain{
_, err := proxyApp.InitChain(ctx, &abci.RequestInitChain{
Validators: validators,
})
require.NoError(t, err)
@@ -1017,15 +1017,15 @@ type badApp struct {
onlyLastHashIsWrong bool
}
func (app *badApp) Commit() abci.ResponseCommit {
func (app *badApp) Commit(context.Context) (*abci.ResponseCommit, error) {
app.height++
if app.onlyLastHashIsWrong {
if app.height == app.numBlocks {
return abci.ResponseCommit{Data: tmrand.Bytes(8)}
return &abci.ResponseCommit{Data: tmrand.Bytes(8)}, nil
}
return abci.ResponseCommit{Data: []byte{app.height}}
return &abci.ResponseCommit{Data: []byte{app.height}}, nil
} else if app.allHashesAreWrong {
return abci.ResponseCommit{Data: tmrand.Bytes(8)}
return &abci.ResponseCommit{Data: tmrand.Bytes(8)}, nil
}
panic("either allHashesAreWrong or onlyLastHashIsWrong must be set")
@@ -1275,8 +1275,6 @@ type initChainApp struct {
vals []abci.ValidatorUpdate
}
func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
return abci.ResponseInitChain{
Validators: ica.vals,
}
func (ica *initChainApp) InitChain(_ context.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
return &abci.ResponseInitChain{Validators: ica.vals}, nil
}
+16 -3
View File
@@ -922,7 +922,6 @@ func (cs *State) receiveRoutine(ctx context.Context, maxSteps int) {
if err := cs.wal.Write(mi); err != nil {
cs.logger.Error("failed writing to WAL", "err", err)
}
// handles proposals, block parts, votes
// may generate internal events (votes, complete proposals, 2/3 majorities)
cs.handleMsg(ctx, mi)
@@ -1335,6 +1334,7 @@ func (cs *State) defaultDecideProposal(ctx context.Context, height int64, round
} else if block == nil {
return
}
cs.metrics.ProposalCreateCount.Add(1)
blockParts, err = block.MakePartSet(types.BlockPartSizeBytes)
if err != nil {
cs.logger.Error("unable to create proposal block part set", "error", err)
@@ -1532,6 +1532,7 @@ func (cs *State) defaultDoPrevote(ctx context.Context, height int64, round int32
if err != nil {
panic(fmt.Sprintf("ProcessProposal: %v", err))
}
cs.metrics.MarkProposalProcessed(isAppValid)
// Vote nil if the Application rejected the block
if !isAppValid {
@@ -2298,6 +2299,10 @@ func (cs *State) addVote(
"cs_height", cs.Height,
)
if vote.Height < cs.Height || (vote.Height == cs.Height && vote.Round < cs.Round) {
cs.metrics.MarkLateVote(vote.Type)
}
// A precommit for the previous height?
// These come in while we wait timeoutCommit
if vote.Height+1 == cs.Height && vote.Type == tmproto.PrecommitType {
@@ -2338,7 +2343,9 @@ func (cs *State) addVote(
// Verify VoteExtension if precommit
if vote.Type == tmproto.PrecommitType {
if err = cs.blockExec.VerifyVoteExtension(ctx, vote); err != nil {
err := cs.blockExec.VerifyVoteExtension(ctx, vote)
cs.metrics.MarkVoteExtensionReceived(err == nil)
if err != nil {
return false, err
}
}
@@ -2349,6 +2356,11 @@ func (cs *State) addVote(
// Either duplicate, or error upon cs.Votes.AddByIndex()
return
}
if vote.Round == cs.Round {
vals := cs.state.Validators
_, val := vals.GetByIndex(vote.ValidatorIndex)
cs.metrics.MarkVoteReceived(vote.Type, val.VotingPower, vals.TotalVotingPower())
}
if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil {
return added, err
@@ -2492,7 +2504,7 @@ func (cs *State) signVote(
if err != nil {
return nil, err
}
vote.VoteExtension = ext
vote.Extension = ext
default:
timeout = time.Second
}
@@ -2504,6 +2516,7 @@ func (cs *State) signVote(
err := cs.privValidator.SignVote(ctxto, cs.state.ChainID, v)
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
vote.Timestamp = v.Timestamp
return vote, err
+271 -12
View File
@@ -13,7 +13,7 @@ import (
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
abcimocks "github.com/tendermint/tendermint/abci/types/mocks"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/crypto"
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
"github.com/tendermint/tendermint/internal/eventbus"
tmpubsub "github.com/tendermint/tendermint/internal/pubsub"
@@ -1912,12 +1912,13 @@ func TestProcessProposalAccept(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
m := abcimocks.NewBaseMock()
m := abcimocks.NewApplication(t)
status := abci.ResponseProcessProposal_REJECT
if testCase.accept {
status = abci.ResponseProcessProposal_ACCEPT
}
m.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: status})
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: status}, nil)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil).Maybe()
cs1, _ := makeState(ctx, t, makeStateArgs{config: config, application: m})
height, round := cs1.Height, cs1.Round
@@ -1964,12 +1965,18 @@ func TestFinalizeBlockCalled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
m := abcimocks.NewBaseMock()
m.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT})
m.On("VerifyVoteExtension", mock.Anything).Return(abci.ResponseVerifyVoteExtension{
m := abcimocks.NewApplication(t)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{
Status: abci.ResponseProcessProposal_ACCEPT,
}, nil)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
})
m.On("FinalizeBlock", mock.Anything).Return(abci.ResponseFinalizeBlock{}).Maybe()
}, nil)
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil)
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
height, round := cs1.Height, cs1.Round
@@ -2007,14 +2014,254 @@ func TestFinalizeBlockCalled(t *testing.T) {
m.AssertExpectations(t)
if !testCase.expectCalled {
m.AssertNotCalled(t, "FinalizeBlock", mock.Anything)
m.AssertNotCalled(t, "FinalizeBlock", ctx, mock.Anything)
} else {
m.AssertCalled(t, "FinalizeBlock", mock.Anything)
m.AssertCalled(t, "FinalizeBlock", ctx, mock.Anything)
}
})
}
}
// TestExtendVoteCalled tests that the vote extension methods are called at the
// correct point in the consensus algorithm.
func TestExtendVoteCalled(t *testing.T) {
config := configSetup(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
m := abcimocks.NewApplication(t)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
VoteExtension: []byte("extension"),
}, nil)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil)
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
height, round := cs1.Height, cs1.Round
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
pv1, err := cs1.privValidator.GetPubKey(ctx)
require.NoError(t, err)
addr := pv1.Address()
voteCh := subscribeToVoter(ctx, t, cs1, addr)
startTestRound(ctx, cs1, cs1.Height, round)
ensureNewRound(t, newRoundCh, height, round)
ensureNewProposal(t, proposalCh, height, round)
m.AssertNotCalled(t, "ExtendVote", mock.Anything, mock.Anything)
rs := cs1.GetRoundState()
blockID := types.BlockID{
Hash: rs.ProposalBlock.Hash(),
PartSetHeader: rs.ProposalBlockParts.Header(),
}
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...)
ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash)
ensurePrecommit(t, voteCh, height, round)
m.AssertCalled(t, "ExtendVote", ctx, &abci.RequestExtendVote{
Height: height,
Hash: blockID.Hash,
})
m.AssertCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[1:]...)
ensureNewRound(t, newRoundCh, height+1, 0)
m.AssertExpectations(t)
// Only 3 of the vote extensions are seen, as consensus proceeds as soon as the +2/3 threshold
// is observed by the consensus engine.
for _, pv := range vss[:3] {
pv, err := pv.GetPubKey(ctx)
require.NoError(t, err)
addr := pv.Address()
m.AssertCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
}
}
// TestVerifyVoteExtensionNotCalledOnAbsentPrecommit tests that the VerifyVoteExtension
// method is not called for a validator's vote that is never delivered.
func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) {
config := configSetup(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
m := abcimocks.NewApplication(t)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
VoteExtension: []byte("extension"),
}, nil)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil)
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
height, round := cs1.Height, cs1.Round
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
pv1, err := cs1.privValidator.GetPubKey(ctx)
require.NoError(t, err)
addr := pv1.Address()
voteCh := subscribeToVoter(ctx, t, cs1, addr)
startTestRound(ctx, cs1, cs1.Height, round)
ensureNewRound(t, newRoundCh, height, round)
ensureNewProposal(t, proposalCh, height, round)
rs := cs1.GetRoundState()
blockID := types.BlockID{
Hash: rs.ProposalBlock.Hash(),
PartSetHeader: rs.ProposalBlockParts.Header(),
}
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[2:]...)
ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash)
ensurePrecommit(t, voteCh, height, round)
m.AssertCalled(t, "ExtendVote", mock.Anything, &abci.RequestExtendVote{
Height: height,
Hash: blockID.Hash,
})
m.AssertCalled(t, "VerifyVoteExtension", mock.Anything, &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[2:]...)
ensureNewRound(t, newRoundCh, height+1, 0)
m.AssertExpectations(t)
// vss[1] did not issue a precommit for the block, ensure that a vote extension
// for its address was not sent to the application.
pv, err := vss[1].GetPubKey(ctx)
require.NoError(t, err)
addr = pv.Address()
m.AssertNotCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
}
// TestPrepareProposalReceivesVoteExtensions tests that the PrepareProposal method
// is called with the vote extensions from the previous height. The test functions
// be completing a consensus height with a mock application as the proposer. The
// test then proceeds to fail sever rounds of consensus until the mock application
// is the proposer again and ensures that the mock application receives the set of
// vote extensions from the previous consensus instance.
func TestPrepareProposalReceivesVoteExtensions(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
config := configSetup(t)
// create a list of vote extensions, one for each validator.
voteExtensions := [][]byte{
[]byte("extension 0"),
[]byte("extension 1"),
[]byte("extension 2"),
[]byte("extension 3"),
}
// m := abcimocks.NewApplication(t)
m := &abcimocks.Application{}
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
VoteExtension: voteExtensions[0],
}, nil)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
// capture the prepare proposal request.
rpp := &abci.RequestPrepareProposal{}
m.On("PrepareProposal", mock.Anything, mock.MatchedBy(func(r *abci.RequestPrepareProposal) bool {
rpp = r
return true
})).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil).Once()
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil)
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil)
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
height, round := cs1.Height, cs1.Round
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
pv1, err := cs1.privValidator.GetPubKey(ctx)
require.NoError(t, err)
addr := pv1.Address()
voteCh := subscribeToVoter(ctx, t, cs1, addr)
startTestRound(ctx, cs1, height, round)
ensureNewRound(t, newRoundCh, height, round)
ensureNewProposal(t, proposalCh, height, round)
rs := cs1.GetRoundState()
blockID := types.BlockID{
Hash: rs.ProposalBlock.Hash(),
PartSetHeader: rs.ProposalBlockParts.Header(),
}
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...)
// create a precommit for each validator with the associated vote extension.
for i, vs := range vss[1:] {
signAddPrecommitWithExtension(ctx, t, cs1, config.ChainID(), blockID, voteExtensions[i+1], vs)
}
ensurePrevote(t, voteCh, height, round)
// ensure that the height is committed.
ensurePrecommitMatch(t, voteCh, height, round, blockID.Hash)
incrementHeight(vss[1:]...)
height++
round = 0
ensureNewRound(t, newRoundCh, height, round)
incrementRound(vss[1:]...)
incrementRound(vss[1:]...)
incrementRound(vss[1:]...)
round = 3
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), types.BlockID{}, vss[1:]...)
ensureNewRound(t, newRoundCh, height, round)
ensureNewProposal(t, proposalCh, height, round)
// ensure that the proposer received the list of vote extensions from the
// previous height.
require.Len(t, rpp.LocalLastCommit.Votes, len(vss))
for i := range vss {
require.Equal(t, rpp.LocalLastCommit.Votes[i].VoteExtension, voteExtensions[i])
}
}
// 4 vals, 3 Nil Precommits at P0
// What we want:
// P0 waits for timeoutPrecommit before starting next round
@@ -2522,7 +2769,7 @@ func TestStateOutputVoteStats(t *testing.T) {
peerID, err := types.NewNodeID("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
require.NoError(t, err)
randBytes := tmrand.Bytes(tmhash.Size)
randBytes := tmrand.Bytes(crypto.HashSize)
blockID := types.BlockID{
Hash: randBytes,
}
@@ -2560,7 +2807,7 @@ func TestSignSameVoteTwice(t *testing.T) {
_, vss := makeState(ctx, t, makeStateArgs{config: config, validators: 2})
randBytes := tmrand.Bytes(tmhash.Size)
randBytes := tmrand.Bytes(crypto.HashSize)
vote := signVote(
ctx,
@@ -2719,3 +2966,15 @@ func subscribe(
}()
return ch
}
func signAddPrecommitWithExtension(ctx context.Context,
t *testing.T,
cs *State,
chainID string,
blockID types.BlockID,
extension []byte,
stub *validatorStub) {
v, err := stub.signVote(ctx, tmproto.PrecommitType, chainID, blockID, extension)
require.NoError(t, err, "failed to sign vote")
addVotes(cs, v)
}
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/internal/test/factory"
tmrand "github.com/tendermint/tendermint/libs/rand"
tmtime "github.com/tendermint/tendermint/libs/time"
@@ -71,7 +71,7 @@ func makeVoteHR(
pubKey, err := privVal.GetPubKey(ctx)
require.NoError(t, err)
randBytes := tmrand.Bytes(tmhash.Size)
randBytes := tmrand.Bytes(crypto.HashSize)
vote := &types.Vote{
ValidatorAddress: pubKey.Address(),
@@ -88,6 +88,7 @@ func makeVoteHR(
require.NoError(t, err, "Error signing vote")
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
return vote
}
+13
View File
@@ -3,7 +3,10 @@
package mocks
import (
testing "testing"
mock "github.com/stretchr/testify/mock"
types "github.com/tendermint/tendermint/types"
)
@@ -57,3 +60,13 @@ func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
return r0
}
// NewBlockStore creates a new instance of BlockStore. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewBlockStore(t testing.TB) *BlockStore {
mock := &BlockStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+6 -7
View File
@@ -133,7 +133,7 @@ func (r *Reactor) handleEvidenceMessage(ctx context.Context, envelope *p2p.Envel
// handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
// It will handle errors and any possible panics gracefully. A caller can handle
// any error returned by sending a PeerError on the respective channel.
func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelope *p2p.Envelope) (err error) {
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("panic in processing message: %v", e)
@@ -147,15 +147,14 @@ func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelop
r.logger.Debug("received message", "message", envelope.Message, "peer", envelope.From)
switch chID {
switch envelope.ChannelID {
case EvidenceChannel:
err = r.handleEvidenceMessage(ctx, envelope)
default:
err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", chID, envelope)
err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", envelope.ChannelID, envelope)
}
return err
return
}
// processEvidenceCh implements a blocking event loop where we listen for p2p
@@ -164,8 +163,8 @@ func (r *Reactor) processEvidenceCh(ctx context.Context, evidenceCh *p2p.Channel
iter := evidenceCh.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
if err := r.handleMessage(ctx, evidenceCh.ID, envelope); err != nil {
r.logger.Error("failed to process message", "ch_id", evidenceCh.ID, "envelope", envelope, "err", err)
if err := r.handleMessage(ctx, envelope); err != nil {
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := evidenceCh.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
+2 -3
View File
@@ -16,7 +16,6 @@ import (
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/eventbus"
"github.com/tendermint/tendermint/internal/evidence"
"github.com/tendermint/tendermint/internal/evidence/mocks"
@@ -523,10 +522,10 @@ func TestEvidenceListSerialization(t *testing.T) {
Round: 2,
Timestamp: stamp,
BlockID: types.BlockID{
Hash: tmhash.Sum([]byte("blockID_hash")),
Hash: crypto.Checksum([]byte("blockID_hash")),
PartSetHeader: types.PartSetHeader{
Total: 1000000,
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
Hash: crypto.Checksum([]byte("blockID_part_set_header_hash")),
},
},
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
+3 -4
View File
@@ -11,7 +11,6 @@ import (
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/eventbus"
"github.com/tendermint/tendermint/internal/evidence"
"github.com/tendermint/tendermint/internal/evidence/mocks"
@@ -273,7 +272,7 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) {
// conflicting header has different next validators hash which should have been correctly derived from
// the previous round
ev.ConflictingBlock.Header.NextValidatorsHash = crypto.CRandBytes(tmhash.Size)
ev.ConflictingBlock.Header.NextValidatorsHash = crypto.CRandBytes(crypto.HashSize)
assert.Error(t, evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, nil,
defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour))
@@ -618,8 +617,8 @@ func makeVote(
func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
var (
h = make([]byte, tmhash.Size)
psH = make([]byte, tmhash.Size)
h = make([]byte, crypto.HashSize)
psH = make([]byte, crypto.HashSize)
)
copy(h, hash)
copy(psH, partSetHash)
+10 -10
View File
@@ -38,16 +38,16 @@ func Routes(cfg config.RPCConfig, s state.Store, bs state.BlockStore, es []index
Logger: logger,
}
return core.RoutesMap{
"blockchain": server.NewRPCFunc(env.BlockchainInfo, "minHeight", "maxHeight"),
"consensus_params": server.NewRPCFunc(env.ConsensusParams, "height"),
"block": server.NewRPCFunc(env.Block, "height"),
"block_by_hash": server.NewRPCFunc(env.BlockByHash, "hash"),
"block_results": server.NewRPCFunc(env.BlockResults, "height"),
"commit": server.NewRPCFunc(env.Commit, "height"),
"validators": server.NewRPCFunc(env.Validators, "height", "page", "per_page"),
"tx": server.NewRPCFunc(env.Tx, "hash", "prove"),
"tx_search": server.NewRPCFunc(env.TxSearch, "query", "prove", "page", "per_page", "order_by"),
"block_search": server.NewRPCFunc(env.BlockSearch, "query", "page", "per_page", "order_by"),
"blockchain": server.NewRPCFunc(env.BlockchainInfo),
"consensus_params": server.NewRPCFunc(env.ConsensusParams),
"block": server.NewRPCFunc(env.Block),
"block_by_hash": server.NewRPCFunc(env.BlockByHash),
"block_results": server.NewRPCFunc(env.BlockResults),
"commit": server.NewRPCFunc(env.Commit),
"validators": server.NewRPCFunc(env.Validators),
"tx": server.NewRPCFunc(env.Tx),
"tx_search": server.NewRPCFunc(env.TxSearch),
"block_search": server.NewRPCFunc(env.BlockSearch),
}
}
+2 -3
View File
@@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/libs/protoio"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types"
@@ -25,10 +24,10 @@ func aVote(t testing.TB) *types.Vote {
Round: 2,
Timestamp: stamp,
BlockID: types.BlockID{
Hash: tmhash.Sum([]byte("blockID_hash")),
Hash: crypto.Checksum([]byte("blockID_hash")),
PartSetHeader: types.PartSetHeader{
Total: 1000000,
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
Hash: crypto.Checksum([]byte("blockID_part_set_header_hash")),
},
},
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
+2 -2
View File
@@ -260,7 +260,7 @@ func (txmp *TxMempool) CheckTx(
return types.ErrTxInCache
}
res, err := txmp.proxyAppConn.CheckTx(ctx, abci.RequestCheckTx{Tx: tx})
res, err := txmp.proxyAppConn.CheckTx(ctx, &abci.RequestCheckTx{Tx: tx})
if err != nil {
txmp.cache.Remove(tx)
return err
@@ -700,7 +700,7 @@ func (txmp *TxMempool) updateReCheckTxs(ctx context.Context) {
// Only execute CheckTx if the transaction is not marked as removed which
// could happen if the transaction was evicted.
if !txmp.txStore.IsTxRemoved(wtx.hash) {
res, err := txmp.proxyAppConn.CheckTx(ctx, abci.RequestCheckTx{
res, err := txmp.proxyAppConn.CheckTx(ctx, &abci.RequestCheckTx{
Tx: wtx.tx,
Type: abci.CheckTxType_Recheck,
})
+7 -7
View File
@@ -36,7 +36,7 @@ type testTx struct {
priority int64
}
func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) {
var (
priority int64
sender string
@@ -47,29 +47,29 @@ func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
if len(parts) == 3 {
v, err := strconv.ParseInt(string(parts[2]), 10, 64)
if err != nil {
return abci.ResponseCheckTx{
return &abci.ResponseCheckTx{
Priority: priority,
Code: 100,
GasWanted: 1,
}
}, nil
}
priority = v
sender = string(parts[0])
} else {
return abci.ResponseCheckTx{
return &abci.ResponseCheckTx{
Priority: priority,
Code: 101,
GasWanted: 1,
}
}, nil
}
return abci.ResponseCheckTx{
return &abci.ResponseCheckTx{
Priority: priority,
Sender: sender,
Code: code.CodeTypeOK,
GasWanted: 1,
}
}, nil
}
func setup(t testing.TB, app abciclient.Client, cacheSize int, options ...TxMempoolOption) *TxMempool {
+12
View File
@@ -11,6 +11,8 @@ import (
mock "github.com/stretchr/testify/mock"
testing "testing"
types "github.com/tendermint/tendermint/types"
)
@@ -170,3 +172,13 @@ func (_m *Mempool) Update(ctx context.Context, blockHeight int64, blockTxs types
return r0
}
// NewMempool creates a new instance of Mempool. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewMempool(t testing.TB) *Mempool {
mock := &Mempool{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+6 -7
View File
@@ -169,7 +169,7 @@ func (r *Reactor) handleMempoolMessage(ctx context.Context, envelope *p2p.Envelo
// handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
// It will handle errors and any possible panics gracefully. A caller can handle
// any error returned by sending a PeerError on the respective channel.
func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelope *p2p.Envelope) (err error) {
func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope) (err error) {
defer func() {
if e := recover(); e != nil {
r.observePanic(e)
@@ -184,15 +184,14 @@ func (r *Reactor) handleMessage(ctx context.Context, chID p2p.ChannelID, envelop
r.logger.Debug("received message", "peer", envelope.From)
switch chID {
switch envelope.ChannelID {
case MempoolChannel:
err = r.handleMempoolMessage(ctx, envelope)
default:
err = fmt.Errorf("unknown channel ID (%d) for envelope (%T)", chID, envelope.Message)
err = fmt.Errorf("unknown channel ID (%d) for envelope (%T)", envelope.ChannelID, envelope.Message)
}
return err
return
}
// processMempoolCh implements a blocking event loop where we listen for p2p
@@ -201,8 +200,8 @@ func (r *Reactor) processMempoolCh(ctx context.Context, mempoolCh *p2p.Channel)
iter := mempoolCh.Receive(ctx)
for iter.Next(ctx) {
envelope := iter.Envelope()
if err := r.handleMessage(ctx, mempoolCh.ID, envelope); err != nil {
r.logger.Error("failed to process message", "ch_id", mempoolCh.ID, "envelope", envelope, "err", err)
if err := r.handleMessage(ctx, envelope); err != nil {
r.logger.Error("failed to process message", "ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := mempoolCh.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
+4 -4
View File
@@ -97,7 +97,7 @@ func ParseNodeAddress(urlString string) (NodeAddress, error) {
// Resolve resolves a NodeAddress into a set of Endpoints, by expanding
// out a DNS hostname to IP addresses.
func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) {
func (a NodeAddress) Resolve(ctx context.Context) ([]*Endpoint, error) {
if a.Protocol == "" {
return nil, errors.New("address has no protocol")
}
@@ -109,7 +109,7 @@ func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) {
if a.NodeID == "" {
return nil, errors.New("local address has no node ID")
}
return []Endpoint{{
return []*Endpoint{{
Protocol: a.Protocol,
Path: string(a.NodeID),
}}, nil
@@ -119,9 +119,9 @@ func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) {
if err != nil {
return nil, err
}
endpoints := make([]Endpoint, len(ips))
endpoints := make([]*Endpoint, len(ips))
for i, ip := range ips {
endpoints[i] = Endpoint{
endpoints[i] = &Endpoint{
Protocol: a.Protocol,
IP: ip,
Port: a.Port,
+17 -17
View File
@@ -210,71 +210,71 @@ func TestNodeAddress_Resolve(t *testing.T) {
testcases := []struct {
address p2p.NodeAddress
expect p2p.Endpoint
expect *p2p.Endpoint
ok bool
}{
// Valid networked addresses (with hostname).
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1", Port: 80, Path: "/path"},
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
true,
},
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "localhost", Port: 80, Path: "/path"},
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
true,
},
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "localhost", Port: 80, Path: "/path"},
p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback, Port: 80, Path: "/path"},
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback, Port: 80, Path: "/path"},
true,
},
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1"},
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1)},
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1)},
true,
},
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "::1"},
p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback},
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback},
true,
},
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "8.8.8.8"},
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(8, 8, 8, 8)},
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(8, 8, 8, 8)},
true,
},
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "2001:0db8::ff00:0042:8329"},
p2p.Endpoint{Protocol: "tcp", IP: []byte{
&p2p.Endpoint{Protocol: "tcp", IP: []byte{
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29}},
true,
},
{
p2p.NodeAddress{Protocol: "tcp", Hostname: "some.missing.host.tendermint.com"},
p2p.Endpoint{},
&p2p.Endpoint{},
false,
},
// Valid non-networked addresses.
{
p2p.NodeAddress{Protocol: "memory", NodeID: id},
p2p.Endpoint{Protocol: "memory", Path: string(id)},
&p2p.Endpoint{Protocol: "memory", Path: string(id)},
true,
},
{
p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: string(id)},
p2p.Endpoint{Protocol: "memory", Path: string(id)},
&p2p.Endpoint{Protocol: "memory", Path: string(id)},
true,
},
// Invalid addresses.
{p2p.NodeAddress{}, p2p.Endpoint{}, false},
{p2p.NodeAddress{Hostname: "127.0.0.1"}, p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1:80"}, p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "memory"}, p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "memory", Path: string(id)}, p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "tcp", Hostname: "💥"}, p2p.Endpoint{}, false},
{p2p.NodeAddress{}, &p2p.Endpoint{}, false},
{p2p.NodeAddress{Hostname: "127.0.0.1"}, &p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1:80"}, &p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "memory"}, &p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "memory", Path: string(id)}, &p2p.Endpoint{}, false},
{p2p.NodeAddress{Protocol: "tcp", Hostname: "💥"}, &p2p.Endpoint{}, false},
}
for _, tc := range testcases {
tc := tc
+6 -14
View File
@@ -59,25 +59,17 @@ type Channel struct {
outCh chan<- Envelope // outbound messages (reactors to peers)
errCh chan<- PeerError // peer error reporting
messageType proto.Message // the channel's message type, used for unmarshaling
name string
name string
}
// NewChannel creates a new channel. It is primarily for internal and test
// use, reactors should use Router.OpenChannel().
func NewChannel(
id ChannelID,
messageType proto.Message,
inCh <-chan Envelope,
outCh chan<- Envelope,
errCh chan<- PeerError,
) *Channel {
func NewChannel(id ChannelID, inCh <-chan Envelope, outCh chan<- Envelope, errCh chan<- PeerError) *Channel {
return &Channel{
ID: id,
messageType: messageType,
inCh: inCh,
outCh: outCh,
errCh: errCh,
ID: id,
inCh: inCh,
outCh: outCh,
errCh: errCh,
}
}
-16
View File
@@ -1,16 +0,0 @@
//go:build go1.10
// +build go1.10
package conn
// Go1.10 has a proper net.Conn implementation that
// has the SetDeadline method implemented as per
// https://github.com/golang/go/commit/e2dd8ca946be884bb877e074a21727f1a685a706
// lest we run into problems like
// https://github.com/tendermint/tendermint/issues/851
import "net"
func NetPipe() (net.Conn, net.Conn) {
return net.Pipe()
}
-33
View File
@@ -1,33 +0,0 @@
//go:build !go1.10
// +build !go1.10
package conn
import (
"net"
"time"
)
// Only Go1.10 has a proper net.Conn implementation that
// has the SetDeadline method implemented as per
// https://github.com/golang/go/commit/e2dd8ca946be884bb877e074a21727f1a685a706
// lest we run into problems like
// https://github.com/tendermint/tendermint/issues/851
// so for go versions < Go1.10 use our custom net.Conn creator
// that doesn't return an `Unimplemented error` for net.Conn.
// Before https://github.com/tendermint/tendermint/commit/49faa79bdce5663894b3febbf4955fb1d172df04
// we hadn't cared about errors from SetDeadline so swallow them up anyways.
type pipe struct {
net.Conn
}
func (p *pipe) SetDeadline(t time.Time) error {
return nil
}
func NetPipe() (net.Conn, net.Conn) {
p1, p2 := net.Pipe()
return &pipe{p1}, &pipe{p2}
}
var _ net.Conn = (*pipe)(nil)
+6 -6
View File
@@ -48,7 +48,7 @@ func createMConnectionWithCallbacks(
}
func TestMConnectionSendFlushStop(t *testing.T) {
server, client := NetPipe()
server, client := net.Pipe()
t.Cleanup(closeAll(t, client, server))
ctx, cancel := context.WithCancel(context.Background())
@@ -85,7 +85,7 @@ func TestMConnectionSendFlushStop(t *testing.T) {
}
func TestMConnectionSend(t *testing.T) {
server, client := NetPipe()
server, client := net.Pipe()
t.Cleanup(closeAll(t, client, server))
ctx, cancel := context.WithCancel(context.Background())
@@ -116,7 +116,7 @@ func TestMConnectionSend(t *testing.T) {
}
func TestMConnectionReceive(t *testing.T) {
server, client := NetPipe()
server, client := net.Pipe()
t.Cleanup(closeAll(t, client, server))
receivedCh := make(chan []byte)
@@ -378,7 +378,7 @@ func TestMConnectionPingPongs(t *testing.T) {
}
func TestMConnectionStopsAndReturnsError(t *testing.T) {
server, client := NetPipe()
server, client := net.Pipe()
t.Cleanup(closeAll(t, client, server))
receivedCh := make(chan []byte)
@@ -423,7 +423,7 @@ func newClientAndServerConnsForReadErrors(
t *testing.T,
chOnErr chan struct{},
) (*MConnection, *MConnection) {
server, client := NetPipe()
server, client := net.Pipe()
onReceive := func(context.Context, ChannelID, []byte) {}
onError := func(context.Context, interface{}) {}
@@ -558,7 +558,7 @@ func TestMConnectionReadErrorUnknownMsgType(t *testing.T) {
}
func TestMConnectionTrySend(t *testing.T) {
server, client := NetPipe()
server, client := net.Pipe()
t.Cleanup(closeAll(t, client, server))
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
+12
View File
@@ -13,6 +13,8 @@ import (
p2p "github.com/tendermint/tendermint/internal/p2p"
testing "testing"
types "github.com/tendermint/tendermint/types"
)
@@ -150,3 +152,13 @@ func (_m *Connection) String() string {
return r0
}
// NewConnection creates a new instance of Connection. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewConnection(t testing.TB) *Connection {
mock := &Connection{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+30 -11
View File
@@ -10,6 +10,8 @@ import (
mock "github.com/stretchr/testify/mock"
p2p "github.com/tendermint/tendermint/internal/p2p"
testing "testing"
)
// Transport is an autogenerated mock type for the Transport type
@@ -60,11 +62,11 @@ func (_m *Transport) Close() error {
}
// Dial provides a mock function with given fields: _a0, _a1
func (_m *Transport) Dial(_a0 context.Context, _a1 p2p.Endpoint) (p2p.Connection, error) {
func (_m *Transport) Dial(_a0 context.Context, _a1 *p2p.Endpoint) (p2p.Connection, error) {
ret := _m.Called(_a0, _a1)
var r0 p2p.Connection
if rf, ok := ret.Get(0).(func(context.Context, p2p.Endpoint) p2p.Connection); ok {
if rf, ok := ret.Get(0).(func(context.Context, *p2p.Endpoint) p2p.Connection); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
@@ -73,7 +75,7 @@ func (_m *Transport) Dial(_a0 context.Context, _a1 p2p.Endpoint) (p2p.Connection
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, p2p.Endpoint) error); ok {
if rf, ok := ret.Get(1).(func(context.Context, *p2p.Endpoint) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
@@ -82,28 +84,35 @@ func (_m *Transport) Dial(_a0 context.Context, _a1 p2p.Endpoint) (p2p.Connection
return r0, r1
}
// Endpoints provides a mock function with given fields:
func (_m *Transport) Endpoints() []p2p.Endpoint {
// Endpoint provides a mock function with given fields:
func (_m *Transport) Endpoint() (*p2p.Endpoint, error) {
ret := _m.Called()
var r0 []p2p.Endpoint
if rf, ok := ret.Get(0).(func() []p2p.Endpoint); ok {
var r0 *p2p.Endpoint
if rf, ok := ret.Get(0).(func() *p2p.Endpoint); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]p2p.Endpoint)
r0 = ret.Get(0).(*p2p.Endpoint)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Listen provides a mock function with given fields: _a0
func (_m *Transport) Listen(_a0 p2p.Endpoint) error {
func (_m *Transport) Listen(_a0 *p2p.Endpoint) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(p2p.Endpoint) error); ok {
if rf, ok := ret.Get(0).(func(*p2p.Endpoint) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
@@ -141,3 +150,13 @@ func (_m *Transport) String() string {
return r0
}
// NewTransport creates a new instance of Transport. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewTransport(t testing.TB) *Transport {
mock := &Transport{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+6 -5
View File
@@ -247,7 +247,9 @@ func (n *Network) MakeNode(ctx context.Context, t *testing.T, opts NodeOptions)
}
transport := n.memoryNetwork.CreateTransport(nodeID)
require.Len(t, transport.Endpoints(), 1, "transport not listening on 1 endpoint")
ep, err := transport.Endpoint()
require.NoError(t, err)
require.NotNil(t, ep, "transport not listening an endpoint")
peerManager, err := p2p.NewPeerManager(nodeID, dbm.NewMemDB(), p2p.PeerManagerOptions{
MinRetryTime: 10 * time.Millisecond,
@@ -264,8 +266,8 @@ func (n *Network) MakeNode(ctx context.Context, t *testing.T, opts NodeOptions)
privKey,
peerManager,
func() *types.NodeInfo { return &nodeInfo },
[]p2p.Transport{transport},
transport.Endpoints(),
transport,
ep,
p2p.RouterOptions{DialSleep: func(_ context.Context) {}},
)
@@ -284,7 +286,7 @@ func (n *Network) MakeNode(ctx context.Context, t *testing.T, opts NodeOptions)
return &Node{
NodeID: nodeID,
NodeInfo: nodeInfo,
NodeAddress: transport.Endpoints()[0].NodeAddress(nodeID),
NodeAddress: ep.NodeAddress(nodeID),
PrivKey: privKey,
Router: router,
PeerManager: peerManager,
@@ -304,7 +306,6 @@ func (n *Node) MakeChannel(
ctx, cancel := context.WithCancel(ctx)
channel, err := n.Router.OpenChannel(ctx, chDesc)
require.NoError(t, err)
require.Contains(t, n.Router.NodeInfo().Channels, byte(chDesc.ID))
t.Cleanup(func() {
RequireEmpty(ctx, t, channel)
cancel()
+1 -1
View File
@@ -123,7 +123,7 @@ func RequireError(ctx context.Context, t *testing.T, channel *p2p.Channel, peerE
err := channel.SendError(tctx, peerError)
switch {
case errors.Is(err, context.DeadlineExceeded):
require.Fail(t, "timed out reporting error", "%v on %v", peerError, channel.ID)
require.Fail(t, "timed out reporting error", "%v for %q", peerError, channel.String())
default:
require.NoError(t, err, "unexpected error")
}
+1 -1
View File
@@ -193,7 +193,7 @@ func (r *Reactor) processPexCh(ctx context.Context, pexCh *p2p.Channel) {
dur, err := r.handlePexMessage(ctx, envelope, pexCh)
if err != nil {
r.logger.Error("failed to process message",
"ch_id", pexCh.ID, "envelope", envelope, "err", err)
"ch_id", envelope.ChannelID, "envelope", envelope, "err", err)
if serr := pexCh.SendError(ctx, p2p.PeerError{
NodeID: envelope.From,
Err: err,
-1
View File
@@ -289,7 +289,6 @@ func setupSingle(ctx context.Context, t *testing.T) *singleTestReactor {
pexErrCh := make(chan p2p.PeerError, chBuf)
pexCh := p2p.NewChannel(
p2p.ChannelID(pex.PexChannel),
new(p2pproto.PexMessage),
pexInCh,
pexOutCh,
pexErrCh,
+28 -69
View File
@@ -148,15 +148,14 @@ type Router struct {
*service.BaseService
logger log.Logger
metrics *Metrics
options RouterOptions
privKey crypto.PrivKey
peerManager *PeerManager
chDescs []*ChannelDescriptor
transports []Transport
endpoints []Endpoint
connTracker connectionTracker
protocolTransports map[Protocol]Transport
metrics *Metrics
options RouterOptions
privKey crypto.PrivKey
peerManager *PeerManager
chDescs []*ChannelDescriptor
transport Transport
endpoint *Endpoint
connTracker connectionTracker
peerMtx sync.RWMutex
peerQueues map[types.NodeID]queue // outbound messages per peer for all channels
@@ -182,8 +181,8 @@ func NewRouter(
privKey crypto.PrivKey,
peerManager *PeerManager,
nodeInfoProducer func() *types.NodeInfo,
transports []Transport,
endpoints []Endpoint,
transport Transport,
endpoint *Endpoint,
options RouterOptions,
) (*Router, error) {
@@ -200,28 +199,19 @@ func NewRouter(
options.MaxIncomingConnectionAttempts,
options.IncomingConnectionWindow,
),
chDescs: make([]*ChannelDescriptor, 0),
transports: transports,
endpoints: endpoints,
protocolTransports: map[Protocol]Transport{},
peerManager: peerManager,
options: options,
channelQueues: map[ChannelID]queue{},
channelMessages: map[ChannelID]proto.Message{},
peerQueues: map[types.NodeID]queue{},
peerChannels: make(map[types.NodeID]ChannelIDSet),
chDescs: make([]*ChannelDescriptor, 0),
transport: transport,
endpoint: endpoint,
peerManager: peerManager,
options: options,
channelQueues: map[ChannelID]queue{},
channelMessages: map[ChannelID]proto.Message{},
peerQueues: map[types.NodeID]queue{},
peerChannels: make(map[types.NodeID]ChannelIDSet),
}
router.BaseService = service.NewBaseService(logger, "router", router)
for _, transport := range transports {
for _, protocol := range transport.Protocols() {
if _, ok := router.protocolTransports[protocol]; !ok {
router.protocolTransports[protocol] = transport
}
}
}
return router, nil
}
@@ -272,7 +262,7 @@ func (r *Router) OpenChannel(ctx context.Context, chDesc *ChannelDescriptor) (*C
queue := r.queueFactory(chDesc.RecvBufferCapacity)
outCh := make(chan Envelope, chDesc.RecvBufferCapacity)
errCh := make(chan PeerError, chDesc.RecvBufferCapacity)
channel := NewChannel(id, messageType, queue.dequeue(), outCh, errCh)
channel := NewChannel(id, queue.dequeue(), outCh, errCh)
channel.name = chDesc.Name
var wrapper Wrapper
@@ -286,9 +276,7 @@ func (r *Router) OpenChannel(ctx context.Context, chDesc *ChannelDescriptor) (*C
// add the channel to the nodeInfo if it's not already there.
r.nodeInfoProducer().AddChannel(uint16(chDesc.ID))
for _, t := range r.transports {
t.AddChannelDescriptors([]*ChannelDescriptor{chDesc})
}
r.transport.AddChannelDescriptors([]*ChannelDescriptor{chDesc})
go func() {
defer func() {
@@ -670,12 +658,6 @@ func (r *Router) dialPeer(ctx context.Context, address NodeAddress) (Connection,
}
for _, endpoint := range endpoints {
transport, ok := r.protocolTransports[endpoint.Protocol]
if !ok {
r.logger.Error("no transport found for protocol", "endpoint", endpoint)
continue
}
dialCtx := ctx
if r.options.DialTimeout > 0 {
var cancel context.CancelFunc
@@ -690,7 +672,7 @@ func (r *Router) dialPeer(ctx context.Context, address NodeAddress) (Connection,
// by the peer's endpoint, since e.g. a peer on 192.168.0.0 can reach us
// on a private address on this endpoint, but a peer on the public
// Internet can't and needs a different public address.
conn, err := transport.Dial(dialCtx, endpoint)
conn, err := r.transport.Dial(dialCtx, endpoint)
if err != nil {
r.logger.Error("failed to dial endpoint", "peer", address.NodeID, "endpoint", endpoint, "err", err)
} else {
@@ -731,7 +713,7 @@ func (r *Router) handshakePeer(
return peerInfo, fmt.Errorf("expected to connect with peer %q, got %q",
expectID, peerInfo.NodeID)
}
if err := r.nodeInfoProducer().CompatibleWith(peerInfo); err != nil {
if err := nodeInfo.CompatibleWith(peerInfo); err != nil {
return peerInfo, ErrRejected{
err: err,
id: peerInfo.ID(),
@@ -929,11 +911,6 @@ func (r *Router) evictPeers(ctx context.Context) {
}
}
// NodeInfo returns a copy of the current NodeInfo. Used for testing.
func (r *Router) NodeInfo() types.NodeInfo {
return r.nodeInfoProducer().Copy()
}
func (r *Router) setupQueueFactory(ctx context.Context) error {
qf, err := r.createQueueFactory(ctx)
if err != nil {
@@ -950,29 +927,13 @@ func (r *Router) OnStart(ctx context.Context) error {
return err
}
for _, transport := range r.transports {
for _, endpoint := range r.endpoints {
if err := transport.Listen(endpoint); err != nil {
return err
}
}
if err := r.transport.Listen(r.endpoint); err != nil {
return err
}
nodeInfo := r.nodeInfoProducer()
r.logger.Info(
"starting router",
"node_id", nodeInfo.NodeID,
"channels", nodeInfo.Channels,
"listen_addr", nodeInfo.ListenAddr,
"transports", len(r.transports),
)
go r.dialPeers(ctx)
go r.evictPeers(ctx)
for _, transport := range r.transports {
go r.acceptPeers(ctx, transport)
}
go r.acceptPeers(ctx, r.transport)
return nil
}
@@ -985,10 +946,8 @@ func (r *Router) OnStart(ctx context.Context) error {
// sender's responsibility.
func (r *Router) OnStop() {
// Close transport listeners (unblocks Accept calls).
for _, transport := range r.transports {
if err := transport.Close(); err != nil {
r.logger.Error("failed to close transport", "transport", transport, "err", err)
}
if err := r.transport.Close(); err != nil {
r.logger.Error("failed to close transport", "err", err)
}
// Collect all remaining queues, and wait for them to close.
+24 -25
View File
@@ -105,14 +105,16 @@ func TestRouter_Channel_Basic(t *testing.T) {
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
require.NoError(t, err)
testnet := p2ptest.MakeNetwork(ctx, t, p2ptest.NetworkOptions{NumNodes: 1})
router, err := p2p.NewRouter(
log.NewNopLogger(),
p2p.NopMetrics(),
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
nil,
nil,
testnet.RandomNode().Transport,
&p2p.Endpoint{},
p2p.RouterOptions{},
)
require.NoError(t, err)
@@ -126,7 +128,6 @@ func TestRouter_Channel_Basic(t *testing.T) {
channel, err := router.OpenChannel(chctx, chDesc)
require.NoError(t, err)
require.Contains(t, router.NodeInfo().Channels, byte(chDesc.ID))
require.NotNil(t, channel)
// Opening the same channel again should fail.
@@ -136,9 +137,7 @@ func TestRouter_Channel_Basic(t *testing.T) {
// Opening a different channel should work.
chDesc2 := &p2p.ChannelDescriptor{ID: 2, MessageType: &p2ptest.Message{}}
_, err = router.OpenChannel(ctx, chDesc2)
require.NoError(t, err)
require.Contains(t, router.NodeInfo().Channels, byte(chDesc2.ID))
// Closing the channel, then opening it again should be fine.
chcancel()
@@ -396,10 +395,10 @@ func TestRouter_AcceptPeers(t *testing.T) {
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil).Maybe()
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
mockTransport.On("Listen", mock.Anything).Return(nil)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@@ -413,7 +412,7 @@ func TestRouter_AcceptPeers(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
@@ -453,9 +452,9 @@ func TestRouter_AcceptPeers_Error(t *testing.T) {
// the router from calling Accept again.
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Accept", mock.Anything).Once().Return(nil, errors.New("boom"))
mockTransport.On("Close").Return(nil)
mockTransport.On("Listen", mock.Anything).Return(nil)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@@ -467,7 +466,7 @@ func TestRouter_AcceptPeers_Error(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
@@ -490,9 +489,9 @@ func TestRouter_AcceptPeers_ErrorEOF(t *testing.T) {
// the router from calling Accept again.
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
mockTransport.On("Close").Return(nil)
mockTransport.On("Listen", mock.Anything).Return(nil)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@@ -504,7 +503,7 @@ func TestRouter_AcceptPeers_ErrorEOF(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
@@ -538,12 +537,12 @@ func TestRouter_AcceptPeers_HeadOfLineBlocking(t *testing.T) {
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Accept", mock.Anything).Times(3).Run(func(_ mock.Arguments) {
acceptCh <- true
}).Return(mockConnection, nil)
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
mockTransport.On("Listen", mock.Anything).Return(nil)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@@ -555,7 +554,7 @@ func TestRouter_AcceptPeers_HeadOfLineBlocking(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
@@ -611,7 +610,7 @@ func TestRouter_DialPeers(t *testing.T) {
defer cancel()
address := p2p.NodeAddress{Protocol: "mock", NodeID: tc.dialID}
endpoint := p2p.Endpoint{Protocol: "mock", Path: string(tc.dialID)}
endpoint := &p2p.Endpoint{Protocol: "mock", Path: string(tc.dialID)}
// Set up a mock transport that handshakes.
connCtx, connCancel := context.WithCancel(context.Background())
@@ -629,8 +628,8 @@ func TestRouter_DialPeers(t *testing.T) {
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil).Maybe()
mockTransport.On("Listen", mock.Anything).Return(nil)
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
if tc.dialErr == nil {
mockTransport.On("Dial", mock.Anything, endpoint).Once().Return(mockConnection, nil)
@@ -658,7 +657,7 @@ func TestRouter_DialPeers(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
@@ -711,11 +710,11 @@ func TestRouter_DialPeers_Parallel(t *testing.T) {
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Listen", mock.Anything).Return(nil)
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
for _, address := range []p2p.NodeAddress{a, b, c} {
endpoint := p2p.Endpoint{Protocol: address.Protocol, Path: string(address.NodeID)}
endpoint := &p2p.Endpoint{Protocol: address.Protocol, Path: string(address.NodeID)}
mockTransport.On("Dial", mock.Anything, endpoint).Run(func(_ mock.Arguments) {
dialCh <- true
}).Return(mockConnection, nil)
@@ -743,7 +742,7 @@ func TestRouter_DialPeers_Parallel(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{
DialSleep: func(_ context.Context) {},
@@ -800,10 +799,10 @@ func TestRouter_EvictPeers(t *testing.T) {
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
mockTransport.On("Listen", mock.Anything).Return(nil)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@@ -817,7 +816,7 @@ func TestRouter_EvictPeers(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
@@ -864,10 +863,10 @@ func TestRouter_ChannelCompatability(t *testing.T) {
mockTransport := &mocks.Transport{}
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
mockTransport.On("Listen", mock.Anything).Return(nil)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@@ -879,7 +878,7 @@ func TestRouter_ChannelCompatability(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
@@ -917,10 +916,10 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) {
mockTransport := &mocks.Transport{}
mockTransport.On("AddChannelDescriptors", mock.Anything).Return()
mockTransport.On("String").Maybe().Return("mock")
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
mockTransport.On("Listen", mock.Anything).Return(nil)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@@ -934,7 +933,7 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) {
selfKey,
peerManager,
func() *types.NodeInfo { return &selfInfo },
[]p2p.Transport{mockTransport},
mockTransport,
nil,
p2p.RouterOptions{},
)
+6 -6
View File
@@ -24,7 +24,7 @@ type Protocol string
// Transport is a connection-oriented mechanism for exchanging data with a peer.
type Transport interface {
// Listen starts the transport on the specified endpoint.
Listen(Endpoint) error
Listen(*Endpoint) error
// Protocols returns the protocols supported by the transport. The Router
// uses this to pick a transport for an Endpoint.
@@ -34,7 +34,7 @@ type Transport interface {
//
// How to listen is transport-dependent, e.g. MConnTransport uses Listen() while
// MemoryTransport starts listening via MemoryNetwork.CreateTransport().
Endpoints() []Endpoint
Endpoint() (*Endpoint, error)
// Accept waits for the next inbound connection on a listening endpoint, blocking
// until either a connection is available or the transport is closed. On closure,
@@ -42,7 +42,7 @@ type Transport interface {
Accept(context.Context) (Connection, error)
// Dial creates an outbound connection to an endpoint.
Dial(context.Context, Endpoint) (Connection, error)
Dial(context.Context, *Endpoint) (Connection, error)
// Close stops accepting new connections, but does not close active connections.
Close() error
@@ -129,13 +129,13 @@ type Endpoint struct {
}
// NewEndpoint constructs an Endpoint from a types.NetAddress structure.
func NewEndpoint(addr string) (Endpoint, error) {
func NewEndpoint(addr string) (*Endpoint, error) {
ip, port, err := types.ParseAddressString(addr)
if err != nil {
return Endpoint{}, err
return nil, err
}
return Endpoint{
return &Endpoint{
Protocol: MConnProtocol,
IP: ip,
Port: port,
+9 -9
View File
@@ -78,25 +78,25 @@ func (m *MConnTransport) Protocols() []Protocol {
return []Protocol{MConnProtocol, TCPProtocol}
}
// Endpoints implements Transport.
func (m *MConnTransport) Endpoints() []Endpoint {
// Endpoint implements Transport.
func (m *MConnTransport) Endpoint() (*Endpoint, error) {
if m.listener == nil {
return []Endpoint{}
return nil, errors.New("listenter not defined")
}
select {
case <-m.doneCh:
return []Endpoint{}
return nil, errors.New("transport closed")
default:
}
endpoint := Endpoint{
endpoint := &Endpoint{
Protocol: MConnProtocol,
}
if addr, ok := m.listener.Addr().(*net.TCPAddr); ok {
endpoint.IP = addr.IP
endpoint.Port = uint16(addr.Port)
}
return []Endpoint{endpoint}
return endpoint, nil
}
// Listen asynchronously listens for inbound connections on the given endpoint.
@@ -106,7 +106,7 @@ func (m *MConnTransport) Endpoints() []Endpoint {
// FIXME: Listen currently only supports listening on a single endpoint, it
// might be useful to support listening on multiple addresses (e.g. IPv4 and
// IPv6, or a private and public address) via multiple Listen() calls.
func (m *MConnTransport) Listen(endpoint Endpoint) error {
func (m *MConnTransport) Listen(endpoint *Endpoint) error {
if m.listener != nil {
return errors.New("transport is already listening")
}
@@ -170,7 +170,7 @@ func (m *MConnTransport) Accept(ctx context.Context) (Connection, error) {
}
// Dial implements Transport.
func (m *MConnTransport) Dial(ctx context.Context, endpoint Endpoint) (Connection, error) {
func (m *MConnTransport) Dial(ctx context.Context, endpoint *Endpoint) (Connection, error) {
if err := m.validateEndpoint(endpoint); err != nil {
return nil, err
}
@@ -217,7 +217,7 @@ func (m *MConnTransport) AddChannelDescriptors(channelDesc []*ChannelDescriptor)
}
// validateEndpoint validates an endpoint.
func (m *MConnTransport) validateEndpoint(endpoint Endpoint) error {
func (m *MConnTransport) validateEndpoint(endpoint *Endpoint) error {
if err := endpoint.Validate(); err != nil {
return err
}
+21 -18
View File
@@ -25,7 +25,7 @@ func init() {
[]*p2p.ChannelDescriptor{{ID: chID, Priority: 1}},
p2p.MConnTransportOptions{},
)
err := transport.Listen(p2p.Endpoint{
err := transport.Listen(&p2p.Endpoint{
Protocol: p2p.MConnProtocol,
IP: net.IPv4(127, 0, 0, 1),
Port: 0, // assign a random port
@@ -73,13 +73,14 @@ func TestMConnTransport_AcceptMaxAcceptedConnections(t *testing.T) {
t.Cleanup(func() {
_ = transport.Close()
})
err := transport.Listen(p2p.Endpoint{
err := transport.Listen(&p2p.Endpoint{
Protocol: p2p.MConnProtocol,
IP: net.IPv4(127, 0, 0, 1),
})
require.NoError(t, err)
require.NotEmpty(t, transport.Endpoints())
endpoint := transport.Endpoints()[0]
endpoint, err := transport.Endpoint()
require.NoError(t, err)
require.NotNil(t, endpoint)
// Start a goroutine to just accept any connections.
acceptCh := make(chan p2p.Connection, 10)
@@ -132,20 +133,20 @@ func TestMConnTransport_Listen(t *testing.T) {
defer cancel()
testcases := []struct {
endpoint p2p.Endpoint
endpoint *p2p.Endpoint
ok bool
}{
// Valid v4 and v6 addresses, with mconn and tcp protocols.
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero}, true},
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4(127, 0, 0, 1)}, true},
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6zero}, true},
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6loopback}, true},
{p2p.Endpoint{Protocol: p2p.TCPProtocol, IP: net.IPv4zero}, true},
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero}, true},
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4(127, 0, 0, 1)}, true},
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6zero}, true},
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6loopback}, true},
{&p2p.Endpoint{Protocol: p2p.TCPProtocol, IP: net.IPv4zero}, true},
// Invalid endpoints.
{p2p.Endpoint{}, false},
{p2p.Endpoint{Protocol: p2p.MConnProtocol, Path: "foo"}, false},
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero, Path: "foo"}, false},
{&p2p.Endpoint{}, false},
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, Path: "foo"}, false},
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero, Path: "foo"}, false},
}
for _, tc := range testcases {
tc := tc
@@ -160,10 +161,12 @@ func TestMConnTransport_Listen(t *testing.T) {
)
// Transport should not listen on any endpoints yet.
require.Empty(t, transport.Endpoints())
endpoint, err := transport.Endpoint()
require.Error(t, err)
require.Nil(t, endpoint)
// Start listening, and check any expected errors.
err := transport.Listen(tc.endpoint)
err = transport.Listen(tc.endpoint)
if !tc.ok {
require.Error(t, err)
return
@@ -171,9 +174,9 @@ func TestMConnTransport_Listen(t *testing.T) {
require.NoError(t, err)
// Check the endpoint.
endpoints := transport.Endpoints()
require.Len(t, endpoints, 1)
endpoint := endpoints[0]
endpoint, err = transport.Endpoint()
require.NoError(t, err)
require.NotNil(t, endpoint)
require.Equal(t, p2p.MConnProtocol, endpoint.Protocol)
if tc.endpoint.IP.IsUnspecified() {
+6 -6
View File
@@ -119,7 +119,7 @@ func (t *MemoryTransport) String() string {
return string(MemoryProtocol)
}
func (*MemoryTransport) Listen(Endpoint) error { return nil }
func (*MemoryTransport) Listen(*Endpoint) error { return nil }
func (t *MemoryTransport) AddChannelDescriptors([]*ChannelDescriptor) {}
@@ -129,19 +129,19 @@ func (t *MemoryTransport) Protocols() []Protocol {
}
// Endpoints implements Transport.
func (t *MemoryTransport) Endpoints() []Endpoint {
func (t *MemoryTransport) Endpoint() (*Endpoint, error) {
if n := t.network.GetTransport(t.nodeID); n == nil {
return []Endpoint{}
return nil, errors.New("node not defined")
}
return []Endpoint{{
return &Endpoint{
Protocol: MemoryProtocol,
Path: string(t.nodeID),
// An arbitrary IP and port is used in order for the pex
// reactor to be able to send addresses to one another.
IP: net.IPv4zero,
Port: 0,
}}
}, nil
}
// Accept implements Transport.
@@ -158,7 +158,7 @@ func (t *MemoryTransport) Accept(ctx context.Context) (Connection, error) {
}
// Dial implements Transport.
func (t *MemoryTransport) Dial(ctx context.Context, endpoint Endpoint) (Connection, error) {
func (t *MemoryTransport) Dial(ctx context.Context, endpoint *Endpoint) (Connection, error) {
if endpoint.Protocol != MemoryProtocol {
return nil, fmt.Errorf("invalid protocol %q", endpoint.Protocol)
}
+39 -31
View File
@@ -87,9 +87,9 @@ func TestTransport_DialEndpoints(t *testing.T) {
withTransports(ctx, t, func(ctx context.Context, t *testing.T, makeTransport transportFactory) {
a := makeTransport(t)
endpoints := a.Endpoints()
require.NotEmpty(t, endpoints)
endpoint := endpoints[0]
endpoint, err := a.Endpoint()
require.NoError(t, err)
require.NotNil(t, endpoint)
// Spawn a goroutine to simply accept any connections until closed.
go func() {
@@ -108,19 +108,19 @@ func TestTransport_DialEndpoints(t *testing.T) {
require.NoError(t, conn.Close())
// Dialing empty endpoint should error.
_, err = a.Dial(ctx, p2p.Endpoint{})
_, err = a.Dial(ctx, &p2p.Endpoint{})
require.Error(t, err)
// Dialing without protocol should error.
noProtocol := endpoint
noProtocol := *endpoint
noProtocol.Protocol = ""
_, err = a.Dial(ctx, noProtocol)
_, err = a.Dial(ctx, &noProtocol)
require.Error(t, err)
// Dialing with invalid protocol should error.
fooProtocol := endpoint
fooProtocol := *endpoint
fooProtocol.Protocol = "foo"
_, err = a.Dial(ctx, fooProtocol)
_, err = a.Dial(ctx, &fooProtocol)
require.Error(t, err)
// Tests for networked endpoints (with IP).
@@ -129,11 +129,12 @@ func TestTransport_DialEndpoints(t *testing.T) {
tc := tc
t.Run(tc.ip.String(), func(t *testing.T) {
e := endpoint
require.NotNil(t, e)
e.IP = tc.ip
conn, err := a.Dial(ctx, e)
if tc.ok {
require.NoError(t, conn.Close())
require.NoError(t, err)
require.NoError(t, conn.Close())
} else {
require.Error(t, err, "endpoint=%s", e)
}
@@ -167,16 +168,18 @@ func TestTransport_Dial(t *testing.T) {
a := makeTransport(t)
b := makeTransport(t)
require.NotEmpty(t, a.Endpoints())
require.NotEmpty(t, b.Endpoints())
aEndpoint := a.Endpoints()[0]
bEndpoint := b.Endpoints()[0]
aEndpoint, err := a.Endpoint()
require.NoError(t, err)
require.NotNil(t, aEndpoint)
bEndpoint, err := b.Endpoint()
require.NoError(t, err)
require.NotNil(t, bEndpoint)
// Context cancellation should error. We can't test timeouts since we'd
// need a non-responsive endpoint.
cancelCtx, cancel := context.WithCancel(ctx)
cancel()
_, err := a.Dial(cancelCtx, bEndpoint)
_, err = a.Dial(cancelCtx, bEndpoint)
require.Error(t, err)
// Unavailable endpoint should error.
@@ -210,21 +213,26 @@ func TestTransport_Endpoints(t *testing.T) {
b := makeTransport(t)
// Both transports return valid and different endpoints.
aEndpoints := a.Endpoints()
bEndpoints := b.Endpoints()
require.NotEmpty(t, aEndpoints)
require.NotEmpty(t, bEndpoints)
require.NotEqual(t, aEndpoints, bEndpoints)
for _, endpoint := range append(aEndpoints, bEndpoints...) {
aEndpoint, err := a.Endpoint()
require.NoError(t, err)
require.NotNil(t, aEndpoint)
bEndpoint, err := b.Endpoint()
require.NoError(t, err)
require.NotNil(t, bEndpoint)
require.NotEqual(t, aEndpoint, bEndpoint)
for _, endpoint := range []*p2p.Endpoint{aEndpoint, bEndpoint} {
err := endpoint.Validate()
require.NoError(t, err, "invalid endpoint %q", endpoint)
}
// When closed, the transport should no longer return any endpoints.
err := a.Close()
require.NoError(t, a.Close())
aEndpoint, err = a.Endpoint()
require.Error(t, err)
require.Nil(t, aEndpoint)
bEndpoint, err = b.Endpoint()
require.NoError(t, err)
require.Empty(t, a.Endpoints())
require.NotEmpty(t, b.Endpoints())
require.NotNil(t, bEndpoint)
})
}
@@ -235,13 +243,12 @@ func TestTransport_Protocols(t *testing.T) {
withTransports(ctx, t, func(ctx context.Context, t *testing.T, makeTransport transportFactory) {
a := makeTransport(t)
protocols := a.Protocols()
endpoints := a.Endpoints()
endpoint, err := a.Endpoint()
require.NoError(t, err)
require.NotEmpty(t, protocols)
require.NotEmpty(t, endpoints)
require.NotNil(t, endpoint)
for _, endpoint := range endpoints {
require.Contains(t, protocols, endpoint.Protocol)
}
require.Contains(t, protocols, endpoint.Protocol)
})
}
@@ -595,8 +602,9 @@ func TestEndpoint_Validate(t *testing.T) {
func dialAccept(ctx context.Context, t *testing.T, a, b p2p.Transport) (p2p.Connection, p2p.Connection) {
t.Helper()
endpoints := b.Endpoints()
require.NotEmpty(t, endpoints, "peer not listening on any endpoints")
endpoint, err := b.Endpoint()
require.NoError(t, err)
require.NotNil(t, endpoint, "peer not listening on any endpoints")
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
@@ -609,7 +617,7 @@ func dialAccept(ctx context.Context, t *testing.T, a, b p2p.Transport) (p2p.Conn
acceptCh <- conn
}()
dialConn, err := a.Dial(ctx, endpoints[0])
dialConn, err := a.Dial(ctx, endpoint)
require.NoError(t, err)
acceptConn := <-acceptCh
+14 -14
View File
@@ -124,32 +124,32 @@ func kill() error {
return p.Signal(syscall.SIGABRT)
}
func (app *proxyClient) InitChain(ctx context.Context, req types.RequestInitChain) (*types.ResponseInitChain, error) {
func (app *proxyClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "init_chain", "type", "sync"))()
return app.client.InitChain(ctx, req)
}
func (app *proxyClient) PrepareProposal(ctx context.Context, req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
func (app *proxyClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "prepare_proposal", "type", "sync"))()
return app.client.PrepareProposal(ctx, req)
}
func (app *proxyClient) ProcessProposal(ctx context.Context, req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
func (app *proxyClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "process_proposal", "type", "sync"))()
return app.client.ProcessProposal(ctx, req)
}
func (app *proxyClient) ExtendVote(ctx context.Context, req types.RequestExtendVote) (*types.ResponseExtendVote, error) {
func (app *proxyClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "extend_vote", "type", "sync"))()
return app.client.ExtendVote(ctx, req)
}
func (app *proxyClient) VerifyVoteExtension(ctx context.Context, req types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
func (app *proxyClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "verify_vote_extension", "type", "sync"))()
return app.client.VerifyVoteExtension(ctx, req)
}
func (app *proxyClient) FinalizeBlock(ctx context.Context, req types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
func (app *proxyClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "finalize_block", "type", "sync"))()
return app.client.FinalizeBlock(ctx, req)
}
@@ -164,7 +164,7 @@ func (app *proxyClient) Flush(ctx context.Context) error {
return app.client.Flush(ctx)
}
func (app *proxyClient) CheckTx(ctx context.Context, req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
func (app *proxyClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "check_tx", "type", "sync"))()
return app.client.CheckTx(ctx, req)
}
@@ -174,32 +174,32 @@ func (app *proxyClient) Echo(ctx context.Context, msg string) (*types.ResponseEc
return app.client.Echo(ctx, msg)
}
func (app *proxyClient) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) {
func (app *proxyClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "info", "type", "sync"))()
return app.client.Info(ctx, req)
}
func (app *proxyClient) Query(ctx context.Context, reqQuery types.RequestQuery) (*types.ResponseQuery, error) {
func (app *proxyClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "query", "type", "sync"))()
return app.client.Query(ctx, reqQuery)
return app.client.Query(ctx, req)
}
func (app *proxyClient) ListSnapshots(ctx context.Context, req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
func (app *proxyClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "list_snapshots", "type", "sync"))()
return app.client.ListSnapshots(ctx, req)
}
func (app *proxyClient) OfferSnapshot(ctx context.Context, req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
func (app *proxyClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "offer_snapshot", "type", "sync"))()
return app.client.OfferSnapshot(ctx, req)
}
func (app *proxyClient) LoadSnapshotChunk(ctx context.Context, req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
func (app *proxyClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "load_snapshot_chunk", "type", "sync"))()
return app.client.LoadSnapshotChunk(ctx, req)
}
func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
defer addTimeSample(app.metrics.MethodTiming.With("method", "apply_snapshot_chunk", "type", "sync"))()
return app.client.ApplySnapshotChunk(ctx, req)
}
+3 -3
View File
@@ -30,7 +30,7 @@ import (
type appConnTestI interface {
Echo(context.Context, string) (*types.ResponseEcho, error)
Flush(context.Context) error
Info(context.Context, types.RequestInfo) (*types.ResponseInfo, error)
Info(context.Context, *types.RequestInfo) (*types.ResponseInfo, error)
}
type appConnTest struct {
@@ -49,7 +49,7 @@ func (app *appConnTest) Flush(ctx context.Context) error {
return app.appConn.Flush(ctx)
}
func (app *appConnTest) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) {
func (app *appConnTest) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
return app.appConn.Info(ctx, req)
}
@@ -164,7 +164,7 @@ func TestInfo(t *testing.T) {
proxy := newAppConnTest(client)
t.Log("Connected")
resInfo, err := proxy.Info(ctx, RequestInfo)
resInfo, err := proxy.Info(ctx, &RequestInfo)
require.NoError(t, err)
if resInfo.Data != "{\"size\":0}" {
+7 -14
View File
@@ -5,24 +5,17 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/internal/proxy"
"github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/rpc/coretypes"
)
// ABCIQuery queries the application for some information.
// More: https://docs.tendermint.com/master/rpc/#/ABCI/abci_query
func (env *Environment) ABCIQuery(
ctx context.Context,
path string,
data bytes.HexBytes,
height int64,
prove bool,
) (*coretypes.ResultABCIQuery, error) {
resQuery, err := env.ProxyApp.Query(ctx, abci.RequestQuery{
Path: path,
Data: data,
Height: height,
Prove: prove,
func (env *Environment) ABCIQuery(ctx context.Context, req *coretypes.RequestABCIQuery) (*coretypes.ResultABCIQuery, error) {
resQuery, err := env.ProxyApp.Query(ctx, &abci.RequestQuery{
Path: req.Path,
Data: req.Data,
Height: int64(req.Height),
Prove: req.Prove,
})
if err != nil {
return nil, err
@@ -34,7 +27,7 @@ func (env *Environment) ABCIQuery(
// ABCIInfo gets some info about the application.
// More: https://docs.tendermint.com/master/rpc/#/ABCI/abci_info
func (env *Environment) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error) {
resInfo, err := env.ProxyApp.Info(ctx, proxy.RequestInfo)
resInfo, err := env.ProxyApp.Info(ctx, &proxy.RequestInfo)
if err != nil {
return nil, err
}
+25 -43
View File
@@ -7,7 +7,6 @@ import (
tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
"github.com/tendermint/tendermint/internal/state/indexer"
"github.com/tendermint/tendermint/libs/bytes"
tmmath "github.com/tendermint/tendermint/libs/math"
"github.com/tendermint/tendermint/rpc/coretypes"
"github.com/tendermint/tendermint/types"
@@ -23,17 +22,15 @@ import (
// order (highest first).
//
// More: https://docs.tendermint.com/master/rpc/#/Info/blockchain
func (env *Environment) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
const limit int64 = 20
var err error
minHeight, maxHeight, err = filterMinMax(
func (env *Environment) BlockchainInfo(ctx context.Context, req *coretypes.RequestBlockchainInfo) (*coretypes.ResultBlockchainInfo, error) {
const limit = 20
minHeight, maxHeight, err := filterMinMax(
env.BlockStore.Base(),
env.BlockStore.Height(),
minHeight,
maxHeight,
limit)
int64(req.MinHeight),
int64(req.MaxHeight),
limit,
)
if err != nil {
return nil, err
}
@@ -90,8 +87,8 @@ func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
// Block gets block at a given height.
// If no height is provided, it will fetch the latest block.
// More: https://docs.tendermint.com/master/rpc/#/Info/block
func (env *Environment) Block(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlock, error) {
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
func (env *Environment) Block(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlock, error) {
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
if err != nil {
return nil, err
}
@@ -107,12 +104,8 @@ func (env *Environment) Block(ctx context.Context, heightPtr *int64) (*coretypes
// BlockByHash gets block by hash.
// More: https://docs.tendermint.com/master/rpc/#/Info/block_by_hash
func (env *Environment) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) {
// N.B. The hash parameter is HexBytes so that the reflective parameter
// decoding logic in the HTTP service will correctly translate from JSON.
// See https://github.com/tendermint/tendermint/issues/6802 for context.
block := env.BlockStore.LoadBlockByHash(hash)
func (env *Environment) BlockByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultBlock, error) {
block := env.BlockStore.LoadBlockByHash(req.Hash)
if block == nil {
return &coretypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
}
@@ -124,8 +117,8 @@ func (env *Environment) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*
// Header gets block header at a given height.
// If no height is provided, it will fetch the latest header.
// More: https://docs.tendermint.com/master/rpc/#/Info/header
func (env *Environment) Header(ctx context.Context, heightPtr *int64) (*coretypes.ResultHeader, error) {
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
func (env *Environment) Header(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultHeader, error) {
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
if err != nil {
return nil, err
}
@@ -140,12 +133,8 @@ func (env *Environment) Header(ctx context.Context, heightPtr *int64) (*coretype
// HeaderByHash gets header by hash.
// More: https://docs.tendermint.com/master/rpc/#/Info/header_by_hash
func (env *Environment) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
// N.B. The hash parameter is HexBytes so that the reflective parameter
// decoding logic in the HTTP service will correctly translate from JSON.
// See https://github.com/tendermint/tendermint/issues/6802 for context.
blockMeta := env.BlockStore.LoadBlockMetaByHash(hash)
func (env *Environment) HeaderByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultHeader, error) {
blockMeta := env.BlockStore.LoadBlockMetaByHash(req.Hash)
if blockMeta == nil {
return &coretypes.ResultHeader{}, nil
}
@@ -156,8 +145,8 @@ func (env *Environment) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (
// Commit gets block commit at a given height.
// If no height is provided, it will fetch the commit for the latest block.
// More: https://docs.tendermint.com/master/rpc/#/Info/commit
func (env *Environment) Commit(ctx context.Context, heightPtr *int64) (*coretypes.ResultCommit, error) {
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
func (env *Environment) Commit(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultCommit, error) {
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
if err != nil {
return nil, err
}
@@ -192,8 +181,8 @@ func (env *Environment) Commit(ctx context.Context, heightPtr *int64) (*coretype
//
// Results are for the height of the block containing the txs.
// More: https://docs.tendermint.com/master/rpc/#/Info/block_results
func (env *Environment) BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error) {
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
func (env *Environment) BlockResults(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlockResults, error) {
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
if err != nil {
return nil, err
}
@@ -218,20 +207,13 @@ func (env *Environment) BlockResults(ctx context.Context, heightPtr *int64) (*co
}, nil
}
// BlockSearch searches for a paginated set of blocks matching the provided
// query.
func (env *Environment) BlockSearch(
ctx context.Context,
query string,
pagePtr, perPagePtr *int,
orderBy string,
) (*coretypes.ResultBlockSearch, error) {
// BlockSearch searches for a paginated set of blocks matching the provided query.
func (env *Environment) BlockSearch(ctx context.Context, req *coretypes.RequestBlockSearch) (*coretypes.ResultBlockSearch, error) {
if !indexer.KVSinkEnabled(env.EventSinks) {
return nil, fmt.Errorf("block searching is disabled due to no kvEventSink")
}
q, err := tmquery.New(query)
q, err := tmquery.New(req.Query)
if err != nil {
return nil, err
}
@@ -249,7 +231,7 @@ func (env *Environment) BlockSearch(
}
// sort results (must be done before pagination)
switch orderBy {
switch req.OrderBy {
case "desc", "":
sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
@@ -262,9 +244,9 @@ func (env *Environment) BlockSearch(
// paginate results
totalCount := len(results)
perPage := env.validatePerPage(perPagePtr)
perPage := env.validatePerPage(req.PerPage.IntPtr())
page, err := validatePage(pagePtr, perPage, totalCount)
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
if err != nil {
return nil, err
}
+3 -1
View File
@@ -109,7 +109,9 @@ func TestBlockResults(t *testing.T) {
ctx := context.Background()
for _, tc := range testCases {
res, err := env.BlockResults(ctx, &tc.height)
res, err := env.BlockResults(ctx, &coretypes.RequestBlockInfo{
Height: (*coretypes.Int64)(&tc.height),
})
if tc.wantErr {
assert.Error(t, err)
} else {
+10 -11
View File
@@ -14,10 +14,9 @@ import (
// for the validators in the set as used in computing their Merkle root.
//
// More: https://docs.tendermint.com/master/rpc/#/Info/validators
func (env *Environment) Validators(ctx context.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*coretypes.ResultValidators, error) {
func (env *Environment) Validators(ctx context.Context, req *coretypes.RequestValidators) (*coretypes.ResultValidators, error) {
// The latest validator that we know is the NextValidator of the last block.
height, err := env.getHeight(env.latestUncommittedHeight(), heightPtr)
height, err := env.getHeight(env.latestUncommittedHeight(), (*int64)(req.Height))
if err != nil {
return nil, err
}
@@ -28,8 +27,8 @@ func (env *Environment) Validators(ctx context.Context, heightPtr *int64, pagePt
}
totalCount := len(validators.Validators)
perPage := env.validatePerPage(perPagePtr)
page, err := validatePage(pagePtr, perPage, totalCount)
perPage := env.validatePerPage(req.PerPage.IntPtr())
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
if err != nil {
return nil, err
}
@@ -42,7 +41,8 @@ func (env *Environment) Validators(ctx context.Context, heightPtr *int64, pagePt
BlockHeight: height,
Validators: v,
Count: len(v),
Total: totalCount}, nil
Total: totalCount,
}, nil
}
// DumpConsensusState dumps consensus state.
@@ -99,11 +99,10 @@ func (env *Environment) GetConsensusState(ctx context.Context) (*coretypes.Resul
// ConsensusParams gets the consensus parameters at the given block height.
// If no height is provided, it will fetch the latest consensus params.
// More: https://docs.tendermint.com/master/rpc/#/Info/consensus_params
func (env *Environment) ConsensusParams(ctx context.Context, heightPtr *int64) (*coretypes.ResultConsensusParams, error) {
// The latest consensus params that we know is the consensus params after the
// last block.
height, err := env.getHeight(env.latestUncommittedHeight(), heightPtr)
func (env *Environment) ConsensusParams(ctx context.Context, req *coretypes.RequestConsensusParams) (*coretypes.ResultConsensusParams, error) {
// The latest consensus params that we know is the consensus params after
// the last block.
height, err := env.getHeight(env.latestUncommittedHeight(), (*int64)(req.Height))
if err != nil {
return nil, err
}
+23 -17
View File
@@ -26,7 +26,7 @@ const (
// Subscribe for events via WebSocket.
// More: https://docs.tendermint.com/master/rpc/#/Websocket/subscribe
func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes.ResultSubscribe, error) {
func (env *Environment) Subscribe(ctx context.Context, req *coretypes.RequestSubscribe) (*coretypes.ResultSubscribe, error) {
callInfo := rpctypes.GetCallInfo(ctx)
addr := callInfo.RemoteAddr()
@@ -34,15 +34,15 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
return nil, fmt.Errorf("max_subscription_clients %d reached", env.Config.MaxSubscriptionClients)
} else if env.EventBus.NumClientSubscriptions(addr) >= env.Config.MaxSubscriptionsPerClient {
return nil, fmt.Errorf("max_subscriptions_per_client %d reached", env.Config.MaxSubscriptionsPerClient)
} else if len(query) > maxQueryLength {
} else if len(req.Query) > maxQueryLength {
return nil, errors.New("maximum query length exceeded")
}
env.Logger.Info("WARNING: Websocket subscriptions are deprecated and will be removed " +
"in Tendermint v0.37. See https://tinyurl.com/adr075 for more information.")
env.Logger.Info("Subscribe to query", "remote", addr, "query", query)
env.Logger.Info("Subscribe to query", "remote", addr, "query", req.Query)
q, err := tmquery.New(query)
q, err := tmquery.New(req.Query)
if err != nil {
return nil, fmt.Errorf("failed to parse query: %w", err)
}
@@ -83,7 +83,7 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
// We have a message to deliver to the client.
resp := callInfo.RPCRequest.MakeResponse(&coretypes.ResultEvent{
Query: query,
Query: req.Query,
Data: msg.Data(),
Events: msg.Events(),
})
@@ -102,15 +102,15 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
// Unsubscribe from events via WebSocket.
// More: https://docs.tendermint.com/master/rpc/#/Websocket/unsubscribe
func (env *Environment) Unsubscribe(ctx context.Context, query string) (*coretypes.ResultUnsubscribe, error) {
func (env *Environment) Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error) {
args := tmpubsub.UnsubscribeArgs{Subscriber: rpctypes.GetCallInfo(ctx).RemoteAddr()}
env.Logger.Info("Unsubscribe from query", "remote", args.Subscriber, "subscription", query)
env.Logger.Info("Unsubscribe from query", "remote", args.Subscriber, "subscription", req.Query)
var err error
args.Query, err = tmquery.New(query)
args.Query, err = tmquery.New(req.Query)
if err != nil {
args.ID = query
args.ID = req.Query
}
err = env.EventBus.Unsubscribe(ctx, args)
@@ -148,17 +148,13 @@ func (env *Environment) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUn
// If maxItems ≤ 0, a default positive number of events is chosen. The values
// of maxItems and waitTime may be capped to sensible internal maxima without
// reporting an error to the caller.
func (env *Environment) Events(ctx context.Context,
filter *coretypes.EventFilter,
maxItems int,
before, after cursor.Cursor,
waitTime time.Duration,
) (*coretypes.ResultEvents, error) {
func (env *Environment) Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error) {
if env.EventLog == nil {
return nil, errors.New("the event log is not enabled")
}
// Parse and validate parameters.
maxItems := req.MaxItems
if maxItems <= 0 {
maxItems = 10
} else if maxItems > 100 {
@@ -167,6 +163,8 @@ func (env *Environment) Events(ctx context.Context,
const minWaitTime = 1 * time.Second
const maxWaitTime = 30 * time.Second
waitTime := req.WaitTime
if waitTime < minWaitTime {
waitTime = minWaitTime
} else if waitTime > maxWaitTime {
@@ -174,14 +172,22 @@ func (env *Environment) Events(ctx context.Context,
}
query := tmquery.All
if filter != nil && filter.Query != "" {
q, err := tmquery.New(filter.Query)
if req.Filter != nil && req.Filter.Query != "" {
q, err := tmquery.New(req.Filter.Query)
if err != nil {
return nil, fmt.Errorf("invalid filter query: %w", err)
}
query = q
}
var before, after cursor.Cursor
if err := before.UnmarshalText([]byte(req.Before)); err != nil {
return nil, fmt.Errorf("invalid cursor %q: %w", req.Before, err)
}
if err := after.UnmarshalText([]byte(req.After)); err != nil {
return nil, fmt.Errorf("invalid cursor %q: %w", req.After, err)
}
var info eventlog.Info
var items []*eventlog.Item
var err error
+5 -8
View File
@@ -9,18 +9,15 @@ import (
// BroadcastEvidence broadcasts evidence of the misbehavior.
// More: https://docs.tendermint.com/master/rpc/#/Evidence/broadcast_evidence
func (env *Environment) BroadcastEvidence(
ctx context.Context,
ev coretypes.Evidence,
) (*coretypes.ResultBroadcastEvidence, error) {
if ev.Value == nil {
func (env *Environment) BroadcastEvidence(ctx context.Context, req *coretypes.RequestBroadcastEvidence) (*coretypes.ResultBroadcastEvidence, error) {
if req.Evidence == nil {
return nil, fmt.Errorf("%w: no evidence was provided", coretypes.ErrInvalidRequest)
}
if err := ev.Value.ValidateBasic(); err != nil {
if err := req.Evidence.ValidateBasic(); err != nil {
return nil, fmt.Errorf("evidence.ValidateBasic failed: %w", err)
}
if err := env.EvidencePool.AddEvidence(ctx, ev.Value); err != nil {
if err := env.EvidencePool.AddEvidence(ctx, req.Evidence); err != nil {
return nil, fmt.Errorf("failed to add evidence: %w", err)
}
return &coretypes.ResultBroadcastEvidence{Hash: ev.Value.Hash()}, nil
return &coretypes.ResultBroadcastEvidence{Hash: req.Evidence.Hash()}, nil
}
+26 -24
View File
@@ -12,7 +12,6 @@ import (
"github.com/tendermint/tendermint/internal/state/indexer"
tmmath "github.com/tendermint/tendermint/libs/math"
"github.com/tendermint/tendermint/rpc/coretypes"
"github.com/tendermint/tendermint/types"
)
//-----------------------------------------------------------------------------
@@ -21,23 +20,23 @@ import (
// BroadcastTxAsync returns right away, with no response. Does not wait for
// CheckTx nor DeliverTx results.
// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_async
func (env *Environment) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
err := env.Mempool.CheckTx(ctx, tx, nil, mempool.TxInfo{})
func (env *Environment) BroadcastTxAsync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
err := env.Mempool.CheckTx(ctx, req.Tx, nil, mempool.TxInfo{})
if err != nil {
return nil, err
}
return &coretypes.ResultBroadcastTx{Hash: tx.Hash()}, nil
return &coretypes.ResultBroadcastTx{Hash: req.Tx.Hash()}, nil
}
// BroadcastTxSync returns with the response from CheckTx. Does not wait for
// DeliverTx result.
// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_sync
func (env *Environment) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
func (env *Environment) BroadcastTxSync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
resCh := make(chan *abci.ResponseCheckTx, 1)
err := env.Mempool.CheckTx(
ctx,
tx,
req.Tx,
func(res *abci.ResponseCheckTx) {
select {
case <-ctx.Done():
@@ -60,19 +59,18 @@ func (env *Environment) BroadcastTxSync(ctx context.Context, tx types.Tx) (*core
Log: r.Log,
Codespace: r.Codespace,
MempoolError: r.MempoolError,
Hash: tx.Hash(),
Hash: req.Tx.Hash(),
}, nil
}
}
// BroadcastTxCommit returns with the responses from CheckTx and DeliverTx.
// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_commit
func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) {
func (env *Environment) BroadcastTxCommit(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTxCommit, error) {
resCh := make(chan *abci.ResponseCheckTx, 1)
err := env.Mempool.CheckTx(
ctx,
tx,
req.Tx,
func(res *abci.ResponseCheckTx) {
select {
case <-ctx.Done():
@@ -92,14 +90,14 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
if r.Code != abci.CodeTypeOK {
return &coretypes.ResultBroadcastTxCommit{
CheckTx: *r,
Hash: tx.Hash(),
Hash: req.Tx.Hash(),
}, fmt.Errorf("transaction encountered error (%s)", r.MempoolError)
}
if !indexer.KVSinkEnabled(env.EventSinks) {
return &coretypes.ResultBroadcastTxCommit{
CheckTx: *r,
Hash: tx.Hash(),
Hash: req.Tx.Hash(),
},
errors.New("cannot confirm transaction because kvEventSink is not enabled")
}
@@ -118,11 +116,14 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
"err", err)
return &coretypes.ResultBroadcastTxCommit{
CheckTx: *r,
Hash: tx.Hash(),
Hash: req.Tx.Hash(),
}, fmt.Errorf("timeout waiting for commit of tx %s (%s)",
tx.Hash(), time.Since(startAt))
req.Tx.Hash(), time.Since(startAt))
case <-timer.C:
txres, err := env.Tx(ctx, tx.Hash(), false)
txres, err := env.Tx(ctx, &coretypes.RequestTx{
Hash: req.Tx.Hash(),
Prove: false,
})
if err != nil {
jitter := 100*time.Millisecond + time.Duration(rand.Int63n(int64(time.Second))) // nolint: gosec
backoff := 100 * time.Duration(count) * time.Millisecond
@@ -133,7 +134,7 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
return &coretypes.ResultBroadcastTxCommit{
CheckTx: *r,
TxResult: txres.TxResult,
Hash: tx.Hash(),
Hash: req.Tx.Hash(),
Height: txres.Height,
}, nil
}
@@ -143,10 +144,10 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
// UnconfirmedTxs gets unconfirmed transactions from the mempool in order of priority
// More: https://docs.tendermint.com/master/rpc/#/Info/unconfirmed_txs
func (env *Environment) UnconfirmedTxs(ctx context.Context, pagePtr, perPagePtr *int) (*coretypes.ResultUnconfirmedTxs, error) {
func (env *Environment) UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) {
totalCount := env.Mempool.Size()
perPage := env.validatePerPage(perPagePtr)
page, err := validatePage(pagePtr, perPage, totalCount)
perPage := env.validatePerPage(req.PerPage.IntPtr())
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
if err != nil {
return nil, err
}
@@ -160,7 +161,8 @@ func (env *Environment) UnconfirmedTxs(ctx context.Context, pagePtr, perPagePtr
Count: len(result),
Total: totalCount,
TotalBytes: env.Mempool.SizeBytes(),
Txs: result}, nil
Txs: result,
}, nil
}
// NumUnconfirmedTxs gets number of unconfirmed transactions.
@@ -175,14 +177,14 @@ func (env *Environment) NumUnconfirmedTxs(ctx context.Context) (*coretypes.Resul
// CheckTx checks the transaction without executing it. The transaction won't
// be added to the mempool either.
// More: https://docs.tendermint.com/master/rpc/#/Tx/check_tx
func (env *Environment) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) {
res, err := env.ProxyApp.CheckTx(ctx, abci.RequestCheckTx{Tx: tx})
func (env *Environment) CheckTx(ctx context.Context, req *coretypes.RequestCheckTx) (*coretypes.ResultCheckTx, error) {
res, err := env.ProxyApp.CheckTx(ctx, &abci.RequestCheckTx{Tx: req.Tx})
if err != nil {
return nil, err
}
return &coretypes.ResultCheckTx{ResponseCheckTx: *res}, nil
}
func (env *Environment) RemoveTx(ctx context.Context, txkey types.TxKey) error {
return env.Mempool.RemoveTxByKey(txkey)
func (env *Environment) RemoveTx(ctx context.Context, req *coretypes.RequestRemoveTx) error {
return env.Mempool.RemoveTxByKey(req.TxKey)
}
+2 -2
View File
@@ -44,7 +44,7 @@ func (env *Environment) Genesis(ctx context.Context) (*coretypes.ResultGenesis,
return &coretypes.ResultGenesis{Genesis: env.GenDoc}, nil
}
func (env *Environment) GenesisChunked(ctx context.Context, chunk uint) (*coretypes.ResultGenesisChunk, error) {
func (env *Environment) GenesisChunked(ctx context.Context, req *coretypes.RequestGenesisChunked) (*coretypes.ResultGenesisChunk, error) {
if env.genChunks == nil {
return nil, fmt.Errorf("service configuration error, genesis chunks are not initialized")
}
@@ -53,7 +53,7 @@ func (env *Environment) GenesisChunked(ctx context.Context, chunk uint) (*corety
return nil, fmt.Errorf("service configuration error, there are no chunks")
}
id := int(chunk)
id := int(req.Chunk)
if id > len(env.genChunks)-1 {
return nil, fmt.Errorf("there are %d chunks, %d is invalid", len(env.genChunks)-1, id)
+48 -52
View File
@@ -2,13 +2,9 @@ package core
import (
"context"
"time"
"github.com/tendermint/tendermint/internal/eventlog/cursor"
"github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/rpc/coretypes"
rpc "github.com/tendermint/tendermint/rpc/jsonrpc/server"
"github.com/tendermint/tendermint/types"
)
// TODO: better system than "unsafe" prefix
@@ -32,47 +28,47 @@ func NewRoutesMap(svc RPCService, opts *RouteOptions) RoutesMap {
out := RoutesMap{
// Event subscription. Note that subscribe, unsubscribe, and
// unsubscribe_all are only available via the websocket endpoint.
"events": rpc.NewRPCFunc(svc.Events, "filter", "maxItems", "before", "after", "waitTime"),
"subscribe": rpc.NewWSRPCFunc(svc.Subscribe, "query"),
"unsubscribe": rpc.NewWSRPCFunc(svc.Unsubscribe, "query"),
"events": rpc.NewRPCFunc(svc.Events),
"subscribe": rpc.NewWSRPCFunc(svc.Subscribe),
"unsubscribe": rpc.NewWSRPCFunc(svc.Unsubscribe),
"unsubscribe_all": rpc.NewWSRPCFunc(svc.UnsubscribeAll),
// info API
"health": rpc.NewRPCFunc(svc.Health),
"status": rpc.NewRPCFunc(svc.Status),
"net_info": rpc.NewRPCFunc(svc.NetInfo),
"blockchain": rpc.NewRPCFunc(svc.BlockchainInfo, "minHeight", "maxHeight"),
"blockchain": rpc.NewRPCFunc(svc.BlockchainInfo),
"genesis": rpc.NewRPCFunc(svc.Genesis),
"genesis_chunked": rpc.NewRPCFunc(svc.GenesisChunked, "chunk"),
"header": rpc.NewRPCFunc(svc.Header, "height"),
"header_by_hash": rpc.NewRPCFunc(svc.HeaderByHash, "hash"),
"block": rpc.NewRPCFunc(svc.Block, "height"),
"block_by_hash": rpc.NewRPCFunc(svc.BlockByHash, "hash"),
"block_results": rpc.NewRPCFunc(svc.BlockResults, "height"),
"commit": rpc.NewRPCFunc(svc.Commit, "height"),
"check_tx": rpc.NewRPCFunc(svc.CheckTx, "tx"),
"remove_tx": rpc.NewRPCFunc(svc.RemoveTx, "txkey"),
"tx": rpc.NewRPCFunc(svc.Tx, "hash", "prove"),
"tx_search": rpc.NewRPCFunc(svc.TxSearch, "query", "prove", "page", "per_page", "order_by"),
"block_search": rpc.NewRPCFunc(svc.BlockSearch, "query", "page", "per_page", "order_by"),
"validators": rpc.NewRPCFunc(svc.Validators, "height", "page", "per_page"),
"genesis_chunked": rpc.NewRPCFunc(svc.GenesisChunked),
"header": rpc.NewRPCFunc(svc.Header),
"header_by_hash": rpc.NewRPCFunc(svc.HeaderByHash),
"block": rpc.NewRPCFunc(svc.Block),
"block_by_hash": rpc.NewRPCFunc(svc.BlockByHash),
"block_results": rpc.NewRPCFunc(svc.BlockResults),
"commit": rpc.NewRPCFunc(svc.Commit),
"check_tx": rpc.NewRPCFunc(svc.CheckTx),
"remove_tx": rpc.NewRPCFunc(svc.RemoveTx),
"tx": rpc.NewRPCFunc(svc.Tx),
"tx_search": rpc.NewRPCFunc(svc.TxSearch),
"block_search": rpc.NewRPCFunc(svc.BlockSearch),
"validators": rpc.NewRPCFunc(svc.Validators),
"dump_consensus_state": rpc.NewRPCFunc(svc.DumpConsensusState),
"consensus_state": rpc.NewRPCFunc(svc.GetConsensusState),
"consensus_params": rpc.NewRPCFunc(svc.ConsensusParams, "height"),
"unconfirmed_txs": rpc.NewRPCFunc(svc.UnconfirmedTxs, "page", "per_page"),
"consensus_params": rpc.NewRPCFunc(svc.ConsensusParams),
"unconfirmed_txs": rpc.NewRPCFunc(svc.UnconfirmedTxs),
"num_unconfirmed_txs": rpc.NewRPCFunc(svc.NumUnconfirmedTxs),
// tx broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(svc.BroadcastTxCommit, "tx"),
"broadcast_tx_sync": rpc.NewRPCFunc(svc.BroadcastTxSync, "tx"),
"broadcast_tx_async": rpc.NewRPCFunc(svc.BroadcastTxAsync, "tx"),
"broadcast_tx_commit": rpc.NewRPCFunc(svc.BroadcastTxCommit),
"broadcast_tx_sync": rpc.NewRPCFunc(svc.BroadcastTxSync),
"broadcast_tx_async": rpc.NewRPCFunc(svc.BroadcastTxAsync),
// abci API
"abci_query": rpc.NewRPCFunc(svc.ABCIQuery, "path", "data", "height", "prove"),
"abci_query": rpc.NewRPCFunc(svc.ABCIQuery),
"abci_info": rpc.NewRPCFunc(svc.ABCIInfo),
// evidence API
"broadcast_evidence": rpc.NewRPCFunc(svc.BroadcastEvidence, "evidence"),
"broadcast_evidence": rpc.NewRPCFunc(svc.BroadcastEvidence),
}
if u, ok := svc.(RPCUnsafe); ok && opts.Unsafe {
out["unsafe_flush_mempool"] = rpc.NewRPCFunc(u.UnsafeFlushMempool)
@@ -84,38 +80,38 @@ func NewRoutesMap(svc RPCService, opts *RouteOptions) RoutesMap {
// implementation, for use in constructing a routing table.
type RPCService interface {
ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error)
ABCIQuery(ctx context.Context, path string, data bytes.HexBytes, height int64, prove bool) (*coretypes.ResultABCIQuery, error)
Block(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlock, error)
BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error)
BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error)
BlockSearch(ctx context.Context, query string, pagePtr, perPagePtr *int, orderBy string) (*coretypes.ResultBlockSearch, error)
BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error)
BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error)
BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)
BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error)
BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)
CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error)
Commit(ctx context.Context, heightPtr *int64) (*coretypes.ResultCommit, error)
ConsensusParams(ctx context.Context, heightPtr *int64) (*coretypes.ResultConsensusParams, error)
ABCIQuery(ctx context.Context, req *coretypes.RequestABCIQuery) (*coretypes.ResultABCIQuery, error)
Block(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlock, error)
BlockByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultBlock, error)
BlockResults(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlockResults, error)
BlockSearch(ctx context.Context, req *coretypes.RequestBlockSearch) (*coretypes.ResultBlockSearch, error)
BlockchainInfo(ctx context.Context, req *coretypes.RequestBlockchainInfo) (*coretypes.ResultBlockchainInfo, error)
BroadcastEvidence(ctx context.Context, req *coretypes.RequestBroadcastEvidence) (*coretypes.ResultBroadcastEvidence, error)
BroadcastTxAsync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error)
BroadcastTxCommit(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTxCommit, error)
BroadcastTxSync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error)
CheckTx(ctx context.Context, req *coretypes.RequestCheckTx) (*coretypes.ResultCheckTx, error)
Commit(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultCommit, error)
ConsensusParams(ctx context.Context, req *coretypes.RequestConsensusParams) (*coretypes.ResultConsensusParams, error)
DumpConsensusState(ctx context.Context) (*coretypes.ResultDumpConsensusState, error)
Events(ctx context.Context, filter *coretypes.EventFilter, maxItems int, before, after cursor.Cursor, waitTime time.Duration) (*coretypes.ResultEvents, error)
Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error)
Genesis(ctx context.Context) (*coretypes.ResultGenesis, error)
GenesisChunked(ctx context.Context, chunk uint) (*coretypes.ResultGenesisChunk, error)
GenesisChunked(ctx context.Context, req *coretypes.RequestGenesisChunked) (*coretypes.ResultGenesisChunk, error)
GetConsensusState(ctx context.Context) (*coretypes.ResultConsensusState, error)
Header(ctx context.Context, heightPtr *int64) (*coretypes.ResultHeader, error)
HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error)
Header(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultHeader, error)
HeaderByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultHeader, error)
Health(ctx context.Context) (*coretypes.ResultHealth, error)
NetInfo(ctx context.Context) (*coretypes.ResultNetInfo, error)
NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error)
RemoveTx(ctx context.Context, txkey types.TxKey) error
RemoveTx(ctx context.Context, req *coretypes.RequestRemoveTx) error
Status(ctx context.Context) (*coretypes.ResultStatus, error)
Subscribe(ctx context.Context, query string) (*coretypes.ResultSubscribe, error)
Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error)
TxSearch(ctx context.Context, query string, prove bool, pagePtr, perPagePtr *int, orderBy string) (*coretypes.ResultTxSearch, error)
UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error)
Unsubscribe(ctx context.Context, query string) (*coretypes.ResultUnsubscribe, error)
Subscribe(ctx context.Context, req *coretypes.RequestSubscribe) (*coretypes.ResultSubscribe, error)
Tx(ctx context.Context, req *coretypes.RequestTx) (*coretypes.ResultTx, error)
TxSearch(ctx context.Context, req *coretypes.RequestTxSearch) (*coretypes.ResultTxSearch, error)
UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error)
Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error)
UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error)
Validators(ctx context.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*coretypes.ResultValidators, error)
Validators(ctx context.Context, req *coretypes.RequestValidators) (*coretypes.ResultValidators, error)
}
// RPCUnsafe defines the set of "unsafe" methods that may optionally be
+12 -25
View File
@@ -8,7 +8,6 @@ import (
tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
"github.com/tendermint/tendermint/internal/state/indexer"
"github.com/tendermint/tendermint/libs/bytes"
tmmath "github.com/tendermint/tendermint/libs/math"
"github.com/tendermint/tendermint/rpc/coretypes"
"github.com/tendermint/tendermint/types"
@@ -18,32 +17,27 @@ import (
// transaction is in the mempool, invalidated, or was not sent in the first
// place.
// More: https://docs.tendermint.com/master/rpc/#/Info/tx
func (env *Environment) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error) {
func (env *Environment) Tx(ctx context.Context, req *coretypes.RequestTx) (*coretypes.ResultTx, error) {
// if index is disabled, return error
// N.B. The hash parameter is HexBytes so that the reflective parameter
// decoding logic in the HTTP service will correctly translate from JSON.
// See https://github.com/tendermint/tendermint/issues/6802 for context.
if !indexer.KVSinkEnabled(env.EventSinks) {
return nil, errors.New("transaction querying is disabled due to no kvEventSink")
}
for _, sink := range env.EventSinks {
if sink.Type() == indexer.KV {
r, err := sink.GetTxByHash(hash)
r, err := sink.GetTxByHash(req.Hash)
if r == nil {
return nil, fmt.Errorf("tx (%X) not found, err: %w", hash, err)
return nil, fmt.Errorf("tx (%X) not found, err: %w", req.Hash, err)
}
var proof types.TxProof
if prove {
if req.Prove {
block := env.BlockStore.LoadBlock(r.Height)
proof = block.Data.Txs.Proof(int(r.Index))
}
return &coretypes.ResultTx{
Hash: hash,
Hash: req.Hash,
Height: r.Height,
Index: r.Index,
TxResult: r.Result,
@@ -59,21 +53,14 @@ func (env *Environment) Tx(ctx context.Context, hash bytes.HexBytes, prove bool)
// TxSearch allows you to query for multiple transactions results. It returns a
// list of transactions (maximum ?per_page entries) and the total count.
// More: https://docs.tendermint.com/master/rpc/#/Info/tx_search
func (env *Environment) TxSearch(
ctx context.Context,
query string,
prove bool,
pagePtr, perPagePtr *int,
orderBy string,
) (*coretypes.ResultTxSearch, error) {
func (env *Environment) TxSearch(ctx context.Context, req *coretypes.RequestTxSearch) (*coretypes.ResultTxSearch, error) {
if !indexer.KVSinkEnabled(env.EventSinks) {
return nil, fmt.Errorf("transaction searching is disabled due to no kvEventSink")
} else if len(query) > maxQueryLength {
} else if len(req.Query) > maxQueryLength {
return nil, errors.New("maximum query length exceeded")
}
q, err := tmquery.New(query)
q, err := tmquery.New(req.Query)
if err != nil {
return nil, err
}
@@ -86,7 +73,7 @@ func (env *Environment) TxSearch(
}
// sort results (must be done before pagination)
switch orderBy {
switch req.OrderBy {
case "desc", "":
sort.Slice(results, func(i, j int) bool {
if results[i].Height == results[j].Height {
@@ -107,9 +94,9 @@ func (env *Environment) TxSearch(
// paginate results
totalCount := len(results)
perPage := env.validatePerPage(perPagePtr)
perPage := env.validatePerPage(req.PerPage.IntPtr())
page, err := validatePage(pagePtr, perPage, totalCount)
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
if err != nil {
return nil, err
}
@@ -122,7 +109,7 @@ func (env *Environment) TxSearch(
r := results[i]
var proof types.TxProof
if prove {
if req.Prove {
block := env.BlockStore.LoadBlock(r.Height)
proof = block.Data.Txs.Proof(int(r.Index))
}
+52 -26
View File
@@ -7,6 +7,7 @@ import (
abciclient "github.com/tendermint/tendermint/abci/client"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/internal/eventbus"
@@ -105,7 +106,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
localLastCommit := buildLastCommitInfo(block, blockExec.store, state.InitialHeight)
rpp, err := blockExec.appClient.PrepareProposal(
ctx,
abci.RequestPrepareProposal{
&abci.RequestPrepareProposal{
MaxTxBytes: maxDataBytes,
Txs: block.Txs.ToSliceOfBytes(),
LocalLastCommit: extendedCommitInfo(localLastCommit, votes),
@@ -147,7 +148,7 @@ func (blockExec *BlockExecutor) ProcessProposal(
block *types.Block,
state State,
) (bool, error) {
req := abci.RequestProcessProposal{
resp, err := blockExec.appClient.ProcessProposal(ctx, &abci.RequestProcessProposal{
Hash: block.Header.Hash(),
Height: block.Header.Height,
Time: block.Header.Time,
@@ -156,9 +157,7 @@ func (blockExec *BlockExecutor) ProcessProposal(
ByzantineValidators: block.Evidence.ToABCI(),
ProposerAddress: block.ProposerAddress,
NextValidatorsHash: block.NextValidatorsHash,
}
resp, err := blockExec.appClient.ProcessProposal(ctx, req)
})
if err != nil {
return false, ErrInvalidBlock(err)
}
@@ -210,7 +209,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
startTime := time.Now().UnixNano()
finalizeBlockResponse, err := blockExec.appClient.FinalizeBlock(
ctx,
abci.RequestFinalizeBlock{
&abci.RequestFinalizeBlock{
Hash: block.Hash(),
Height: block.Header.Height,
Time: block.Header.Time,
@@ -248,6 +247,10 @@ func (blockExec *BlockExecutor) ApplyBlock(
}
if len(validatorUpdates) > 0 {
blockExec.logger.Debug("updates to validators", "updates", types.ValidatorListString(validatorUpdates))
blockExec.metrics.ValidatorSetUpdates.Add(1)
}
if finalizeBlockResponse.ConsensusParamUpdates != nil {
blockExec.metrics.ConsensusParamUpdates.Add(1)
}
// Update the state with the block and responses.
@@ -296,29 +299,29 @@ func (blockExec *BlockExecutor) ApplyBlock(
return state, nil
}
func (blockExec *BlockExecutor) ExtendVote(ctx context.Context, vote *types.Vote) (types.VoteExtension, error) {
req := abci.RequestExtendVote{
Vote: vote.ToProto(),
}
resp, err := blockExec.appClient.ExtendVote(ctx, req)
func (blockExec *BlockExecutor) ExtendVote(ctx context.Context, vote *types.Vote) ([]byte, error) {
resp, err := blockExec.appClient.ExtendVote(ctx, &abci.RequestExtendVote{
Hash: vote.BlockID.Hash,
Height: vote.Height,
})
if err != nil {
return types.VoteExtension{}, err
panic(fmt.Errorf("ExtendVote call failed: %w", err))
}
return types.VoteExtensionFromProto(resp.VoteExtension), nil
return resp.VoteExtension, nil
}
func (blockExec *BlockExecutor) VerifyVoteExtension(ctx context.Context, vote *types.Vote) error {
req := abci.RequestVerifyVoteExtension{
Vote: vote.ToProto(),
}
resp, err := blockExec.appClient.VerifyVoteExtension(ctx, req)
resp, err := blockExec.appClient.VerifyVoteExtension(ctx, &abci.RequestVerifyVoteExtension{
Hash: vote.BlockID.Hash,
ValidatorAddress: vote.ValidatorAddress,
Height: vote.Height,
VoteExtension: vote.Extension,
})
if err != nil {
return err
panic(fmt.Errorf("VerifyVoteExtension call failed: %w", err))
}
if resp.IsErr() {
if !resp.IsOK() {
return types.ErrVoteInvalidExtension
}
@@ -417,16 +420,39 @@ func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) a
}
}
// extendedCommitInfo expects a CommitInfo struct along with all of the
// original votes relating to that commit, including their vote extensions. The
// order of votes does not matter.
func extendedCommitInfo(c abci.CommitInfo, votes []*types.Vote) abci.ExtendedCommitInfo {
if len(c.Votes) != len(votes) {
panic(fmt.Sprintf("extendedCommitInfo: number of votes from commit differ from the number of votes supplied (%d != %d)", len(c.Votes), len(votes)))
}
votesByVal := make(map[string]*types.Vote)
for _, vote := range votes {
if vote != nil {
valAddr := vote.ValidatorAddress.String()
if _, ok := votesByVal[valAddr]; ok {
panic(fmt.Sprintf("extendedCommitInfo: found duplicate vote for validator with address %s", valAddr))
}
votesByVal[valAddr] = vote
}
}
vs := make([]abci.ExtendedVoteInfo, len(c.Votes))
for i := range vs {
var ext []byte
// votes[i] will be nil if c.Votes[i].SignedLastBlock is false
if c.Votes[i].SignedLastBlock {
valAddr := crypto.Address(c.Votes[i].Validator.Address).String()
vote, ok := votesByVal[valAddr]
if !ok || vote == nil {
panic(fmt.Sprintf("extendedCommitInfo: validator with address %s signed last block, but could not find vote for it", valAddr))
}
ext = vote.Extension
}
vs[i] = abci.ExtendedVoteInfo{
Validator: c.Votes[i].Validator,
SignedLastBlock: c.Votes[i].SignedLastBlock,
/*
TODO: Include vote extensions information when implementing vote extensions.
VoteExtension: []byte{},
*/
VoteExtension: ext,
}
}
return abci.ExtendedCommitInfo{
@@ -608,7 +634,7 @@ func ExecCommitBlock(
) ([]byte, error) {
finalizeBlockResponse, err := appConn.FinalizeBlock(
ctx,
abci.RequestFinalizeBlock{
&abci.RequestFinalizeBlock{
Hash: block.Hash(),
Height: block.Height,
Time: block.Time,
+45 -46
View File
@@ -18,7 +18,6 @@ import (
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/eventbus"
mpmocks "github.com/tendermint/tendermint/internal/mempool/mocks"
"github.com/tendermint/tendermint/internal/proxy"
@@ -180,14 +179,14 @@ func TestFinalizeBlockByzantineValidators(t *testing.T) {
Height: 10,
Time: defaultEvidenceTime,
LastBlockID: blockID,
LastCommitHash: crypto.CRandBytes(tmhash.Size),
DataHash: crypto.CRandBytes(tmhash.Size),
LastCommitHash: crypto.CRandBytes(crypto.HashSize),
DataHash: crypto.CRandBytes(crypto.HashSize),
ValidatorsHash: state.Validators.Hash(),
NextValidatorsHash: state.Validators.Hash(),
ConsensusHash: crypto.CRandBytes(tmhash.Size),
AppHash: crypto.CRandBytes(tmhash.Size),
LastResultsHash: crypto.CRandBytes(tmhash.Size),
EvidenceHash: crypto.CRandBytes(tmhash.Size),
ConsensusHash: crypto.CRandBytes(crypto.HashSize),
AppHash: crypto.CRandBytes(crypto.HashSize),
LastResultsHash: crypto.CRandBytes(crypto.HashSize),
EvidenceHash: crypto.CRandBytes(crypto.HashSize),
ProposerAddress: crypto.CRandBytes(crypto.AddressSize),
}
@@ -277,7 +276,7 @@ func TestProcessProposal(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
app := abcimocks.NewBaseMock()
app := abcimocks.NewApplication(t)
logger := log.NewNopLogger()
cc := abciclient.NewLocalClient(logger, app)
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
@@ -329,7 +328,7 @@ func TestProcessProposal(t *testing.T) {
block1 := sf.MakeBlock(state, height, lastCommit)
block1.Txs = txs
expectedRpp := abci.RequestProcessProposal{
expectedRpp := &abci.RequestProcessProposal{
Txs: block1.Txs.ToSliceOfBytes(),
Hash: block1.Hash(),
Height: block1.Header.Height,
@@ -343,12 +342,12 @@ func TestProcessProposal(t *testing.T) {
ProposerAddress: block1.ProposerAddress,
}
app.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT})
app.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
acceptBlock, err := blockExec.ProcessProposal(ctx, block1, state)
require.NoError(t, err)
require.True(t, acceptBlock)
app.AssertExpectations(t)
app.AssertCalled(t, "ProcessProposal", expectedRpp)
app.AssertCalled(t, "ProcessProposal", ctx, expectedRpp)
}
func TestValidateValidatorUpdates(t *testing.T) {
@@ -621,8 +620,8 @@ func TestEmptyPrepareProposal(t *testing.T) {
eventBus := eventbus.NewDefault(logger)
require.NoError(t, eventBus.Start(ctx))
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{})
app := abcimocks.NewApplication(t)
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
cc := abciclient.NewLocalClient(logger, app)
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
err := proxyApp.Start(ctx)
@@ -654,8 +653,8 @@ func TestEmptyPrepareProposal(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
_, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
_, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
require.NoError(t, err)
}
@@ -680,10 +679,10 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) {
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
app := abcimocks.NewBaseMock()
app := abcimocks.NewApplication(t)
// create an invalid ResponsePrepareProposal
rpp := abci.ResponsePrepareProposal{
rpp := &abci.ResponsePrepareProposal{
TxRecords: []*abci.TxRecord{
{
Action: abci.TxRecord_REMOVED,
@@ -691,7 +690,7 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) {
},
},
}
app.On("PrepareProposal", mock.Anything).Return(rpp)
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(rpp, nil)
cc := abciclient.NewLocalClient(logger, app)
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
@@ -709,10 +708,10 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil)
require.Nil(t, block)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
require.ErrorContains(t, err, "new transaction incorrectly marked as removed")
require.Nil(t, block)
mp.AssertExpectations(t)
}
@@ -744,10 +743,10 @@ func TestPrepareProposalRemoveTxs(t *testing.T) {
trs[1].Action = abci.TxRecord_REMOVED
mp.On("RemoveTxByKey", mock.Anything).Return(nil).Twice()
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
app := abcimocks.NewApplication(t)
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
TxRecords: trs,
})
}, nil)
cc := abciclient.NewLocalClient(logger, app)
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
@@ -765,8 +764,8 @@ func TestPrepareProposalRemoveTxs(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
require.NoError(t, err)
require.Len(t, block.Data.Txs.ToSliceOfBytes(), len(trs)-2)
@@ -803,10 +802,10 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
trs[0].Action = abci.TxRecord_ADDED
trs[1].Action = abci.TxRecord_ADDED
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
app := abcimocks.NewApplication(t)
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
TxRecords: trs,
})
}, nil)
cc := abciclient.NewLocalClient(logger, app)
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
@@ -824,8 +823,8 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
require.NoError(t, err)
require.Equal(t, txs[0], block.Data.Txs[0])
@@ -859,10 +858,10 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
trs = trs[2:]
trs = append(trs[len(trs)/2:], trs[:len(trs)/2]...)
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
app := abcimocks.NewApplication(t)
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
TxRecords: trs,
})
}, nil)
cc := abciclient.NewLocalClient(logger, app)
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
@@ -880,8 +879,8 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
require.NoError(t, err)
for i, tx := range block.Data.Txs {
require.Equal(t, types.Tx(trs[i].Tx), tx)
@@ -919,10 +918,10 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
trs := txsToTxRecords(types.Txs(txs))
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
app := abcimocks.NewApplication(t)
app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{
TxRecords: trs,
})
}, nil)
cc := abciclient.NewLocalClient(logger, app)
proxyApp := proxy.New(cc, logger, proxy.NopMetrics())
@@ -940,11 +939,11 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil)
require.Nil(t, block)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
require.ErrorContains(t, err, "transaction data size exceeds maximum")
require.Nil(t, block, "")
mp.AssertExpectations(t)
}
@@ -992,9 +991,9 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
require.Nil(t, block)
require.ErrorContains(t, err, "an injected error")
@@ -1003,8 +1002,8 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID {
var (
h = make([]byte, tmhash.Size)
psH = make([]byte, tmhash.Size)
h = make([]byte, crypto.HashSize)
psH = make([]byte, crypto.HashSize)
)
copy(h, hash)
copy(psH, partSetHash)
+21 -19
View File
@@ -46,7 +46,7 @@ func makeAndCommitGoodBlock(
state, blockID := makeAndApplyGoodBlock(ctx, t, state, height, lastCommit, proposerAddr, blockExec, evidence)
// Simulate a lastCommit for this block from all validators for the next height
commit := makeValidCommit(ctx, t, height, blockID, state.Validators, privVals)
commit, _ := makeValidCommit(ctx, t, height, blockID, state.Validators, privVals)
return state, blockID, commit
}
@@ -82,17 +82,19 @@ func makeValidCommit(
blockID types.BlockID,
vals *types.ValidatorSet,
privVals map[string]types.PrivValidator,
) *types.Commit {
) (*types.Commit, []*types.Vote) {
t.Helper()
sigs := make([]types.CommitSig, 0)
sigs := make([]types.CommitSig, vals.Size())
votes := make([]*types.Vote, vals.Size())
for i := 0; i < vals.Size(); i++ {
_, val := vals.GetByIndex(int32(i))
vote, err := factory.MakeVote(ctx, privVals[val.Address.String()], chainID, int32(i), height, 0, 2, blockID, time.Now())
require.NoError(t, err)
sigs = append(sigs, vote.CommitSig())
sigs[i] = vote.CommitSig()
votes[i] = vote
}
return types.NewCommit(height, 0, blockID, sigs)
return types.NewCommit(height, 0, blockID, sigs), votes
}
func makeState(t *testing.T, nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) {
@@ -276,11 +278,11 @@ type testApp struct {
var _ abci.Application = (*testApp)(nil)
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
return abci.ResponseInfo{}
func (app *testApp) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) {
return &abci.ResponseInfo{}, nil
}
func (app *testApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock {
func (app *testApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
app.CommitVotes = req.DecidedLastCommit.Votes
app.ByzantineValidators = req.ByzantineValidators
@@ -293,7 +295,7 @@ func (app *testApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFi
}
}
return abci.ResponseFinalizeBlock{
return &abci.ResponseFinalizeBlock{
ValidatorUpdates: app.ValidatorUpdates,
ConsensusParamUpdates: &tmproto.ConsensusParams{
Version: &tmproto.VersionParams{
@@ -302,26 +304,26 @@ func (app *testApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFi
},
Events: []abci.Event{},
TxResults: resTxs,
}
}, nil
}
func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
return abci.ResponseCheckTx{}
func (app *testApp) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) {
return &abci.ResponseCheckTx{}, nil
}
func (app *testApp) Commit() abci.ResponseCommit {
return abci.ResponseCommit{RetainHeight: 1}
func (app *testApp) Commit(context.Context) (*abci.ResponseCommit, error) {
return &abci.ResponseCommit{RetainHeight: 1}, nil
}
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
return
func (app *testApp) Query(_ context.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error) {
return &abci.ResponseQuery{}, nil
}
func (app *testApp) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal {
func (app *testApp) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
for _, tx := range req.Txs {
if len(tx) == 0 {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
}
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}
+51
View File
@@ -0,0 +1,51 @@
// Code generated by metricsgen. DO NOT EDIT.
package indexer
import (
"github.com/go-kit/kit/metrics/discard"
prometheus "github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
labels := []string{}
for i := 0; i < len(labelsAndValues); i += 2 {
labels = append(labels, labelsAndValues[i])
}
return &Metrics{
BlockEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "block_events_seconds",
Help: "Latency for indexing block events.",
}, labels).With(labelsAndValues...),
TxEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "tx_events_seconds",
Help: "Latency for indexing transaction events.",
}, labels).With(labelsAndValues...),
BlocksIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "blocks_indexed",
Help: "Number of complete blocks indexed.",
}, labels).With(labelsAndValues...),
TransactionsIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "transactions_indexed",
Help: "Number of transactions indexed.",
}, labels).With(labelsAndValues...),
}
}
func NopMetrics() *Metrics {
return &Metrics{
BlockEventsSeconds: discard.NewHistogram(),
TxEventsSeconds: discard.NewHistogram(),
BlocksIndexed: discard.NewCounter(),
TransactionsIndexed: discard.NewCounter(),
}
}
+2 -50
View File
@@ -2,12 +2,10 @@ package indexer
import (
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/discard"
prometheus "github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
//go:generate go run github.com/tendermint/tendermint/scripts/metricsgen -struct=Metrics
// MetricsSubsystem is a the subsystem label for the indexer package.
const MetricsSubsystem = "indexer"
@@ -25,49 +23,3 @@ type Metrics struct {
// Number of transactions indexed.
TransactionsIndexed metrics.Counter
}
// PrometheusMetrics returns Metrics build using Prometheus client library.
// Optionally, labels can be provided along with their values ("foo",
// "fooValue").
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
labels := []string{}
for i := 0; i < len(labelsAndValues); i += 2 {
labels = append(labels, labelsAndValues[i])
}
return &Metrics{
BlockEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "block_events_seconds",
Help: "Latency for indexing block events.",
}, labels).With(labelsAndValues...),
TxEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "tx_events_seconds",
Help: "Latency for indexing transaction events.",
}, labels).With(labelsAndValues...),
BlocksIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "blocks_indexed",
Help: "Number of complete blocks indexed.",
}, labels).With(labelsAndValues...),
TransactionsIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "transactions_indexed",
Help: "Number of transactions indexed.",
}, labels).With(labelsAndValues...),
}
}
// NopMetrics returns an indexer metrics stub that discards all samples.
func NopMetrics() *Metrics {
return &Metrics{
BlockEventsSeconds: discard.NewHistogram(),
TxEventsSeconds: discard.NewHistogram(),
BlocksIndexed: discard.NewCounter(),
TransactionsIndexed: discard.NewCounter(),
}
}
@@ -12,6 +12,8 @@ import (
tenderminttypes "github.com/tendermint/tendermint/types"
testing "testing"
types "github.com/tendermint/tendermint/abci/types"
)
@@ -165,3 +167,13 @@ func (_m *EventSink) Type() indexer.EventSinkType {
return r0
}
// NewEventSink creates a new instance of EventSink. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewEventSink(t testing.TB) *EventSink {
mock := &EventSink{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+26 -1
View File
@@ -17,6 +17,14 @@ const (
type Metrics struct {
// Time between BeginBlock and EndBlock.
BlockProcessingTime metrics.Histogram
// ConsensusParamUpdates is the total number of times the application has
// udated the consensus params since process start.
ConsensusParamUpdates metrics.Counter
// ValidatorSetUpdates is the total number of times the application has
// udated the validator set since process start.
ValidatorSetUpdates metrics.Counter
}
// PrometheusMetrics returns Metrics build using Prometheus client library.
@@ -35,12 +43,29 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
Help: "Time between BeginBlock and EndBlock in ms.",
Buckets: stdprometheus.LinearBuckets(1, 10, 10),
}, labels).With(labelsAndValues...),
ConsensusParamUpdates: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "consensus_param_updates",
Help: "The total number of times the application as updated the consensus " +
"parameters since process start.",
}, labels).With(labelsAndValues...),
ValidatorSetUpdates: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "validator_set_updates",
Help: "The total number of times the application as updated the validator " +
"set since process start.",
}, labels).With(labelsAndValues...),
}
}
// NopMetrics returns no-op Metrics.
func NopMetrics() *Metrics {
return &Metrics{
BlockProcessingTime: discard.NewHistogram(),
BlockProcessingTime: discard.NewHistogram(),
ConsensusParamUpdates: discard.NewCounter(),
ValidatorSetUpdates: discard.NewCounter(),
}
}
+12
View File
@@ -5,6 +5,8 @@ package mocks
import (
mock "github.com/stretchr/testify/mock"
testing "testing"
types "github.com/tendermint/tendermint/types"
)
@@ -208,3 +210,13 @@ func (_m *BlockStore) Size() int64 {
return r0
}
// NewBlockStore creates a new instance of BlockStore. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewBlockStore(t testing.TB) *BlockStore {
mock := &BlockStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+12
View File
@@ -8,6 +8,8 @@ import (
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/internal/state"
testing "testing"
types "github.com/tendermint/tendermint/types"
)
@@ -71,3 +73,13 @@ func (_m *EvidencePool) PendingEvidence(maxBytes int64) ([]types.Evidence, int64
func (_m *EvidencePool) Update(_a0 context.Context, _a1 state.State, _a2 types.EvidenceList) {
_m.Called(_a0, _a1, _a2)
}
// NewEvidencePool creates a new instance of EvidencePool. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewEvidencePool(t testing.TB) *EvidencePool {
mock := &EvidencePool{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+12
View File
@@ -7,6 +7,8 @@ import (
state "github.com/tendermint/tendermint/internal/state"
tendermintstate "github.com/tendermint/tendermint/proto/tendermint/state"
testing "testing"
types "github.com/tendermint/tendermint/types"
)
@@ -186,3 +188,13 @@ func (_m *Store) SaveValidatorSets(_a0 int64, _a1 int64, _a2 *types.ValidatorSet
return r0
}
// NewStore creates a new instance of Store. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewStore(t testing.TB) *Store {
mock := &Store{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+2 -2
View File
@@ -12,8 +12,8 @@ import (
abciclient "github.com/tendermint/tendermint/abci/client"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/internal/eventbus"
mpmocks "github.com/tendermint/tendermint/internal/mempool/mocks"
"github.com/tendermint/tendermint/internal/proxy"
@@ -68,7 +68,7 @@ func TestValidateBlockHeader(t *testing.T) {
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
// some bad values
wrongHash := tmhash.Sum([]byte("this hash is wrong"))
wrongHash := crypto.Checksum([]byte("this hash is wrong"))
wrongVersion1 := state.Version.Consensus
wrongVersion1.Block += 2
wrongVersion2 := state.Version.Consensus
+1 -1
View File
@@ -30,7 +30,7 @@ func testChannel(size int) (*channelInternal, *p2p.Channel) {
Out: make(chan p2p.Envelope, size),
Error: make(chan p2p.PeerError, size),
}
return in, p2p.NewChannel(0, nil, in.In, in.Out, in.Error)
return in, p2p.NewChannel(0, in.In, in.Out, in.Error)
}
func TestDispatcherBasic(t *testing.T) {
@@ -8,6 +8,8 @@ import (
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/internal/state"
testing "testing"
types "github.com/tendermint/tendermint/types"
)
@@ -82,3 +84,13 @@ func (_m *StateProvider) State(ctx context.Context, height uint64) (state.State,
return r0, r1
}
// NewStateProvider creates a new instance of StateProvider. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewStateProvider(t testing.TB) *StateProvider {
mock := &StateProvider{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
+13 -9
View File
@@ -305,7 +305,12 @@ func (r *Reactor) OnStart(ctx context.Context) error {
return nil
}
go r.processChannels(ctx, snapshotCh, chunkCh, blockCh, paramsCh)
go r.processChannels(ctx, map[p2p.ChannelID]*p2p.Channel{
SnapshotChannel: snapshotCh,
ChunkChannel: chunkCh,
LightBlockChannel: blockCh,
ParamsChannel: paramsCh,
})
go r.processPeerUpdates(ctx, r.peerEvents(ctx))
if r.needsStateSync {
@@ -661,7 +666,7 @@ func (r *Reactor) handleSnapshotMessage(ctx context.Context, envelope *p2p.Envel
"failed to add snapshot",
"height", msg.Height,
"format", msg.Format,
"channel", snapshotCh.ID,
"channel", envelope.ChannelID,
"err", err,
)
return nil
@@ -688,7 +693,7 @@ func (r *Reactor) handleChunkMessage(ctx context.Context, envelope *p2p.Envelope
"chunk", msg.Index,
"peer", envelope.From,
)
resp, err := r.conn.LoadSnapshotChunk(ctx, abci.RequestLoadSnapshotChunk{
resp, err := r.conn.LoadSnapshotChunk(ctx, &abci.RequestLoadSnapshotChunk{
Height: msg.Height,
Format: msg.Format,
Chunk: msg.Index,
@@ -907,15 +912,14 @@ func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, cha
// encountered during message execution will result in a PeerError being sent on
// the respective channel. When the reactor is stopped, we will catch the signal
// and close the p2p Channel gracefully.
func (r *Reactor) processChannels(ctx context.Context, chs ...*p2p.Channel) {
func (r *Reactor) processChannels(ctx context.Context, chanTable map[p2p.ChannelID]*p2p.Channel) {
// make sure that the iterator gets cleaned up in case of error
ctx, cancel := context.WithCancel(ctx)
defer cancel()
chanTable := make(map[p2p.ChannelID]*p2p.Channel, len(chs))
for idx := range chs {
ch := chs[idx]
chanTable[ch.ID] = ch
chs := make([]*p2p.Channel, 0, len(chanTable))
for key := range chanTable {
chs = append(chs, chanTable[key])
}
iter := p2p.MergedChannelIterator(ctx, chs...)
@@ -1015,7 +1019,7 @@ func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerU
// recentSnapshots fetches the n most recent snapshots from the app
func (r *Reactor) recentSnapshots(ctx context.Context, n uint32) ([]*snapshot, error) {
resp, err := r.conn.ListSnapshots(ctx, abci.RequestListSnapshots{})
resp, err := r.conn.ListSnapshots(ctx, &abci.RequestListSnapshots{})
if err != nil {
return nil, err
}
+5 -9
View File
@@ -102,7 +102,6 @@ func setup(
rts.snapshotChannel = p2p.NewChannel(
SnapshotChannel,
new(ssproto.Message),
rts.snapshotInCh,
rts.snapshotOutCh,
rts.snapshotPeerErrCh,
@@ -110,7 +109,6 @@ func setup(
rts.chunkChannel = p2p.NewChannel(
ChunkChannel,
new(ssproto.Message),
rts.chunkInCh,
rts.chunkOutCh,
rts.chunkPeerErrCh,
@@ -118,7 +116,6 @@ func setup(
rts.blockChannel = p2p.NewChannel(
LightBlockChannel,
new(ssproto.Message),
rts.blockInCh,
rts.blockOutCh,
rts.blockPeerErrCh,
@@ -126,7 +123,6 @@ func setup(
rts.paramsChannel = p2p.NewChannel(
ParamsChannel,
new(ssproto.Message),
rts.paramsInCh,
rts.paramsOutCh,
rts.paramsPeerErrCh,
@@ -204,15 +200,15 @@ func TestReactor_Sync(t *testing.T) {
rts := setup(ctx, t, nil, nil, 100)
chain := buildLightBlockChain(ctx, t, 1, 10, time.Now())
// app accepts any snapshot
rts.conn.On("OfferSnapshot", ctx, mock.AnythingOfType("types.RequestOfferSnapshot")).
rts.conn.On("OfferSnapshot", ctx, mock.IsType(&abci.RequestOfferSnapshot{})).
Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, nil)
// app accepts every chunk
rts.conn.On("ApplySnapshotChunk", ctx, mock.AnythingOfType("types.RequestApplySnapshotChunk")).
rts.conn.On("ApplySnapshotChunk", ctx, mock.IsType(&abci.RequestApplySnapshotChunk{})).
Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
// app query returns valid state app hash
rts.conn.On("Info", mock.Anything, proxy.RequestInfo).Return(&abci.ResponseInfo{
rts.conn.On("Info", mock.Anything, &proxy.RequestInfo).Return(&abci.ResponseInfo{
AppVersion: testAppVersion,
LastBlockHeight: snapshotHeight,
LastBlockAppHash: chain[snapshotHeight+1].AppHash,
@@ -307,7 +303,7 @@ func TestReactor_ChunkRequest(t *testing.T) {
// mock ABCI connection to return local snapshots
conn := &clientmocks.Client{}
conn.On("LoadSnapshotChunk", mock.Anything, abci.RequestLoadSnapshotChunk{
conn.On("LoadSnapshotChunk", mock.Anything, &abci.RequestLoadSnapshotChunk{
Height: tc.request.Height,
Format: tc.request.Format,
Chunk: tc.request.Index,
@@ -396,7 +392,7 @@ func TestReactor_SnapshotsRequest(t *testing.T) {
// mock ABCI connection to return local snapshots
conn := &clientmocks.Client{}
conn.On("ListSnapshots", mock.Anything, abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{
conn.On("ListSnapshots", mock.Anything, &abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{
Snapshots: tc.snapshots,
}, nil)
+3 -3
View File
@@ -337,7 +337,7 @@ func (s *syncer) Sync(ctx context.Context, snapshot *snapshot, chunks *chunkQueu
func (s *syncer) offerSnapshot(ctx context.Context, snapshot *snapshot) error {
s.logger.Info("Offering snapshot to ABCI app", "height", snapshot.Height,
"format", snapshot.Format, "hash", snapshot.Hash)
resp, err := s.conn.OfferSnapshot(ctx, abci.RequestOfferSnapshot{
resp, err := s.conn.OfferSnapshot(ctx, &abci.RequestOfferSnapshot{
Snapshot: &abci.Snapshot{
Height: snapshot.Height,
Format: snapshot.Format,
@@ -379,7 +379,7 @@ func (s *syncer) applyChunks(ctx context.Context, chunks *chunkQueue, start time
return fmt.Errorf("failed to fetch chunk: %w", err)
}
resp, err := s.conn.ApplySnapshotChunk(ctx, abci.RequestApplySnapshotChunk{
resp, err := s.conn.ApplySnapshotChunk(ctx, &abci.RequestApplySnapshotChunk{
Index: chunk.Index,
Chunk: chunk.Chunk,
Sender: string(chunk.Sender),
@@ -519,7 +519,7 @@ func (s *syncer) requestChunk(ctx context.Context, snapshot *snapshot, chunk uin
// verifyApp verifies the sync, checking the app hash, last block height and app version
func (s *syncer) verifyApp(ctx context.Context, snapshot *snapshot, appVersion uint64) error {
resp, err := s.conn.Info(ctx, proxy.RequestInfo)
resp, err := s.conn.Info(ctx, &proxy.RequestInfo)
if err != nil {
return fmt.Errorf("failed to query ABCI app for appHash: %w", err)
}
+27 -27
View File
@@ -109,7 +109,7 @@ func TestSyncer_SyncAny(t *testing.T) {
// We start a sync, with peers sending back chunks when requested. We first reject the snapshot
// with height 2 format 2, and accept the snapshot at height 1.
conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: &abci.Snapshot{
Height: 2,
Format: 2,
@@ -118,7 +118,7 @@ func TestSyncer_SyncAny(t *testing.T) {
},
AppHash: []byte("app_hash_2"),
}).Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_FORMAT}, nil)
conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: &abci.Snapshot{
Height: s.Height,
Format: s.Format,
@@ -170,7 +170,7 @@ func TestSyncer_SyncAny(t *testing.T) {
// The first time we're applying chunk 2 we tell it to retry the snapshot and discard chunk 1,
// which should cause it to keep the existing chunk 0 and 2, and restart restoration from
// beginning. We also wait for a little while, to exercise the retry logic in fetchChunks().
conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 2, Chunk: []byte{1, 1, 2},
}).Once().Run(func(args mock.Arguments) { time.Sleep(1 * time.Second) }).Return(
&abci.ResponseApplySnapshotChunk{
@@ -178,16 +178,16 @@ func TestSyncer_SyncAny(t *testing.T) {
RefetchChunks: []uint32{1},
}, nil)
conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 0, Chunk: []byte{1, 1, 0},
}).Times(2).Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 1, Chunk: []byte{1, 1, 1},
}).Times(2).Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 2, Chunk: []byte{1, 1, 2},
}).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
conn.On("Info", mock.Anything, proxy.RequestInfo).Return(&abci.ResponseInfo{
conn.On("Info", mock.Anything, &proxy.RequestInfo).Return(&abci.ResponseInfo{
AppVersion: testAppVersion,
LastBlockHeight: 1,
LastBlockAppHash: []byte("app_hash"),
@@ -247,7 +247,7 @@ func TestSyncer_SyncAny_abort(t *testing.T) {
_, err := rts.syncer.AddSnapshot(peerID, s)
require.NoError(t, err)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, nil)
@@ -281,15 +281,15 @@ func TestSyncer_SyncAny_reject(t *testing.T) {
_, err = rts.syncer.AddSnapshot(peerID, s11)
require.NoError(t, err)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s22), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s12), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s11), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil)
@@ -323,11 +323,11 @@ func TestSyncer_SyncAny_reject_format(t *testing.T) {
_, err = rts.syncer.AddSnapshot(peerID, s11)
require.NoError(t, err)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s22), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_FORMAT}, nil)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s11), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, nil)
@@ -372,11 +372,11 @@ func TestSyncer_SyncAny_reject_sender(t *testing.T) {
_, err = rts.syncer.AddSnapshot(peerCID, sbc)
require.NoError(t, err)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(sbc), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_SENDER}, nil)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(sa), AppHash: []byte("app_hash"),
}).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil)
@@ -402,7 +402,7 @@ func TestSyncer_SyncAny_abciError(t *testing.T) {
_, err := rts.syncer.AddSnapshot(peerID, s)
require.NoError(t, err)
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s), AppHash: []byte("app_hash"),
}).Once().Return(nil, errBoom)
@@ -445,7 +445,7 @@ func TestSyncer_offerSnapshot(t *testing.T) {
rts := setup(ctx, t, nil, stateProvider, 2)
s := &snapshot{Height: 1, Format: 1, Chunks: 3, Hash: []byte{1, 2, 3}, trustedAppHash: []byte("app_hash")}
rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{
rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{
Snapshot: toABCI(s),
AppHash: []byte("app_hash"),
}).Return(&abci.ResponseOfferSnapshot{Result: tc.result}, tc.err)
@@ -506,11 +506,11 @@ func TestSyncer_applyChunks_Results(t *testing.T) {
_, err = chunks.Add(&chunk{Height: 1, Format: 1, Index: 0, Chunk: body})
require.NoError(t, err)
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 0, Chunk: body,
}).Once().Return(&abci.ResponseApplySnapshotChunk{Result: tc.result}, tc.err)
if tc.result == abci.ResponseApplySnapshotChunk_RETRY {
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 0, Chunk: body,
}).Once().Return(&abci.ResponseApplySnapshotChunk{
Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
@@ -573,13 +573,13 @@ func TestSyncer_applyChunks_RefetchChunks(t *testing.T) {
require.NoError(t, err)
// The first two chunks are accepted, before the last one asks for 1 to be refetched
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 0, Chunk: []byte{0},
}).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 1, Chunk: []byte{1},
}).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 2, Chunk: []byte{2},
}).Once().Return(&abci.ResponseApplySnapshotChunk{
Result: tc.result,
@@ -673,13 +673,13 @@ func TestSyncer_applyChunks_RejectSenders(t *testing.T) {
require.NoError(t, err)
// The first two chunks are accepted, before the last one asks for b sender to be rejected
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 0, Chunk: []byte{0}, Sender: "aa",
}).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 1, Chunk: []byte{1}, Sender: "bb",
}).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 2, Chunk: []byte{2}, Sender: "cc",
}).Once().Return(&abci.ResponseApplySnapshotChunk{
Result: tc.result,
@@ -688,7 +688,7 @@ func TestSyncer_applyChunks_RejectSenders(t *testing.T) {
// On retry, the last chunk will be tried again, so we just accept it then.
if tc.result == abci.ResponseApplySnapshotChunk_RETRY {
rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{
rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{
Index: 2, Chunk: []byte{2}, Sender: "cc",
}).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil)
}
@@ -761,7 +761,7 @@ func TestSyncer_verifyApp(t *testing.T) {
rts := setup(ctx, t, nil, nil, 2)
rts.conn.On("Info", mock.Anything, proxy.RequestInfo).Return(tc.response, tc.err)
rts.conn.On("Info", mock.Anything, &proxy.RequestInfo).Return(tc.response, tc.err)
err := rts.syncer.verifyApp(ctx, s, appVersion)
unwrapped := errors.Unwrap(err)
if unwrapped != nil {
+4 -43
View File
@@ -126,7 +126,7 @@ func (bs *BlockStore) LoadBaseMeta() *types.BlockMeta {
return nil
}
// LoadBlock returns the block with the given height in protobuf.
// LoadBlockProto returns the block with the given height in protobuf.
// If no block is found for that height, it returns nil.
func (bs *BlockStore) LoadBlockProto(height int64) *tmproto.Block {
var blockMeta = bs.LoadBlockMeta(height)
@@ -157,30 +157,11 @@ func (bs *BlockStore) LoadBlockProto(height int64) *tmproto.Block {
// LoadBlock returns the block with the given height.
// If no block is found for that height, it returns nil.
func (bs *BlockStore) LoadBlock(height int64) *types.Block {
var blockMeta = bs.LoadBlockMeta(height)
if blockMeta == nil {
blockProto := bs.LoadBlockProto(height)
if blockProto == nil {
return nil
}
pbb := new(tmproto.Block)
buf := []byte{}
for i := 0; i < int(blockMeta.BlockID.PartSetHeader.Total); i++ {
part := bs.LoadBlockPart(height, i)
// If the part is missing (e.g. since it has been deleted after we
// loaded the block meta) we consider the whole block to be missing.
if part == nil {
return nil
}
buf = append(buf, part.Bytes...)
}
err := proto.Unmarshal(buf, pbb)
if err != nil {
// NOTE: The existence of meta should imply the existence of the
// block. So, make sure meta is only saved after blocks are saved.
panic(fmt.Errorf("error reading block: %w", err))
}
block, err := types.BlockFromProto(pbb)
block, err := types.BlockFromProto(blockProto)
if err != nil {
panic(fmt.Errorf("error from proto block: %w", err))
}
@@ -282,26 +263,6 @@ func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
return blockMeta
}
// LoadBlockCommit returns the Commit for the given height in protobuf.
// This commit consists of the +2/3 and other Precommit-votes for block at `height`,
// and it comes from the block.LastCommit for `height+1`.
// If no commit is found for the given height, it returns nil.
func (bs *BlockStore) LoadBlockCommitProto(height int64) *tmproto.Commit {
var pbc = new(tmproto.Commit)
bz, err := bs.db.Get(blockCommitKey(height))
if err != nil {
panic(err)
}
if len(bz) == 0 {
return nil
}
err = proto.Unmarshal(bz, pbc)
if err != nil {
panic(fmt.Errorf("error reading block commit: %w", err))
}
return pbc
}
// LoadBlockCommit returns the Commit for the given height.
// This commit consists of the +2/3 and other Precommit-votes for block at `height`,
// and it comes from the block.LastCommit for `height+1`.
+1 -2
View File
@@ -7,7 +7,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/version"
)
@@ -25,7 +24,7 @@ func RandomAddress() []byte {
}
func RandomHash() []byte {
return crypto.CRandBytes(tmhash.Size)
return crypto.CRandBytes(crypto.HashSize)
}
func MakeBlockID() types.BlockID {
+1
View File
@@ -31,6 +31,7 @@ func MakeCommit(ctx context.Context, blockID types.BlockID, height int64, round
return nil, err
}
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
if _, err := voteSet.AddVote(vote); err != nil {
return nil, err
}
+1
View File
@@ -40,5 +40,6 @@ func MakeVote(
}
v.Signature = vpb.Signature
v.ExtensionSignature = vpb.ExtensionSignature
return v, nil
}