Skip to content

Commit

Permalink
Define a selection session wrapper to track accounts state, across al…
Browse files Browse the repository at this point in the history
…l "heap items" (within a selection session).
  • Loading branch information
andreibancioiu committed Dec 12, 2024
1 parent 2759489 commit ec0db29
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 145 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.20
require (
github.com/hashicorp/golang-lru v0.6.0
github.com/multiversx/concurrent-map v0.1.4
github.com/multiversx/mx-chain-core-go v1.2.23
github.com/multiversx/mx-chain-core-go v1.2.24-0.20241204105653-2beb13136490
github.com/multiversx/mx-chain-logger-go v1.0.15
github.com/stretchr/testify v1.7.2
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUYwbO0993uPI=
github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o=
github.com/multiversx/mx-chain-core-go v1.2.23 h1:8WlCGqJHR2HQ0vN4feJwb7W4VrCwBGIzPPHunOOg5Wc=
github.com/multiversx/mx-chain-core-go v1.2.23/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE=
github.com/multiversx/mx-chain-core-go v1.2.24-0.20241204105653-2beb13136490 h1:uK29uJdsvVYMp37wjC/qu74O8V04gFw0Bw7q9C9zc+c=
github.com/multiversx/mx-chain-core-go v1.2.24-0.20241204105653-2beb13136490/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE=
github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+wRqOwi3n+m2QIHXc=
github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
Expand Down
29 changes: 0 additions & 29 deletions txcache/accountsBalancesTracker.go

This file was deleted.

35 changes: 17 additions & 18 deletions txcache/selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ 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()
sessionWrapper := newSelectionSessionWrapper(session)

// Items popped from the heap are added to "selectedTransactions".
transactionsHeap := newMaxTransactionsHeap(len(bunches))
heap.Init(transactionsHeap)

// Initialize the heap with the first transaction of each bunch
for _, bunch := range bunches {
item, err := newTransactionsHeapItem(bunch, balancesTracker)
item, err := newTransactionsHeapItem(bunch)
if err != nil {
continue
}
Expand Down Expand Up @@ -64,24 +64,19 @@ func selectTransactionsFromBunches(session SelectionSession, bunches []bunchOfTr
}
}

err := item.requestAccountStateIfNecessary(session)
if err != nil {
// Skip this sender.
logSelect.Debug("TxCache.selectTransactionsFromBunches, could not retrieve account state", "sender", item.sender, "err", err)
continue
}

shouldSkipSender := detectSkippableSender(item)
shouldSkipSender := detectSkippableSender(sessionWrapper, item)
if shouldSkipSender {
// Item was popped from the heap, but not used downstream.
// Therefore, the sender is completely ignored (from now on) in the current selection session.
continue
}

shouldSkipTransaction := detectSkippableTransaction(session, item)
shouldSkipTransaction := detectSkippableTransaction(sessionWrapper, item)
if !shouldSkipTransaction {
accumulatedGas += gasLimit
selectedTransactions = append(selectedTransactions, item.selectCurrentTransaction())
selectedTransaction := item.selectCurrentTransaction()
selectedTransactions = append(selectedTransactions, selectedTransaction)
sessionWrapper.accumulateConsumedBalance(selectedTransaction)
}

// If there are more transactions in the same bunch (same sender as the popped item),
Expand All @@ -95,25 +90,29 @@ func selectTransactionsFromBunches(session SelectionSession, bunches []bunchOfTr
return selectedTransactions, accumulatedGas
}

func detectSkippableSender(item *transactionsHeapItem) bool {
if item.detectInitialGap() {
func detectSkippableSender(sessionWrapper *selectionSessionWrapper, item *transactionsHeapItem) bool {
nonce := sessionWrapper.getNonce(item.sender)

if item.detectInitialGap(nonce) {
return true
}
if item.detectMiddleGap() {
return true
}
if item.detectWillFeeExceedBalance() {
if sessionWrapper.detectWillFeeExceedBalance(item.currentTransaction) {
return true
}

return false
}

func detectSkippableTransaction(session SelectionSession, item *transactionsHeapItem) bool {
if item.detectLowerNonce() {
func detectSkippableTransaction(sessionWrapper *selectionSessionWrapper, item *transactionsHeapItem) bool {
nonce := sessionWrapper.getNonce(item.sender)

if item.detectLowerNonce(nonce) {
return true
}
if item.detectIncorrectlyGuarded(session) {
if item.detectIncorrectlyGuarded(sessionWrapper) {
return true
}
if item.detectNonceDuplicate() {
Expand Down
106 changes: 106 additions & 0 deletions txcache/selectionSessionWrapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package txcache

import (
"math/big"

"github.com/multiversx/mx-chain-core-go/data"
)

// After moving "mx-chain-storage-go/txcache" into "mx-chain-go", maybe merge this component into "SelectionSession".
type selectionSessionWrapper struct {
session SelectionSession
recordByAddress map[string]*accountBalanceRecord
}

type accountBalanceRecord struct {
initialNonce uint64
initialBalance *big.Int
consumedBalance *big.Int
}

func newSelectionSessionWrapper(session SelectionSession) *selectionSessionWrapper {
return &selectionSessionWrapper{
session: session,
recordByAddress: make(map[string]*accountBalanceRecord),
}
}

func (sessionWrapper *selectionSessionWrapper) getAccountRecord(address []byte) *accountBalanceRecord {
record, ok := sessionWrapper.recordByAddress[string(address)]
if ok {
return record
}

state, err := sessionWrapper.session.GetAccountState(address)
if err != nil {
logSelect.Debug("selectionSessionWrapper.getAccountRecord, could not retrieve account state", "address", address, "err", err)

record = &accountBalanceRecord{
initialNonce: 0,
initialBalance: big.NewInt(0),
consumedBalance: big.NewInt(0),
}
} else {
record = &accountBalanceRecord{
initialNonce: state.Nonce,
initialBalance: state.Balance,
consumedBalance: big.NewInt(0),
}
}

sessionWrapper.recordByAddress[string(address)] = record
return record
}

func (sessionWrapper *selectionSessionWrapper) getNonce(address []byte) uint64 {
record := sessionWrapper.getAccountRecord(address)
return record.initialNonce
}

func (sessionWrapper *selectionSessionWrapper) accumulateConsumedBalance(tx *WrappedTransaction) {
sender := tx.Tx.GetSndAddr()
feePayer := tx.FeePayer

senderRecord := sessionWrapper.getAccountRecord(sender)
feePayerRecord := sessionWrapper.getAccountRecord(feePayer)

transferredValue := tx.TransferredValue
if transferredValue != nil {
senderRecord.consumedBalance.Add(senderRecord.consumedBalance, transferredValue)
}

fee := tx.Fee
if fee != nil {
feePayerRecord.consumedBalance.Add(feePayerRecord.consumedBalance, fee)
}
}

func (sessionWrapper *selectionSessionWrapper) detectWillFeeExceedBalance(tx *WrappedTransaction) bool {
fee := tx.Fee
if fee == nil {
return false
}

// Here, we are not interested into an eventual transfer of value (we only check if there's enough balance to pay the transaction fee).
feePayer := tx.FeePayer
feePayerRecord := sessionWrapper.getAccountRecord(feePayer)

futureConsumedBalance := new(big.Int).Add(feePayerRecord.consumedBalance, fee)
feePayerBalance := feePayerRecord.initialBalance

willFeeExceedBalance := futureConsumedBalance.Cmp(feePayerBalance) > 0
if willFeeExceedBalance {
logSelect.Trace("selectionSessionWrapper.detectWillFeeExceedBalance",
"tx", tx.TxHash,
"feePayer", feePayer,
"initialBalance", feePayerRecord.initialBalance,
"consumedBalance", feePayerRecord.consumedBalance,
)
}

return willFeeExceedBalance
}

func (sessionWrapper *selectionSessionWrapper) isIncorrectlyGuarded(tx data.TransactionHandler) bool {
return sessionWrapper.session.IsIncorrectlyGuarded(tx)
}
Loading

0 comments on commit ec0db29

Please sign in to comment.