consensus: try to use previous proposal after ChangeView

When system and network pressure is high it can be beneficial
to use transactions which and were already proposed.
The assumption is that they will be in other node's memory pool
with more probability.
This commit is contained in:
Evgenii Stratonikov 2020-01-14 14:34:09 +03:00
parent 7ba5267494
commit 3d704a3a3a
2 changed files with 69 additions and 2 deletions

View file

@ -55,6 +55,7 @@ type service struct {
// everything in single thread.
messages chan Payload
transactions chan *transaction.Transaction
lastProposal []util.Uint256
}
// Config is a configuration for consensus services.
@ -205,7 +206,9 @@ func (s *service) OnPayload(cp *Payload) {
// we use switch here because other payloads could be possibly added in future
switch cp.Type() {
case payload.PrepareRequestType:
s.txx.Add(&cp.GetPrepareRequest().(*prepareRequest).minerTx)
req := cp.GetPrepareRequest().(*prepareRequest)
s.txx.Add(&req.minerTx)
s.lastProposal = req.transactionHashes
}
s.messages <- *cp
@ -328,7 +331,23 @@ func (s *service) getBlock(h util.Uint256) block.Block {
func (s *service) getVerifiedTx(count int) []block.Transaction {
pool := s.Config.Chain.GetMemPool()
txx := pool.GetVerifiedTransactions()
var txx []*transaction.Transaction
if s.dbft.ViewNumber > 0 {
txx = make([]*transaction.Transaction, 0, len(s.lastProposal))
for i := range s.lastProposal {
if tx, ok := pool.TryGetValue(s.lastProposal[i]); ok {
txx = append(txx, tx)
}
}
if len(txx) < len(s.lastProposal)/2 {
txx = pool.GetVerifiedTransactions()
}
} else {
txx = pool.GetVerifiedTransactions()
}
res := make([]block.Transaction, len(txx)+1)
for i := range txx {

View file

@ -9,6 +9,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/nspcc-dev/dbft/block"
"github.com/nspcc-dev/dbft/payload"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
@ -28,6 +29,53 @@ func TestNewService(t *testing.T) {
require.Equal(t, tx, txx[1])
}
func TestService_GetVerified(t *testing.T) {
srv := newTestService(t)
txs := []*transaction.Transaction{
newMinerTx(1),
newMinerTx(2),
newMinerTx(3),
newMinerTx(4),
}
pool := srv.Chain.GetMemPool()
item := core.NewPoolItem(txs[3], new(feer))
require.True(t, pool.TryAdd(txs[3].Hash(), item))
hashes := []util.Uint256{txs[0].Hash(), txs[1].Hash(), txs[2].Hash()}
p := new(Payload)
p.SetType(payload.PrepareRequestType)
p.SetPayload(&prepareRequest{transactionHashes: hashes})
p.SetValidatorIndex(1)
priv, _ := getTestValidator(1)
require.NoError(t, p.Sign(priv))
srv.OnPayload(p)
require.Equal(t, hashes, srv.lastProposal)
srv.dbft.ViewNumber = 1
t.Run("new transactions will be proposed in case of failure", func(t *testing.T) {
txx := srv.getVerifiedTx(10)
require.Equal(t, 2, len(txx), "there is only 1 tx in mempool")
require.Equal(t, txs[3], txx[1])
})
t.Run("more than half of the last proposal will be reused", func(t *testing.T) {
for _, tx := range txs[:2] {
item := core.NewPoolItem(tx, new(feer))
require.True(t, pool.TryAdd(tx.Hash(), item))
}
txx := srv.getVerifiedTx(10)
require.Contains(t, txx, txs[0])
require.Contains(t, txx, txs[1])
require.NotContains(t, txx, txs[2])
})
}
func TestService_ValidatePayload(t *testing.T) {
srv := newTestService(t)
priv, _ := getTestValidator(1)