From 748a70569ad50d2638900cb9ded68bebb9ddb811 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 30 Nov 2021 18:42:22 +0300 Subject: [PATCH] mempool: use uint256 library for fee tracking It's very effective in avoiding allocations for big.Int, we don't have a microbenchmark for memppol, but this improves TPS metrics by ~1-2%, so it's noticeable. --- go.mod | 1 + go.sum | 2 ++ pkg/core/mempool/mem_pool.go | 42 +++++++++++++++---------------- pkg/core/mempool/mem_pool_test.go | 17 +++++++------ 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 7e94a0a32..cdf2af233 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/btcsuite/btcd v0.22.0-beta github.com/gorilla/websocket v1.4.2 github.com/hashicorp/golang-lru v0.5.4 + github.com/holiman/uint256 v1.2.0 github.com/mr-tron/base58 v1.2.0 github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02 diff --git a/go.sum b/go.sum index a38e4b761..7b06b7467 100644 --- a/go.sum +++ b/go.sum @@ -128,6 +128,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= +github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= diff --git a/pkg/core/mempool/mem_pool.go b/pkg/core/mempool/mem_pool.go index 7e1bb98ef..04261a6ad 100644 --- a/pkg/core/mempool/mem_pool.go +++ b/pkg/core/mempool/mem_pool.go @@ -3,11 +3,11 @@ package mempool import ( "errors" "fmt" - "math/big" "math/bits" "sort" "sync" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/mempoolevent" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/util" @@ -50,8 +50,8 @@ type items []item // utilityBalanceAndFees stores sender's balance and overall fees of // sender's transactions which are currently in mempool. type utilityBalanceAndFees struct { - balance *big.Int - feeSum *big.Int + balance uint256.Int + feeSum uint256.Int } // Pool stores the unconfirms transactions. @@ -164,8 +164,7 @@ func (mp *Pool) tryAddSendersFee(tx *transaction.Transaction, feer Feer, needChe payer := tx.Signers[mp.payerIndex].Account senderFee, ok := mp.fees[payer] if !ok { - senderFee.balance = feer.GetUtilityTokenBalance(payer) - senderFee.feeSum = big.NewInt(0) + _ = senderFee.balance.SetFromBig(feer.GetUtilityTokenBalance(payer)) mp.fees[payer] = senderFee } if needCheck { @@ -173,23 +172,26 @@ func (mp *Pool) tryAddSendersFee(tx *transaction.Transaction, feer Feer, needChe if err != nil { return false } - senderFee.feeSum.Set(newFeeSum) + senderFee.feeSum = newFeeSum } else { - senderFee.feeSum.Add(senderFee.feeSum, big.NewInt(tx.SystemFee+tx.NetworkFee)) + senderFee.feeSum.AddUint64(&senderFee.feeSum, uint64(tx.SystemFee+tx.NetworkFee)) } + mp.fees[payer] = senderFee return true } // checkBalance returns new cumulative fee balance for account or an error in // case sender doesn't have enough GAS to pay for the transaction. -func checkBalance(tx *transaction.Transaction, balance utilityBalanceAndFees) (*big.Int, error) { - txFee := big.NewInt(tx.SystemFee + tx.NetworkFee) - if balance.balance.Cmp(txFee) < 0 { - return nil, ErrInsufficientFunds +func checkBalance(tx *transaction.Transaction, balance utilityBalanceAndFees) (uint256.Int, error) { + var txFee uint256.Int + + txFee.SetUint64(uint64(tx.SystemFee + tx.NetworkFee)) + if balance.balance.Cmp(&txFee) < 0 { + return txFee, ErrInsufficientFunds } - txFee.Add(txFee, balance.feeSum) - if balance.balance.Cmp(txFee) < 0 { - return nil, ErrConflict + txFee.Add(&txFee, &balance.feeSum) + if balance.balance.Cmp(&txFee) < 0 { + return txFee, ErrConflict } return txFee, nil } @@ -323,7 +325,7 @@ func (mp *Pool) removeInternal(hash util.Uint256, feer Feer) { } payer := itm.txn.Signers[mp.payerIndex].Account senderFee := mp.fees[payer] - senderFee.feeSum.Sub(senderFee.feeSum, big.NewInt(tx.SystemFee+tx.NetworkFee)) + senderFee.feeSum.SubUint64(&senderFee.feeSum, uint64(tx.SystemFee+tx.NetworkFee)) mp.fees[payer] = senderFee if feer.P2PSigExtensionsEnabled() { // remove all conflicting hashes from mp.conflicts list @@ -507,8 +509,7 @@ func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) ([]*tran payer := tx.Signers[mp.payerIndex].Account actualSenderFee, ok := mp.fees[payer] if !ok { - actualSenderFee.balance = fee.GetUtilityTokenBalance(payer) - actualSenderFee.feeSum = big.NewInt(0) + actualSenderFee.balance.SetFromBig(fee.GetUtilityTokenBalance(payer)) } var expectedSenderFee utilityBalanceAndFees @@ -541,13 +542,10 @@ func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) ([]*tran conflictsToBeRemoved = append(conflictsToBeRemoved, existingTx) } // Step 3: take into account sender's conflicting transactions before balance check. - expectedSenderFee = utilityBalanceAndFees{ - balance: new(big.Int).Set(actualSenderFee.balance), - feeSum: new(big.Int).Set(actualSenderFee.feeSum), - } + expectedSenderFee = actualSenderFee for _, conflictingTx := range conflictsToBeRemoved { if conflictingTx.Signers[mp.payerIndex].Account.Equals(payer) { - expectedSenderFee.feeSum.Sub(expectedSenderFee.feeSum, big.NewInt(conflictingTx.SystemFee+conflictingTx.NetworkFee)) + expectedSenderFee.feeSum.SubUint64(&expectedSenderFee.feeSum, uint64(conflictingTx.SystemFee+conflictingTx.NetworkFee)) } } } else { diff --git a/pkg/core/mempool/mem_pool_test.go b/pkg/core/mempool/mem_pool_test.go index a7e73839e..20d571953 100644 --- a/pkg/core/mempool/mem_pool_test.go +++ b/pkg/core/mempool/mem_pool_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/network/payload" @@ -268,8 +269,8 @@ func TestMemPoolFees(t *testing.T) { require.NoError(t, mp.Add(tx1, fs)) require.Equal(t, 1, len(mp.fees)) require.Equal(t, utilityBalanceAndFees{ - balance: big.NewInt(fs.balance), - feeSum: big.NewInt(tx1.NetworkFee), + balance: *uint256.NewInt(uint64(fs.balance)), + feeSum: *uint256.NewInt(uint64(tx1.NetworkFee)), }, mp.fees[sender0]) // balance shouldn't change after adding one more transaction @@ -280,8 +281,8 @@ func TestMemPoolFees(t *testing.T) { require.Equal(t, 2, len(mp.verifiedTxes)) require.Equal(t, 1, len(mp.fees)) require.Equal(t, utilityBalanceAndFees{ - balance: big.NewInt(fs.balance), - feeSum: big.NewInt(fs.balance), + balance: *uint256.NewInt(uint64(fs.balance)), + feeSum: *uint256.NewInt(uint64(fs.balance)), }, mp.fees[sender0]) // can't add more transactions as we don't have enough GAS @@ -292,8 +293,8 @@ func TestMemPoolFees(t *testing.T) { require.Error(t, mp.Add(tx3, fs)) require.Equal(t, 1, len(mp.fees)) require.Equal(t, utilityBalanceAndFees{ - balance: big.NewInt(fs.balance), - feeSum: big.NewInt(fs.balance), + balance: *uint256.NewInt(uint64(fs.balance)), + feeSum: *uint256.NewInt(uint64(fs.balance)), }, mp.fees[sender0]) // check whether sender's fee updates correctly @@ -302,8 +303,8 @@ func TestMemPoolFees(t *testing.T) { }, fs) require.Equal(t, 1, len(mp.fees)) require.Equal(t, utilityBalanceAndFees{ - balance: big.NewInt(fs.balance), - feeSum: big.NewInt(tx2.NetworkFee), + balance: *uint256.NewInt(uint64(fs.balance)), + feeSum: *uint256.NewInt(uint64(tx2.NetworkFee)), }, mp.fees[sender0]) // there should be nothing left