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.
This commit is contained in:
Roman Khimov 2021-11-30 18:42:22 +03:00
parent ee05f73b6f
commit 748a70569a
4 changed files with 32 additions and 30 deletions

1
go.mod
View file

@ -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

2
go.sum
View file

@ -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=

View file

@ -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 {

View file

@ -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