diff --git a/core/sys_context.go b/core/sys_context.go index cf6c819f16..2f8942a085 100644 --- a/core/sys_context.go +++ b/core/sys_context.go @@ -17,7 +17,7 @@ type SysContractCallCtx struct { gasForAlternativeCurrency uint64 // gasPriceMinimums stores values for whitelisted currencies keyed by their contract address // Note that native token(CELO) is keyed by common.ZeroAddress - gasPriceMinimums map[common.Address]*big.Int + gasPriceMinimums GasPriceMinimums } // NewSysContractCallCtx creates the SysContractCallCtx object and makes the contract calls. @@ -63,7 +63,34 @@ func (sc *SysContractCallCtx) IsWhitelisted(feeCurrency *common.Address) bool { } // GetGasPriceMinimum retrieves gas price minimum for given fee currency address. +// Note that the CELO currency is keyed by the Zero address. func (sc *SysContractCallCtx) GetGasPriceMinimum(feeCurrency *common.Address) *big.Int { + return sc.gasPriceMinimums.GetGasPriceMinimum(feeCurrency) +} + +// GetCurrentGasPriceMinimumMap returns the gas price minimum map for all whitelisted currencies. +// Note that the CELO currency is keyed by the Zero address. +func (sc *SysContractCallCtx) GetCurrentGasPriceMinimumMap() GasPriceMinimums { + return sc.gasPriceMinimums +} + +type GasPriceMinimums map[common.Address]*big.Int + +func (gpm GasPriceMinimums) valOrDefault(key common.Address) *big.Int { + val, ok := gpm[key] + if !ok { + return gasprice_minimum.FallbackGasPriceMinimum + } + return val +} + +// GetNativeGPM retrieves the gas price minimum for the native currency. +func (gpm GasPriceMinimums) GetNativeGPM() *big.Int { + return gpm.valOrDefault(common.ZeroAddress) +} + +// GetGasPriceMinimum retrieves gas price minimum for given fee currency address, it returns gasprice_minimum.FallbackGasPriceMinimum when there is an error +func (gpm GasPriceMinimums) GetGasPriceMinimum(feeCurrency *common.Address) *big.Int { // feeCurrency for native token(CELO) is nil, so we bind common.ZeroAddress as key var key common.Address if feeCurrency == nil { @@ -72,9 +99,5 @@ func (sc *SysContractCallCtx) GetGasPriceMinimum(feeCurrency *common.Address) *b key = *feeCurrency } - gasPriceMinimum, ok := sc.gasPriceMinimums[key] - if !ok { - return gasprice_minimum.FallbackGasPriceMinimum - } - return gasPriceMinimum + return gpm.valOrDefault(key) } diff --git a/core/tx_list.go b/core/tx_list.go index d96de139c8..726a4aea61 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -612,113 +612,6 @@ func (h *priceHeap) Pop() interface{} { return x } -// multiCurrencyPriceHeap is a heap.Interface implementation over transactions -// with different fee currencies for retrieving price-sorted transactions to discard -// when the pool fills up. If baseFee is set then the heap is sorted based on the -// effective tip based on the given base fee. If baseFee is nil then the sorting -// is based on gasFeeCap. -type multiCurrencyPriceHeap struct { - currencyCmpFn func(*big.Int, *common.Address, *big.Int, *common.Address) int - baseFeeFn func(*common.Address) *big.Int // heap should always be re-sorted after baseFee is changed - nonNilCurrencyHeaps map[common.Address]*priceHeap // Heap of prices of all the stored non-nil currency transactions - nilCurrencyHeap *priceHeap // Heap of prices of all the stored nil currency transactions - -} - -// Add to the heap. Must call Init afterwards to retain the heap invariants. -func (h *multiCurrencyPriceHeap) Add(tx *types.Transaction) { - if fc := tx.FeeCurrency(); fc == nil { - h.nilCurrencyHeap.list = append(h.nilCurrencyHeap.list, tx) - } else { - if _, ok := h.nonNilCurrencyHeaps[*fc]; !ok { - h.nonNilCurrencyHeaps[*fc] = &priceHeap{ - baseFee: h.baseFeeFn(fc), - } - - } - sh := h.nonNilCurrencyHeaps[*fc] - sh.list = append(sh.list, tx) - } -} - -func (h *multiCurrencyPriceHeap) Push(tx *types.Transaction) { - if fc := tx.FeeCurrency(); fc == nil { - h.nilCurrencyHeap.Push(tx) - } else { - if _, ok := h.nonNilCurrencyHeaps[*fc]; !ok { - h.nonNilCurrencyHeaps[*fc] = &priceHeap{ - baseFee: h.baseFeeFn(fc), - } - - } - sh := h.nonNilCurrencyHeaps[*fc] - sh.Push(tx) - } -} - -func (h *multiCurrencyPriceHeap) Pop() *types.Transaction { - var cheapestHeap *priceHeap - var cheapestTxn *types.Transaction - - if len(h.nilCurrencyHeap.list) > 0 { - cheapestHeap = h.nilCurrencyHeap - cheapestTxn = h.nilCurrencyHeap.list[0] - } - - for _, priceHeap := range h.nonNilCurrencyHeaps { - if len(priceHeap.list) > 0 { - if cheapestHeap == nil { - cheapestHeap = priceHeap - cheapestTxn = cheapestHeap.list[0] - } else { - txn := priceHeap.list[0] - if h.currencyCmpFn(txn.GasPrice(), txn.FeeCurrency(), cheapestTxn.GasPrice(), cheapestTxn.FeeCurrency()) < 0 { - cheapestHeap = priceHeap - cheapestTxn = txn - } - } - } - } - - if cheapestHeap != nil { - return heap.Pop(cheapestHeap).(*types.Transaction) - } - return nil - -} - -func (h *multiCurrencyPriceHeap) Len() int { - r := len(h.nilCurrencyHeap.list) - for _, priceHeap := range h.nonNilCurrencyHeaps { - r += len(priceHeap.list) - } - return r -} - -func (h *multiCurrencyPriceHeap) Init() { - heap.Init(h.nilCurrencyHeap) - for _, priceHeap := range h.nonNilCurrencyHeaps { - heap.Init(priceHeap) - } -} - -func (h *multiCurrencyPriceHeap) Clear() { - h.nilCurrencyHeap.list = nil - for _, priceHeap := range h.nonNilCurrencyHeaps { - priceHeap.list = nil - } -} - -func (h *multiCurrencyPriceHeap) SetBaseFee(txCtx *txPoolContext) { - h.currencyCmpFn = txCtx.CmpValues - h.baseFeeFn = txCtx.GetGasPriceMinimum - h.nilCurrencyHeap.baseFee = txCtx.GetGasPriceMinimum(nil) - for currencyAddr, heap := range h.nonNilCurrencyHeaps { - heap.baseFee = txCtx.GetGasPriceMinimum(¤cyAddr) - } - -} - // txPricedList is a price-sorted heap to allow operating on transactions pool // contents in a price-incrementing way. It's built opon the all transactions // in txpool but only interested in the remote part. It means only remote transactions @@ -752,18 +645,8 @@ func newTxPricedList(all *txLookup, ctx *atomic.Value, maxStales int64) *txPrice ctx: ctx, all: all, maxStales: maxStales, - urgent: multiCurrencyPriceHeap{ - currencyCmpFn: txCtx.CmpValues, - nilCurrencyHeap: &priceHeap{}, - nonNilCurrencyHeaps: make(map[common.Address]*priceHeap), - baseFeeFn: txCtx.GetGasPriceMinimum, - }, - floating: multiCurrencyPriceHeap{ - currencyCmpFn: txCtx.CmpValues, - nilCurrencyHeap: &priceHeap{}, - nonNilCurrencyHeaps: make(map[common.Address]*priceHeap), - baseFeeFn: txCtx.GetGasPriceMinimum, - }, + urgent: newMultiCurrencyPriceHeap(txCtx.CmpValues, txCtx.SysContractCallCtx.GetCurrentGasPriceMinimumMap()), + floating: newMultiCurrencyPriceHeap(txCtx.CmpValues, txCtx.SysContractCallCtx.GetCurrentGasPriceMinimumMap()), } } @@ -808,8 +691,8 @@ func (l *txPricedList) Underpriced(tx *types.Transaction) bool { } func (l *txPricedList) underpricedForMulti(h *multiCurrencyPriceHeap, tx *types.Transaction) bool { - underpriced := l.underpricedFor(h.nilCurrencyHeap, tx) - for _, sh := range h.nonNilCurrencyHeaps { + underpriced := l.underpricedFor(h.nativeCurrencyHeap, tx) + for _, sh := range h.currencyHeaps { if l.underpricedFor(sh, tx) { underpriced = true } @@ -911,6 +794,6 @@ func (l *txPricedList) Reheap() { // SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not // necessary to call right before SetBaseFee when processing a new block. func (l *txPricedList) SetBaseFee(txCtx *txPoolContext) { - l.urgent.SetBaseFee(txCtx) + l.urgent.UpdateFeesAndCurrencies(txCtx.CmpValues, txCtx.SysContractCallCtx.GetCurrentGasPriceMinimumMap()) l.Reheap() } diff --git a/core/tx_multicurrency_priceheap.go b/core/tx_multicurrency_priceheap.go new file mode 100644 index 0000000000..0eac32bafb --- /dev/null +++ b/core/tx_multicurrency_priceheap.go @@ -0,0 +1,132 @@ +package core + +import ( + "container/heap" + "math/big" + + "github.com/celo-org/celo-blockchain/common" + "github.com/celo-org/celo-blockchain/core/types" +) + +type CurrencyCmpFn func(*big.Int, *common.Address, *big.Int, *common.Address) int + +// IsCheaper returns true if tx1 is cheaper than tx2 (GasPrice with currency comparison) +func (cc CurrencyCmpFn) IsCheaper(tx1, tx2 *types.Transaction) bool { + return cc(tx1.GasPrice(), tx1.FeeCurrency(), tx2.GasPrice(), tx2.FeeCurrency()) < 0 +} + +// multiCurrencyPriceHeap is a heap.Interface implementation over transactions +// with different fee currencies for retrieving price-sorted transactions to discard +// when the pool fills up. If baseFee is set then the heap is sorted based on the +// effective tip based on the given base fee. If baseFee is nil then the sorting +// is based on gasFeeCap. +type multiCurrencyPriceHeap struct { + currencyCmp CurrencyCmpFn + gpm GasPriceMinimums // heap should always be re-sorted after gas price minimums (baseFees) is changed + currencyHeaps map[common.Address]*priceHeap // Heap of prices of all the stored non-nil currency transactions + nativeCurrencyHeap *priceHeap // Heap of prices of all the stored nil currency transactions +} + +func newMultiCurrencyPriceHeap(currencyCmp CurrencyCmpFn, gpm GasPriceMinimums) multiCurrencyPriceHeap { + return multiCurrencyPriceHeap{ + currencyCmp: currencyCmp, + gpm: gpm, + + // inner state + + nativeCurrencyHeap: &priceHeap{}, + currencyHeaps: make(map[common.Address]*priceHeap), + } +} + +// getHeapFor returns the proper heap for the given transaction, and creates it +// if it's not available in the currencyHeaps +func (h *multiCurrencyPriceHeap) getHeapFor(tx *types.Transaction) *priceHeap { + fc := tx.FeeCurrency() + if fc == nil { + return h.nativeCurrencyHeap + } + if _, ok := h.currencyHeaps[*fc]; !ok { + h.currencyHeaps[*fc] = &priceHeap{ + baseFee: h.gpm.GetGasPriceMinimum(fc), + } + } + return h.currencyHeaps[*fc] +} + +// Add to the heap. Must call Init afterwards to retain the heap invariants. +func (h *multiCurrencyPriceHeap) Add(tx *types.Transaction) { + ph := h.getHeapFor(tx) + ph.list = append(ph.list, tx) +} + +// Push to the heap, maintains heap invariants. +func (h *multiCurrencyPriceHeap) Push(tx *types.Transaction) { + ph := h.getHeapFor(tx) + heap.Push(ph, tx) +} + +func (h *multiCurrencyPriceHeap) cheapestTxs() []*types.Transaction { + txs := make([]*types.Transaction, 0, 1+len(h.currencyHeaps)) + if len(h.nativeCurrencyHeap.list) > 0 { + txs = append(txs, h.nativeCurrencyHeap.list[0]) + } + for _, ph := range h.currencyHeaps { + if len(ph.list) > 0 { + txs = append(txs, ph.list[0]) + } + } + return txs +} + +func (h *multiCurrencyPriceHeap) cheapestTx() *types.Transaction { + txs := h.cheapestTxs() + var cheapestTx *types.Transaction + for _, tx := range txs { + if cheapestTx == nil || h.currencyCmp.IsCheaper(tx, cheapestTx) { + cheapestTx = tx + } + } + return cheapestTx +} + +func (h *multiCurrencyPriceHeap) Pop() *types.Transaction { + cheapestTx := h.cheapestTx() + if cheapestTx == nil { + return nil + } + ph := h.getHeapFor(cheapestTx) + return heap.Pop(ph).(*types.Transaction) +} + +func (h *multiCurrencyPriceHeap) Len() int { + r := len(h.nativeCurrencyHeap.list) + for _, priceHeap := range h.currencyHeaps { + r += len(priceHeap.list) + } + return r +} + +func (h *multiCurrencyPriceHeap) Init() { + heap.Init(h.nativeCurrencyHeap) + for _, priceHeap := range h.currencyHeaps { + heap.Init(priceHeap) + } +} + +func (h *multiCurrencyPriceHeap) Clear() { + h.nativeCurrencyHeap.list = nil + for _, priceHeap := range h.currencyHeaps { + priceHeap.list = nil + } +} + +func (h *multiCurrencyPriceHeap) UpdateFeesAndCurrencies(currencyCmpFn CurrencyCmpFn, gpm GasPriceMinimums) { + h.currencyCmp = currencyCmpFn + h.gpm = gpm + h.nativeCurrencyHeap.baseFee = gpm.GetNativeGPM() + for currencyAddr, heap := range h.currencyHeaps { + heap.baseFee = gpm.GetGasPriceMinimum(¤cyAddr) + } + +} diff --git a/core/tx_multicurrency_priceheap_test.go b/core/tx_multicurrency_priceheap_test.go new file mode 100644 index 0000000000..20ccccf080 --- /dev/null +++ b/core/tx_multicurrency_priceheap_test.go @@ -0,0 +1,326 @@ +package core + +import ( + "math/big" + "testing" + + "github.com/celo-org/celo-blockchain/common" + "github.com/celo-org/celo-blockchain/core/types" + "github.com/stretchr/testify/assert" +) + +func curr(currency int) *common.Address { + curr := common.BigToAddress(big.NewInt(int64(currency))) + return &curr +} + +func tx(price int) *types.Transaction { + return types.NewTx(&types.LegacyTx{GasPrice: big.NewInt(int64(price))}) +} + +func txC(price int, currency *common.Address) *types.Transaction { + return types.NewTx(&types.LegacyTx{ + GasPrice: big.NewInt(int64(price)), + FeeCurrency: currency, + }) +} + +func TestNilPushes(t *testing.T) { + m := newMultiCurrencyPriceHeap(nil, nil) + m.Push(tx(100)) + m.Push(tx(50)) + m.Push(tx(200)) + m.Push(tx(75)) + assert.Equal(t, 4, m.Len()) + tm := m.Pop() + assert.Equal(t, big.NewInt(50), tm.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestCurrencyPushes(t *testing.T) { + c := curr(1) + gpm := map[common.Address]*big.Int{ + *c: big.NewInt(1000), + } + m := newMultiCurrencyPriceHeap(nil, gpm) + m.Push(txC(100, c)) + m.Push(txC(50, c)) + m.Push(txC(200, c)) + m.Push(txC(75, c)) + assert.Equal(t, 4, m.Len()) + tm := m.Pop() + assert.Equal(t, big.NewInt(50), tm.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestNilAdds(t *testing.T) { + m := newMultiCurrencyPriceHeap(nil, nil) + m.Add(tx(100)) + m.Add(tx(250)) + m.Add(tx(50)) + m.Add(tx(200)) + m.Add(tx(75)) + assert.Equal(t, 5, m.Len()) + tm := m.Pop() + // there was no Init after the adds, so it should return them in FIFO order + assert.Equal(t, big.NewInt(100), tm.GasPrice()) + assert.Equal(t, 4, m.Len()) + + m.Init() + tm2 := m.Pop() + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestCurrencyAdds(t *testing.T) { + c := curr(1) + gpm := map[common.Address]*big.Int{ + *c: big.NewInt(1000), + } + m := newMultiCurrencyPriceHeap(nil, gpm) + m.Add(txC(100, c)) + m.Add(txC(250, c)) + m.Add(txC(50, c)) + m.Add(txC(200, c)) + m.Add(txC(75, c)) + assert.Equal(t, 5, m.Len()) + tm := m.Pop() + // there was no Init after the adds, so it should return them in FIFO order + assert.Equal(t, big.NewInt(100), tm.GasPrice()) + assert.Equal(t, 4, m.Len()) + + m.Init() + tm2 := m.Pop() + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, 3, m.Len()) +} + +func TestMultiPushPop(t *testing.T) { + c1 := curr(1) + c2 := curr(2) + + gpm := map[common.Address]*big.Int{ + *c1: big.NewInt(10), + *c2: big.NewInt(20), + } + var cmp CurrencyCmpFn = func(p1 *big.Int, cc1 *common.Address, p2 *big.Int, cc2 *common.Address) int { + var val1 int = int(p1.Int64()) + var val2 int = int(p2.Int64()) + if cc1 == c1 { + val1 *= 10 + } + if cc2 == c1 { + val2 *= 10 + } + if cc1 == c2 { + val1 *= 100 + } + if cc2 == c2 { + val2 *= 100 + } + return val1 - val2 + } + m := newMultiCurrencyPriceHeap(cmp, gpm) + m.Push(txC(100, c1)) // 1000 + m.Push(txC(250, c1)) // 2500 + m.Push(txC(50, c1)) // 500 + m.Push(txC(200, c1)) // 2000 + m.Push(txC(75, c1)) // 750 + + m.Push(txC(9, c2)) // 900 + m.Push(txC(26, c2)) // 2600 + m.Push(txC(4, c2)) // 400 + m.Push(txC(21, c2)) // 2100 + m.Push(txC(7, c2)) // 700 + + m.Push(tx(1100)) // 1100 + m.Push(tx(2700)) // 2700 + m.Push(tx(560)) // 560 + m.Push(tx(2150)) // 2150 + m.Push(tx(750)) // 750 + + assert.Equal(t, 15, m.Len()) + tm := m.Pop() + assert.Equal(t, 14, m.Len()) + // 400 + assert.Equal(t, big.NewInt(4), tm.GasPrice()) + assert.Equal(t, c2, tm.FeeCurrency()) + + tm2 := m.Pop() + assert.Equal(t, 13, m.Len()) + // 500 + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, c1, tm2.FeeCurrency()) + + tm3 := m.Pop() + assert.Equal(t, 12, m.Len()) + // 560 + assert.Equal(t, big.NewInt(560), tm3.GasPrice()) + assert.Nil(t, tm3.FeeCurrency()) + + // A few more re-pushes + m.Push(tx(585)) // 585 + m.Push(txC(3, c2)) // 300 + assert.Equal(t, 14, m.Len()) + + tm4 := m.Pop() + assert.Equal(t, 13, m.Len()) + // 300 + assert.Equal(t, big.NewInt(3), tm4.GasPrice()) + assert.Equal(t, c2, tm4.FeeCurrency()) + + tm5 := m.Pop() + assert.Equal(t, 12, m.Len()) + // 585 + assert.Equal(t, big.NewInt(585), tm5.GasPrice()) + assert.Nil(t, tm5.FeeCurrency()) +} + +func TestMultiAddInit(t *testing.T) { + c1 := curr(1) + c2 := curr(2) + + gpm := map[common.Address]*big.Int{ + *c1: big.NewInt(10), + *c2: big.NewInt(20), + } + var cmp CurrencyCmpFn = func(p1 *big.Int, cc1 *common.Address, p2 *big.Int, cc2 *common.Address) int { + var val1 int = int(p1.Int64()) + var val2 int = int(p2.Int64()) + if cc1 == c1 { + val1 *= 10 + } + if cc2 == c1 { + val2 *= 10 + } + if cc1 == c2 { + val1 *= 100 + } + if cc2 == c2 { + val2 *= 100 + } + return val1 - val2 + } + m := newMultiCurrencyPriceHeap(cmp, gpm) + m.Add(txC(100, c1)) // 1000 + m.Add(txC(250, c1)) // 2500 + m.Add(txC(50, c1)) // 500 + m.Add(txC(200, c1)) // 2000 + m.Add(txC(75, c1)) // 750 + + m.Add(txC(9, c2)) // 900 + m.Add(txC(26, c2)) // 2600 + m.Add(txC(4, c2)) // 400 + m.Add(txC(21, c2)) // 2100 + m.Add(txC(7, c2)) // 700 + + m.Add(tx(1100)) // 1100 + m.Add(tx(2700)) // 2700 + m.Add(tx(560)) // 560 + m.Add(tx(2150)) // 2150 + m.Add(tx(750)) // 750 + + // no init yet, returns the cheapest of the first of every currency + assert.Equal(t, 15, m.Len()) + odd := m.Pop() + assert.Equal(t, 14, m.Len()) + assert.Equal(t, big.NewInt(9), odd.GasPrice()) + assert.Equal(t, c2, odd.FeeCurrency()) + + m.Init() + + tm := m.Pop() + assert.Equal(t, 13, m.Len()) + // 400 + assert.Equal(t, big.NewInt(4), tm.GasPrice()) + assert.Equal(t, c2, tm.FeeCurrency()) + + tm2 := m.Pop() + assert.Equal(t, 12, m.Len()) + // 500 + assert.Equal(t, big.NewInt(50), tm2.GasPrice()) + assert.Equal(t, c1, tm2.FeeCurrency()) + + tm3 := m.Pop() + assert.Equal(t, 11, m.Len()) + // 560 + assert.Equal(t, big.NewInt(560), tm3.GasPrice()) + assert.Nil(t, tm3.FeeCurrency()) + + // Re add and break it + m.Add(tx(585)) // 585 + m.Add(txC(3, c2)) // 300 + assert.Equal(t, 13, m.Len()) + + tm4 := m.Pop() + assert.Equal(t, 12, m.Len()) + // No Init, next in line should be the 700 tx + assert.Equal(t, big.NewInt(7), tm4.GasPrice()) + assert.Equal(t, c2, tm4.FeeCurrency()) + + m.Init() + tm5 := m.Pop() + assert.Equal(t, 11, m.Len()) + // Init called, new 300 one should be popped first + assert.Equal(t, big.NewInt(3), tm5.GasPrice()) + assert.Equal(t, c2, tm5.FeeCurrency()) +} + +func TestClear(t *testing.T) { + c := curr(1) + gpm := map[common.Address]*big.Int{ + *c: big.NewInt(1000), + } + m := newMultiCurrencyPriceHeap(nil, gpm) + m.Push(txC(100, c)) + m.Push(txC(250, c)) + m.Push(txC(50, c)) + m.Push(txC(200, c)) + m.Push(txC(75, c)) + m.Push(tx(100)) + m.Push(tx(700)) + assert.Equal(t, 7, m.Len()) + m.Clear() + assert.Equal(t, 0, m.Len()) + assert.Nil(t, m.Pop()) +} + +func TestIsCheaper_FwdFields(t *testing.T) { + curr1 := common.BigToAddress(big.NewInt(123)) + price1 := big.NewInt(100) + curr2 := common.BigToAddress(big.NewInt(123)) + price2 := big.NewInt(200) + tx1 := types.NewTx(&types.LegacyTx{ + GasPrice: price1, + FeeCurrency: &curr1, + }) + tx2 := types.NewTx(&types.LegacyTx{ + GasPrice: price2, + FeeCurrency: &curr2, + }) + var cmp CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + assert.Equal(t, price1, p1) + assert.Equal(t, price2, p2) + assert.Equal(t, curr1, *c1) + assert.Equal(t, curr2, *c2) + return -1 + } + assert.True(t, cmp.IsCheaper(tx1, tx2)) +} + +func TestIsCheaper(t *testing.T) { + tx1 := types.NewTx(&types.LegacyTx{}) + tx2 := types.NewTx(&types.LegacyTx{}) + var cheaper CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + return -1 + } + var equal CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + return 0 + } + var notCheaper CurrencyCmpFn = func(p1 *big.Int, c1 *common.Address, p2 *big.Int, c2 *common.Address) int { + return 1 + } + assert.True(t, cheaper.IsCheaper(tx1, tx2)) + assert.False(t, equal.IsCheaper(tx1, tx2)) + assert.False(t, notCheaper.IsCheaper(tx1, tx2)) +}