forked from TrueCloudLab/neoneo-go
core: add function IterateVerifiedTransactions
IterateVerifiedTransactions iterates through verified transactions in memory pool and invokes function cont. Where cont callback returns whether we should continue with the traversal process. Signed-off-by: Tatiana Nesterenko <tatiana@nspcc.io>
This commit is contained in:
parent
abb35ad6fd
commit
d285342d54
2 changed files with 127 additions and 23 deletions
|
@ -609,3 +609,19 @@ func (mp *Pool) removeConflictsOf(tx *transaction.Transaction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IterateVerifiedTransactions iterates through verified transactions and invokes
|
||||||
|
// function `cont`. Iterations continue while the function `cont` returns true.
|
||||||
|
// Function `cont` is executed within a read-locked memory pool,
|
||||||
|
// thus IterateVerifiedTransactions will block any write mempool operation,
|
||||||
|
// use it with care. Do not modify transaction or data via `cont`.
|
||||||
|
func (mp *Pool) IterateVerifiedTransactions(cont func(tx *transaction.Transaction, data any) bool) {
|
||||||
|
mp.lock.RLock()
|
||||||
|
defer mp.lock.RUnlock()
|
||||||
|
|
||||||
|
for i := range mp.verifiedTxes {
|
||||||
|
if !cont(mp.verifiedTxes[i].txn, mp.verifiedTxes[i].data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -637,26 +637,18 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
balance: 100,
|
balance: 100,
|
||||||
}
|
}
|
||||||
mp := New(10, 1, false, nil)
|
mp := New(10, 1, false, nil)
|
||||||
newTx := func(t *testing.T, netFee int64) *transaction.Transaction {
|
|
||||||
tx := transaction.New([]byte{byte(opcode.RET)}, 0)
|
|
||||||
tx.Signers = []transaction.Signer{{}, {}}
|
|
||||||
tx.NetworkFee = netFee
|
|
||||||
nonce++
|
|
||||||
tx.Nonce = nonce
|
|
||||||
return tx
|
|
||||||
}
|
|
||||||
|
|
||||||
// bad, insufficient deposit
|
// bad, insufficient deposit
|
||||||
r1 := &payload.P2PNotaryRequest{
|
r1 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, fs.balance+1),
|
FallbackTransaction: mkTwoSignersTx(fs.balance+1, &nonce),
|
||||||
}
|
}
|
||||||
require.ErrorIs(t, mp.Add(r1.FallbackTransaction, fs, r1), ErrInsufficientFunds)
|
require.ErrorIs(t, mp.Add(r1.FallbackTransaction, fs, r1), ErrInsufficientFunds)
|
||||||
|
|
||||||
// good
|
// good
|
||||||
r2 := &payload.P2PNotaryRequest{
|
r2 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
|
require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
|
||||||
require.True(t, mp.ContainsKey(r2.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r2.FallbackTransaction.Hash()))
|
||||||
|
@ -669,8 +661,8 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// good, higher priority than r2. The resulting mp.verifiedTxes: [r3, r2]
|
// good, higher priority than r2. The resulting mp.verifiedTxes: [r3, r2]
|
||||||
r3 := &payload.P2PNotaryRequest{
|
r3 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee+1),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee+1, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
|
require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
|
||||||
require.True(t, mp.ContainsKey(r3.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r3.FallbackTransaction.Hash()))
|
||||||
|
@ -680,8 +672,8 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4]
|
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4]
|
||||||
r4 := &payload.P2PNotaryRequest{
|
r4 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
|
require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
|
||||||
require.True(t, mp.ContainsKey(r4.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r4.FallbackTransaction.Hash()))
|
||||||
|
@ -691,8 +683,8 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4, r5]
|
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4, r5]
|
||||||
r5 := &payload.P2PNotaryRequest{
|
r5 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r5.FallbackTransaction, fs, r5))
|
require.NoError(t, mp.Add(r5.FallbackTransaction, fs, r5))
|
||||||
require.True(t, mp.ContainsKey(r5.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r5.FallbackTransaction.Hash()))
|
||||||
|
@ -713,7 +705,7 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
|
|
||||||
// but getting nil data is OK. The resulting mp.verifiedTxes: [r3, r2, r4, r5, r6]
|
// but getting nil data is OK. The resulting mp.verifiedTxes: [r3, r2, r4, r5, r6]
|
||||||
r6 := newTx(t, smallNetFee)
|
r6 := mkTwoSignersTx(smallNetFee, &nonce)
|
||||||
require.NoError(t, mp.Add(r6, fs, nil))
|
require.NoError(t, mp.Add(r6, fs, nil))
|
||||||
require.True(t, mp.ContainsKey(r6.Hash()))
|
require.True(t, mp.ContainsKey(r6.Hash()))
|
||||||
data, ok = mp.TryGetData(r6.Hash())
|
data, ok = mp.TryGetData(r6.Hash())
|
||||||
|
@ -722,14 +714,14 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
|
|
||||||
// getting data: item is in verifiedMap, but not in verifiedTxes
|
// getting data: item is in verifiedMap, but not in verifiedTxes
|
||||||
r7 := &payload.P2PNotaryRequest{
|
r7 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r7.FallbackTransaction, fs, r4))
|
require.NoError(t, mp.Add(r7.FallbackTransaction, fs, r4))
|
||||||
require.True(t, mp.ContainsKey(r7.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r7.FallbackTransaction.Hash()))
|
||||||
r8 := &payload.P2PNotaryRequest{
|
r8 := &payload.P2PNotaryRequest{
|
||||||
MainTransaction: newTx(t, 0),
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
FallbackTransaction: newTx(t, smallNetFee-1),
|
FallbackTransaction: mkTwoSignersTx(smallNetFee-1, &nonce),
|
||||||
}
|
}
|
||||||
require.NoError(t, mp.Add(r8.FallbackTransaction, fs, r4))
|
require.NoError(t, mp.Add(r8.FallbackTransaction, fs, r4))
|
||||||
require.True(t, mp.ContainsKey(r8.FallbackTransaction.Hash()))
|
require.True(t, mp.ContainsKey(r8.FallbackTransaction.Hash()))
|
||||||
|
@ -737,3 +729,99 @@ func TestMempoolAddWithDataGetData(t *testing.T) {
|
||||||
_, ok = mp.TryGetData(r7.FallbackTransaction.Hash())
|
_, ok = mp.TryGetData(r7.FallbackTransaction.Hash())
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mkTwoSignersTx(netFee int64, nonce *uint32) *transaction.Transaction {
|
||||||
|
tx := transaction.New([]byte{byte(opcode.RET)}, 0)
|
||||||
|
tx.Signers = []transaction.Signer{{}, {}}
|
||||||
|
tx.NetworkFee = netFee
|
||||||
|
*nonce++
|
||||||
|
tx.Nonce = *nonce
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMempoolIterateVerifiedTransactions(t *testing.T) {
|
||||||
|
var (
|
||||||
|
smallNetFee int64 = 3
|
||||||
|
nonce uint32
|
||||||
|
r1, r2, r3, r4, r5 *payload.P2PNotaryRequest
|
||||||
|
)
|
||||||
|
fs := &FeerStub{
|
||||||
|
feePerByte: 0,
|
||||||
|
p2pSigExt: true,
|
||||||
|
blockHeight: 5,
|
||||||
|
balance: 100,
|
||||||
|
}
|
||||||
|
mp := New(10, 1, false, nil)
|
||||||
|
|
||||||
|
checkRequestsOrder := func(orderedRequests []*payload.P2PNotaryRequest) {
|
||||||
|
var pooledRequests []*payload.P2PNotaryRequest
|
||||||
|
mp.IterateVerifiedTransactions(func(tx *transaction.Transaction, data any) bool {
|
||||||
|
d := data.(*payload.P2PNotaryRequest)
|
||||||
|
pooledRequests = append(pooledRequests, d)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
require.Equal(t, orderedRequests, pooledRequests)
|
||||||
|
}
|
||||||
|
|
||||||
|
r1 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r1.FallbackTransaction, fs, r1))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r1})
|
||||||
|
|
||||||
|
// r2 has higher priority than r1. The resulting mp.verifiedTxes: [r2, r1]
|
||||||
|
r2 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee+1, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1})
|
||||||
|
|
||||||
|
// r3 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3]
|
||||||
|
r3 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1, r3})
|
||||||
|
|
||||||
|
// r4 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3, r4]
|
||||||
|
r4 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
|
||||||
|
checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1, r3, r4})
|
||||||
|
|
||||||
|
checkPooledRequest := func(t *testing.T, r *payload.P2PNotaryRequest, isPooled bool) {
|
||||||
|
cont := true
|
||||||
|
notaryRequest := &payload.P2PNotaryRequest{}
|
||||||
|
mp.IterateVerifiedTransactions(func(tx *transaction.Transaction, data any) bool {
|
||||||
|
if data != nil {
|
||||||
|
notaryRequest = data.(*payload.P2PNotaryRequest)
|
||||||
|
if notaryRequest.MainTransaction.Hash() == r.MainTransaction.Hash() {
|
||||||
|
cont = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cont
|
||||||
|
})
|
||||||
|
|
||||||
|
if isPooled {
|
||||||
|
require.Equal(t, false, cont)
|
||||||
|
require.Equal(t, r, notaryRequest)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, true, cont)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkPooledRequest(t, r1, true)
|
||||||
|
checkPooledRequest(t, r2, true)
|
||||||
|
checkPooledRequest(t, r3, true)
|
||||||
|
checkPooledRequest(t, r4, true)
|
||||||
|
|
||||||
|
r5 = &payload.P2PNotaryRequest{
|
||||||
|
MainTransaction: mkTwoSignersTx(0, &nonce),
|
||||||
|
FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
|
||||||
|
}
|
||||||
|
checkPooledRequest(t, r5, false)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue