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/btcsuite/btcd v0.22.0-beta
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/hashicorp/golang-lru v0.5.4 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/mr-tron/base58 v1.2.0
github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac
github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02 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 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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/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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 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= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=

View file

@ -3,11 +3,11 @@ package mempool
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"math/bits" "math/bits"
"sort" "sort"
"sync" "sync"
"github.com/holiman/uint256"
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -50,8 +50,8 @@ type items []item
// utilityBalanceAndFees stores sender's balance and overall fees of // utilityBalanceAndFees stores sender's balance and overall fees of
// sender's transactions which are currently in mempool. // sender's transactions which are currently in mempool.
type utilityBalanceAndFees struct { type utilityBalanceAndFees struct {
balance *big.Int balance uint256.Int
feeSum *big.Int feeSum uint256.Int
} }
// Pool stores the unconfirms transactions. // 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 payer := tx.Signers[mp.payerIndex].Account
senderFee, ok := mp.fees[payer] senderFee, ok := mp.fees[payer]
if !ok { if !ok {
senderFee.balance = feer.GetUtilityTokenBalance(payer) _ = senderFee.balance.SetFromBig(feer.GetUtilityTokenBalance(payer))
senderFee.feeSum = big.NewInt(0)
mp.fees[payer] = senderFee mp.fees[payer] = senderFee
} }
if needCheck { if needCheck {
@ -173,23 +172,26 @@ func (mp *Pool) tryAddSendersFee(tx *transaction.Transaction, feer Feer, needChe
if err != nil { if err != nil {
return false return false
} }
senderFee.feeSum.Set(newFeeSum) senderFee.feeSum = newFeeSum
} else { } 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 return true
} }
// checkBalance returns new cumulative fee balance for account or an error in // checkBalance returns new cumulative fee balance for account or an error in
// case sender doesn't have enough GAS to pay for the transaction. // case sender doesn't have enough GAS to pay for the transaction.
func checkBalance(tx *transaction.Transaction, balance utilityBalanceAndFees) (*big.Int, error) { func checkBalance(tx *transaction.Transaction, balance utilityBalanceAndFees) (uint256.Int, error) {
txFee := big.NewInt(tx.SystemFee + tx.NetworkFee) var txFee uint256.Int
if balance.balance.Cmp(txFee) < 0 {
return nil, ErrInsufficientFunds txFee.SetUint64(uint64(tx.SystemFee + tx.NetworkFee))
if balance.balance.Cmp(&txFee) < 0 {
return txFee, ErrInsufficientFunds
} }
txFee.Add(txFee, balance.feeSum) txFee.Add(&txFee, &balance.feeSum)
if balance.balance.Cmp(txFee) < 0 { if balance.balance.Cmp(&txFee) < 0 {
return nil, ErrConflict return txFee, ErrConflict
} }
return txFee, nil return txFee, nil
} }
@ -323,7 +325,7 @@ func (mp *Pool) removeInternal(hash util.Uint256, feer Feer) {
} }
payer := itm.txn.Signers[mp.payerIndex].Account payer := itm.txn.Signers[mp.payerIndex].Account
senderFee := mp.fees[payer] 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 mp.fees[payer] = senderFee
if feer.P2PSigExtensionsEnabled() { if feer.P2PSigExtensionsEnabled() {
// remove all conflicting hashes from mp.conflicts list // 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 payer := tx.Signers[mp.payerIndex].Account
actualSenderFee, ok := mp.fees[payer] actualSenderFee, ok := mp.fees[payer]
if !ok { if !ok {
actualSenderFee.balance = fee.GetUtilityTokenBalance(payer) actualSenderFee.balance.SetFromBig(fee.GetUtilityTokenBalance(payer))
actualSenderFee.feeSum = big.NewInt(0)
} }
var expectedSenderFee utilityBalanceAndFees var expectedSenderFee utilityBalanceAndFees
@ -541,13 +542,10 @@ func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) ([]*tran
conflictsToBeRemoved = append(conflictsToBeRemoved, existingTx) conflictsToBeRemoved = append(conflictsToBeRemoved, existingTx)
} }
// Step 3: take into account sender's conflicting transactions before balance check. // Step 3: take into account sender's conflicting transactions before balance check.
expectedSenderFee = utilityBalanceAndFees{ expectedSenderFee = actualSenderFee
balance: new(big.Int).Set(actualSenderFee.balance),
feeSum: new(big.Int).Set(actualSenderFee.feeSum),
}
for _, conflictingTx := range conflictsToBeRemoved { for _, conflictingTx := range conflictsToBeRemoved {
if conflictingTx.Signers[mp.payerIndex].Account.Equals(payer) { 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 { } else {

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/holiman/uint256"
"github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/network/payload" "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.NoError(t, mp.Add(tx1, fs))
require.Equal(t, 1, len(mp.fees)) require.Equal(t, 1, len(mp.fees))
require.Equal(t, utilityBalanceAndFees{ require.Equal(t, utilityBalanceAndFees{
balance: big.NewInt(fs.balance), balance: *uint256.NewInt(uint64(fs.balance)),
feeSum: big.NewInt(tx1.NetworkFee), feeSum: *uint256.NewInt(uint64(tx1.NetworkFee)),
}, mp.fees[sender0]) }, mp.fees[sender0])
// balance shouldn't change after adding one more transaction // 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, 2, len(mp.verifiedTxes))
require.Equal(t, 1, len(mp.fees)) require.Equal(t, 1, len(mp.fees))
require.Equal(t, utilityBalanceAndFees{ require.Equal(t, utilityBalanceAndFees{
balance: big.NewInt(fs.balance), balance: *uint256.NewInt(uint64(fs.balance)),
feeSum: big.NewInt(fs.balance), feeSum: *uint256.NewInt(uint64(fs.balance)),
}, mp.fees[sender0]) }, mp.fees[sender0])
// can't add more transactions as we don't have enough GAS // 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.Error(t, mp.Add(tx3, fs))
require.Equal(t, 1, len(mp.fees)) require.Equal(t, 1, len(mp.fees))
require.Equal(t, utilityBalanceAndFees{ require.Equal(t, utilityBalanceAndFees{
balance: big.NewInt(fs.balance), balance: *uint256.NewInt(uint64(fs.balance)),
feeSum: big.NewInt(fs.balance), feeSum: *uint256.NewInt(uint64(fs.balance)),
}, mp.fees[sender0]) }, mp.fees[sender0])
// check whether sender's fee updates correctly // check whether sender's fee updates correctly
@ -302,8 +303,8 @@ func TestMemPoolFees(t *testing.T) {
}, fs) }, fs)
require.Equal(t, 1, len(mp.fees)) require.Equal(t, 1, len(mp.fees))
require.Equal(t, utilityBalanceAndFees{ require.Equal(t, utilityBalanceAndFees{
balance: big.NewInt(fs.balance), balance: *uint256.NewInt(uint64(fs.balance)),
feeSum: big.NewInt(tx2.NetworkFee), feeSum: *uint256.NewInt(uint64(tx2.NetworkFee)),
}, mp.fees[sender0]) }, mp.fees[sender0])
// there should be nothing left // there should be nothing left