Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVM-728 Gas price comparison for executable transactions in pricedQueue #1696

Merged
merged 5 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gasprice/feehistory.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,13 @@ func (g *GasHelper) FeeHistory(blockCount uint64, newestBlock uint64, rewardPerc
}

sorter := make([]*txGasAndReward, len(block.Transactions))
baseFee := new(big.Int).SetUint64(block.Header.BaseFee)

for j, tx := range block.Transactions {
cost := tx.Cost()
sorter[j] = &txGasAndReward{
gasUsed: cost.Sub(cost, tx.Value),
reward: tx.EffectiveTip(block.Header.BaseFee),
reward: tx.EffectiveGasTip(baseFee),
}
}

Expand Down
12 changes: 6 additions & 6 deletions gasprice/gasprice.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (g *GasHelper) MaxPriorityFeePerGas() (*big.Int, error) {
var allPrices []*big.Int

collectPrices := func(block *types.Block) error {
baseFee := block.Header.BaseFee
baseFee := new(big.Int).SetUint64(block.Header.BaseFee)
txSorter := newTxByEffectiveTipSorter(block.Transactions, baseFee)
sort.Sort(txSorter)

Expand All @@ -150,7 +150,7 @@ func (g *GasHelper) MaxPriorityFeePerGas() (*big.Int, error) {
blockTxPrices := make([]*big.Int, 0)

for _, tx := range txSorter.txs {
tip := tx.EffectiveTip(baseFee)
tip := tx.EffectiveGasTip(baseFee)

if tip.Cmp(g.ignorePrice) == -1 {
// ignore transactions with tip lower than ignore price
Expand Down Expand Up @@ -238,11 +238,11 @@ func (g *GasHelper) MaxPriorityFeePerGas() (*big.Int, error) {
// txSortedByEffectiveTip sorts transactions by effective tip from smallest to largest
type txSortedByEffectiveTip struct {
txs []*types.Transaction
baseFee uint64
baseFee *big.Int
}

// newTxByEffectiveTipSorter is constructor function for txSortedByEffectiveTip
func newTxByEffectiveTipSorter(txs []*types.Transaction, baseFee uint64) *txSortedByEffectiveTip {
func newTxByEffectiveTipSorter(txs []*types.Transaction, baseFee *big.Int) *txSortedByEffectiveTip {
return &txSortedByEffectiveTip{
txs: txs,
baseFee: baseFee,
Expand All @@ -259,8 +259,8 @@ func (t *txSortedByEffectiveTip) Swap(i, j int) {

// Less is implementation of sort.Interface
func (t *txSortedByEffectiveTip) Less(i, j int) bool {
tip1 := t.txs[i].EffectiveTip(t.baseFee)
tip2 := t.txs[j].EffectiveTip(t.baseFee)
tip1 := t.txs[i].EffectiveGasTip(t.baseFee)
tip2 := t.txs[j].EffectiveGasTip(t.baseFee)

return tip1.Cmp(tip2) < 0
}
84 changes: 28 additions & 56 deletions txpool/queue_priced.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package txpool
import (
"container/heap"
"math/big"
"sync/atomic"

"github.com/0xPolygon/polygon-edge/types"
)
Expand All @@ -12,19 +11,16 @@ type pricedQueue struct {
queue *maxPriceQueue
}

func newPricedQueue() *pricedQueue {
q := pricedQueue{
queue: &maxPriceQueue{},
// init initializes the underlying queue with initial transactions
func (q *pricedQueue) init(baseFee uint64, initialTxs []*types.Transaction) *pricedQueue {
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
q.queue = &maxPriceQueue{
baseFee: new(big.Int).SetUint64(baseFee),
txs: initialTxs,
}

heap.Init(q.queue)

return &q
}

// clear empties the underlying queue.
func (q *pricedQueue) clear() {
q.queue.txs = q.queue.txs[:0]
return q
}

// Pushes the given transactions onto the queue.
Expand All @@ -48,13 +44,13 @@ func (q *pricedQueue) pop() *types.Transaction {
}

// length returns the number of transactions in the queue.
func (q *pricedQueue) length() uint64 {
return uint64(q.queue.Len())
func (q *pricedQueue) length() int {
return q.queue.Len()
}

// transactions sorted by gas price (descending)
type maxPriceQueue struct {
baseFee uint64
baseFee *big.Int
txs []*types.Transaction
}

Expand All @@ -76,17 +72,6 @@ func (q *maxPriceQueue) Swap(i, j int) {
q.txs[i], q.txs[j] = q.txs[j], q.txs[i]
}

func (q *maxPriceQueue) Less(i, j int) bool {
switch q.cmp(q.txs[i], q.txs[j]) {
case -1:
return true
case 1:
return false
default:
return q.txs[i].Nonce > q.txs[j].Nonce
}
}

func (q *maxPriceQueue) Push(x interface{}) {
transaction, ok := x.(*types.Transaction)
if !ok {
Expand All @@ -105,45 +90,32 @@ func (q *maxPriceQueue) Pop() interface{} {
return x
}

// cmp compares the given transactions by their fees and returns:
// - 0 if they have same fees
// - 1 if a has higher fees than b
// - -1 if b has higher fees than a
func (q *maxPriceQueue) cmp(a, b *types.Transaction) int {
baseFee := atomic.LoadUint64(&q.baseFee)
effectiveTipA := a.EffectiveTip(baseFee)
effectiveTipB := b.EffectiveTip(baseFee)

// Compare effective tips if baseFee is specified
if c := effectiveTipA.Cmp(effectiveTipB); c != 0 {
return c
}

aGasFeeCap, bGasFeeCap := new(big.Int), new(big.Int)

if a.GasFeeCap != nil {
aGasFeeCap = aGasFeeCap.Set(a.GasFeeCap)
// @see https://github.com/etclabscore/core-geth/blob/4e2b0e37f89515a4e7b6bafaa40910a296cb38c0/core/txpool/list.go#L458
// for details why is something implemented like it is
func (q *maxPriceQueue) Less(i, j int) bool {
switch cmp(q.txs[i], q.txs[j], q.baseFee) {
case -1:
return false
case 1:
return true
default:
return q.txs[i].Nonce < q.txs[j].Nonce
}
}

if b.GasFeeCap != nil {
bGasFeeCap = bGasFeeCap.Set(b.GasFeeCap)
func cmp(a, b *types.Transaction, baseFee *big.Int) int {
if baseFee.BitLen() > 0 {
// Compare effective tips if baseFee is specified
if c := a.EffectiveGasTip(baseFee).Cmp(b.EffectiveGasTip(baseFee)); c != 0 {
return c
}
}

// Compare fee caps if baseFee is not specified or effective tips are equal
if c := aGasFeeCap.Cmp(bGasFeeCap); c != 0 {
if c := a.GetGasFeeCap().Cmp(b.GetGasFeeCap()); c != 0 {
return c
}

aGasTipCap, bGasTipCap := new(big.Int), new(big.Int)

if a.GasTipCap != nil {
aGasTipCap = aGasTipCap.Set(a.GasTipCap)
}

if b.GasTipCap != nil {
bGasTipCap = bGasTipCap.Set(b.GasTipCap)
}

// Compare tips if effective tips and fee caps are equal
return aGasTipCap.Cmp(bGasTipCap)
return a.GetGasTipCap().Cmp(b.GetGasTipCap())
}
26 changes: 10 additions & 16 deletions txpool/queue_priced_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ package txpool
import (
"math/big"
"math/rand"
"sort"
"testing"

"github.com/stretchr/testify/assert"

"github.com/0xPolygon/polygon-edge/types"
"github.com/stretchr/testify/assert"
)

func Test_maxPriceQueue(t *testing.T) {
Expand Down Expand Up @@ -224,6 +222,12 @@ func Test_maxPriceQueue(t *testing.T) {
},
},
},
{
name: "empty",
baseFee: 0,
unsorted: nil,
sorted: []*types.Transaction{},
},
}

for _, tt := range testTable {
Expand All @@ -232,15 +236,10 @@ func Test_maxPriceQueue(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

queue := &maxPriceQueue{
baseFee: tt.baseFee,
txs: tt.unsorted,
}

sort.Sort(queue)
queue := (&pricedQueue{}).init(tt.baseFee, tt.unsorted)

for _, tx := range tt.sorted {
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
actual := queue.Pop()
actual := queue.pop()
assert.Equal(t, tx, actual)
}
})
Expand Down Expand Up @@ -269,12 +268,7 @@ func Benchmark_pricedQueue(t *testing.B) {
for _, tt := range testTable {
t.Run(tt.name, func(b *testing.B) {
for i := 0; i < t.N; i++ {
q := newPricedQueue()
q.queue.baseFee = uint64(i)

for _, tx := range tt.unsortedTxs {
q.push(tx)
}
q := (&pricedQueue{}).init(uint64(100), tt.unsortedTxs)

for q.length() > 0 {
_ = q.pop()
Expand Down
21 changes: 4 additions & 17 deletions txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func NewTxPool(
logger: logger.Named("txpool"),
forks: forks,
store: store,
executables: newPricedQueue(),
executables: (&pricedQueue{}).init(0, nil),
vcastellm marked this conversation as resolved.
Show resolved Hide resolved
accounts: accountsMap{maxEnqueuedLimit: config.MaxAccountEnqueued},
index: lookupMap{all: make(map[types.Hash]*types.Transaction)},
gauge: slotGauge{height: 0, max: config.MaxSlots},
Expand Down Expand Up @@ -325,21 +325,14 @@ func (p *TxPool) AddTx(tx *types.Transaction) error {
// Prepare generates all the transactions
// ready for execution. (primaries)
func (p *TxPool) Prepare(baseFee uint64) {
// clear from previous round
if p.executables.length() != 0 {
p.executables.clear()
}

// set base fee
p.updateBaseFee(baseFee)
atomic.StoreUint64(&p.baseFee, baseFee)

// fetch primary from each account
primaries := p.accounts.getPrimaries()

// push primaries to the executables queue
for _, tx := range primaries {
p.executables.push(tx)
}
// re-initialize executables queue with primaries
p.executables.init(baseFee, primaries)
}

// Peek returns the best-price selected
Expand Down Expand Up @@ -1027,12 +1020,6 @@ func (p *TxPool) Length() uint64 {
return p.accounts.promoted()
}

// updateBaseFee updates base fee in the tx pool and priced queue
func (p *TxPool) updateBaseFee(baseFee uint64) {
atomic.StoreUint64(&p.baseFee, baseFee)
atomic.StoreUint64(&p.executables.queue.baseFee, baseFee)
}

// toHash returns the hash(es) of given transaction(s)
func toHash(txs ...*types.Transaction) (hashes []types.Hash) {
for _, tx := range txs {
Expand Down
Loading