mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 23:14:37 +00:00
Merge remote-tracking branch 'origin/jasmina/4457-blocksync-verification_part1' into jasmina/4457_block_sync_verification
This commit is contained in:
+21
-33
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")),
|
||||
|
||||
@@ -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
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")),
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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{},
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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}" {
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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`.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -40,5 +40,6 @@ func MakeVote(
|
||||
}
|
||||
|
||||
v.Signature = vpb.Signature
|
||||
v.ExtensionSignature = vpb.ExtensionSignature
|
||||
return v, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user