From eadbce8ae1f75479d7d7c38ab7de76c2df9a9e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Wed, 11 Dec 2024 13:15:44 +0200 Subject: [PATCH] Sketch "accountsBalancesTracker". --- txcache/accountsBalancesTracker.go | 29 +++++++++++++++++++ txcache/eviction.go | 3 +- txcache/selection.go | 3 +- txcache/transactionsHeapItem.go | 13 +++++---- txcache/transactionsHeapItem_test.go | 43 +++++++++++++++++----------- 5 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 txcache/accountsBalancesTracker.go diff --git a/txcache/accountsBalancesTracker.go b/txcache/accountsBalancesTracker.go new file mode 100644 index 00000000..1d31d8c9 --- /dev/null +++ b/txcache/accountsBalancesTracker.go @@ -0,0 +1,29 @@ +package txcache + +import ( + "math/big" + + "github.com/multiversx/mx-chain-storage-go/types" +) + +type accountsBalancesTracker struct { + recordByAddress map[string]*accountBalanceRecord +} + +type accountBalanceRecord struct { + initialBalance *big.Int + consumedBalance *big.Int +} + +func newAccountsBalancesTracker() *accountsBalancesTracker { + return &accountsBalancesTracker{ + recordByAddress: make(map[string]*accountBalanceRecord), + } +} + +func (tracker *accountsBalancesTracker) setupAccount(address []byte, state *types.AccountState) { + tracker.recordByAddress[string(address)] = &accountBalanceRecord{ + initialBalance: state.Balance, + consumedBalance: big.NewInt(0), + } +} diff --git a/txcache/eviction.go b/txcache/eviction.go index 61d09cfb..59ebeac7 100644 --- a/txcache/eviction.go +++ b/txcache/eviction.go @@ -86,6 +86,7 @@ func (cache *TxCache) areThereTooManyTxs() bool { func (cache *TxCache) evictLeastLikelyToSelectTransactions() *evictionJournal { senders := cache.getSenders() bunches := make([]bunchOfTransactions, 0, len(senders)) + unusedBalancesTracker := newAccountsBalancesTracker() for _, sender := range senders { // Include transactions after gaps, as well (important), unlike when selecting transactions for processing. @@ -103,7 +104,7 @@ func (cache *TxCache) evictLeastLikelyToSelectTransactions() *evictionJournal { // Initialize the heap with the first transaction of each bunch for _, bunch := range bunches { - item, err := newTransactionsHeapItem(bunch) + item, err := newTransactionsHeapItem(bunch, unusedBalancesTracker) if err != nil { continue } diff --git a/txcache/selection.go b/txcache/selection.go index f889a3a9..9ca70fd4 100644 --- a/txcache/selection.go +++ b/txcache/selection.go @@ -25,6 +25,7 @@ func (cache *TxCache) acquireBunchesOfTransactions() []bunchOfTransactions { // Selection tolerates concurrent transaction additions / removals. func selectTransactionsFromBunches(session SelectionSession, bunches []bunchOfTransactions, gasRequested uint64, maxNum int, selectionLoopMaximumDuration time.Duration) (bunchOfTransactions, uint64) { selectedTransactions := make(bunchOfTransactions, 0, initialCapacityOfSelectionSlice) + balancesTracker := newAccountsBalancesTracker() // Items popped from the heap are added to "selectedTransactions". transactionsHeap := newMaxTransactionsHeap(len(bunches)) @@ -32,7 +33,7 @@ func selectTransactionsFromBunches(session SelectionSession, bunches []bunchOfTr // Initialize the heap with the first transaction of each bunch for _, bunch := range bunches { - item, err := newTransactionsHeapItem(bunch) + item, err := newTransactionsHeapItem(bunch, balancesTracker) if err != nil { continue } diff --git a/txcache/transactionsHeapItem.go b/txcache/transactionsHeapItem.go index 5d09dd59..e59bfb64 100644 --- a/txcache/transactionsHeapItem.go +++ b/txcache/transactionsHeapItem.go @@ -7,8 +7,9 @@ import ( ) type transactionsHeapItem struct { - sender []byte - bunch bunchOfTransactions + sender []byte + bunch bunchOfTransactions + balancesTracker *accountsBalancesTracker // The sender's state, as fetched in "requestAccountStateIfNecessary". senderState *types.AccountState @@ -22,7 +23,7 @@ type transactionsHeapItem struct { consumedBalance *big.Int } -func newTransactionsHeapItem(bunch bunchOfTransactions) (*transactionsHeapItem, error) { +func newTransactionsHeapItem(bunch bunchOfTransactions, balancesTracker *accountsBalancesTracker) (*transactionsHeapItem, error) { if len(bunch) == 0 { return nil, errEmptyBunchOfTransactions } @@ -30,8 +31,9 @@ func newTransactionsHeapItem(bunch bunchOfTransactions) (*transactionsHeapItem, firstTransaction := bunch[0] return &transactionsHeapItem{ - sender: firstTransaction.Tx.GetSndAddr(), - bunch: bunch, + sender: firstTransaction.Tx.GetSndAddr(), + bunch: bunch, + balancesTracker: balancesTracker, senderState: nil, @@ -201,6 +203,7 @@ func (item *transactionsHeapItem) requestAccountStateIfNecessary(session Selecti } item.senderState = senderState + item.balancesTracker.setupAccount(item.sender, senderState) return nil } diff --git a/txcache/transactionsHeapItem_test.go b/txcache/transactionsHeapItem_test.go index 55199472..7717926c 100644 --- a/txcache/transactionsHeapItem_test.go +++ b/txcache/transactionsHeapItem_test.go @@ -11,8 +11,10 @@ import ( ) func TestNewTransactionsHeapItem(t *testing.T) { + balancesTracker := newAccountsBalancesTracker() + t.Run("empty bunch", func(t *testing.T) { - item, err := newTransactionsHeapItem(nil) + item, err := newTransactionsHeapItem(nil, balancesTracker) require.Nil(t, item) require.Equal(t, errEmptyBunchOfTransactions, err) }) @@ -22,7 +24,7 @@ func TestNewTransactionsHeapItem(t *testing.T) { createTx([]byte("tx-1"), "alice", 42), } - item, err := newTransactionsHeapItem(bunch) + item, err := newTransactionsHeapItem(bunch, balancesTracker) require.NotNil(t, item) require.Nil(t, err) @@ -39,13 +41,14 @@ func TestNewTransactionsHeapItem(t *testing.T) { func TestTransactionsHeapItem_selectTransaction(t *testing.T) { host := txcachemocks.NewMempoolHostMock() + balancesTracker := newAccountsBalancesTracker() a := createTx([]byte("tx-1"), "alice", 42) b := createTx([]byte("tx-2"), "alice", 43) a.precomputeFields(host) b.precomputeFields(host) - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) selected := item.selectCurrentTransaction() @@ -68,18 +71,19 @@ func TestTransactionsHeapItem_selectTransaction(t *testing.T) { } func TestTransactionsHeapItem_detectInitialGap(t *testing.T) { + balancesTracker := newAccountsBalancesTracker() a := createTx([]byte("tx-1"), "alice", 42) b := createTx([]byte("tx-2"), "alice", 43) t.Run("unknown", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) require.False(t, item.detectInitialGap()) }) t.Run("known, without gap", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) item.senderState = &types.AccountState{ @@ -90,7 +94,7 @@ func TestTransactionsHeapItem_detectInitialGap(t *testing.T) { }) t.Run("known, without gap", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) item.senderState = &types.AccountState{ @@ -135,6 +139,7 @@ func TestTransactionsHeapItem_detectMiddleGap(t *testing.T) { func TestTransactionsHeapItem_detectWillFeeExceedBalance(t *testing.T) { host := txcachemocks.NewMempoolHostMock() + balancesTracker := newAccountsBalancesTracker() a := createTx([]byte("tx-1"), "alice", 42) b := createTx([]byte("tx-2"), "alice", 43) @@ -147,14 +152,14 @@ func TestTransactionsHeapItem_detectWillFeeExceedBalance(t *testing.T) { d.precomputeFields(host) t.Run("unknown", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) require.False(t, item.detectWillFeeExceedBalance()) }) t.Run("known, not exceeded, then exceeded (a)", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) item.senderState = &types.AccountState{ @@ -171,7 +176,7 @@ func TestTransactionsHeapItem_detectWillFeeExceedBalance(t *testing.T) { }) t.Run("known, not exceeded, then exceeded (b)", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b, c, d}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b, c, d}, balancesTracker) require.NoError(t, err) item.senderState = &types.AccountState{ @@ -204,18 +209,19 @@ func TestTransactionsHeapItem_detectWillFeeExceedBalance(t *testing.T) { } func TestTransactionsHeapItem_detectLowerNonce(t *testing.T) { + balancesTracker := newAccountsBalancesTracker() a := createTx([]byte("tx-1"), "alice", 42) b := createTx([]byte("tx-2"), "alice", 43) t.Run("unknown", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) require.False(t, item.detectInitialGap()) }) t.Run("known, good", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) item.senderState = &types.AccountState{ @@ -226,7 +232,7 @@ func TestTransactionsHeapItem_detectLowerNonce(t *testing.T) { }) t.Run("known, lower", func(t *testing.T) { - item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}) + item, err := newTransactionsHeapItem(bunchOfTransactions{a, b}, balancesTracker) require.NoError(t, err) item.senderState = &types.AccountState{ @@ -270,13 +276,15 @@ func TestTransactionsHeapItem_detectNonceDuplicate(t *testing.T) { } func TestTransactionsHeapItem_detectIncorrectlyGuarded(t *testing.T) { + balancesTracker := newAccountsBalancesTracker() + t.Run("is correctly guarded", func(t *testing.T) { session := txcachemocks.NewSelectionSessionMock() session.IsIncorrectlyGuardedCalled = func(tx data.TransactionHandler) bool { return false } - item, err := newTransactionsHeapItem(bunchOfTransactions{createTx([]byte("tx-1"), "alice", 42)}) + item, err := newTransactionsHeapItem(bunchOfTransactions{createTx([]byte("tx-1"), "alice", 42)}, balancesTracker) require.NoError(t, err) require.False(t, item.detectIncorrectlyGuarded(session)) @@ -288,7 +296,7 @@ func TestTransactionsHeapItem_detectIncorrectlyGuarded(t *testing.T) { return true } - item, err := newTransactionsHeapItem(bunchOfTransactions{createTx([]byte("tx-1"), "alice", 42)}) + item, err := newTransactionsHeapItem(bunchOfTransactions{createTx([]byte("tx-1"), "alice", 42)}, balancesTracker) require.NoError(t, err) require.True(t, item.detectIncorrectlyGuarded(session)) @@ -297,6 +305,7 @@ func TestTransactionsHeapItem_detectIncorrectlyGuarded(t *testing.T) { func TestTransactionsHeapItem_requestAccountStateIfNecessary(t *testing.T) { session := txcachemocks.NewSelectionSessionMock() + balancesTracker := newAccountsBalancesTracker() noncesByAddress := session.AccountStateByAddress noncesByAddress["alice"] = &types.AccountState{ @@ -309,11 +318,13 @@ func TestTransactionsHeapItem_requestAccountStateIfNecessary(t *testing.T) { } a := &transactionsHeapItem{ - sender: []byte("alice"), + sender: []byte("alice"), + balancesTracker: balancesTracker, } b := &transactionsHeapItem{ - sender: []byte("bob"), + sender: []byte("bob"), + balancesTracker: balancesTracker, } c := &transactionsHeapItem{}