Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9b17b0a
core/types: add 1559 compatibility to Transaction helpers
adietrichs Feb 5, 2021
e1bb4a8
core/txpool: update TxPool for EIP-1559 compatibility
adietrichs Feb 5, 2021
1785e33
core/txpool: add Tip as secondary comparison criterion on txPricedLis…
adietrichs Feb 15, 2021
ef9e28b
core/txpool: add ErrTipAboveFeeCap error and respective check during …
adietrichs Feb 15, 2021
9209005
core/txpool: refactor: create txLookup.RemotesBelowTip() to facilitat…
adietrichs Feb 15, 2021
1a114fb
core/txpool: tests: add dynamicFeeTransaction() helper function
adietrichs Feb 15, 2021
ce51585
core/txpool: tests: create TestTransactionTypeNotSupported()
adietrichs Feb 15, 2021
91ea2f3
core/txpool: tests: create TestTransactionTipAboveFeeCap()
adietrichs Feb 15, 2021
ba79aa2
core/txpool: tests: create TestTransactionPoolRepricingDynamicFee()
adietrichs Feb 15, 2021
6ade382
core/txpool: tests: add dynamic fee transactions to TestTransactionPo…
adietrichs Feb 15, 2021
503ad99
core/txpool: tests: create TestTransactionPoolUnderpricingDynamicFee()
adietrichs Feb 15, 2021
685ccaa
core/txpool: tests: create TestTransactionReplacementDynamicFee()
adietrichs Feb 15, 2021
cff2662
core/tx_pool_test: update signer and remove invalid type test
lightclient Apr 6, 2021
e9de7a6
core/tx_pool_test: rename dynamicFeeTransaction to dynamicFeeTx
lightclient Apr 27, 2021
a53fac4
core: remove tx_list cap function
lightclient Apr 29, 2021
146b299
core/tx_pool: revert log change and simplify test
lightclient Apr 29, 2021
2c32c94
core/types/transaction: add godoc for tip and fee cap cmp functions
lightclient Apr 29, 2021
0d79add
core/tx_pool_test: increase test space for repricing test
lightclient Apr 29, 2021
63d83e5
core/tx_pool_test: clean up tx pool tests
lightclient Apr 29, 2021
8c177a4
core/tx_pool_test: rewrite underpriced txs test
lightclient Apr 30, 2021
9164512
core/tx_pool: fix comment
lightclient May 3, 2021
5c35013
core/tx_pool: update aleut references to london
lightclient May 6, 2021
ab92339
core/tx_pool: remove gas target references
lightclient May 10, 2021
052b113
core/tx_list: require both fee cap and tip be bumped to replace exist…
lightclient May 14, 2021
679535b
core/types: create TxWithMinerFee wrapper
adietrichs Feb 16, 2021
8c703be
core/types, miner: add EIP-1559 support to TransactionsByMinerFeeAndN…
adietrichs Feb 16, 2021
f162c37
miner: set base fee when creating a new header
adietrichs Feb 22, 2021
aed8aa4
miner: correctly handle gas limit for EIP-1559
adietrichs Feb 22, 2021
25b3556
miner: correctly log block miner fees under EIP-1559
adietrichs Feb 22, 2021
034475a
all: rename to NewTransactionsByPriceAndNonce
lightclient Apr 19, 2021
26bd94c
core/types: rename to NewTransactionsByPriceAndNonce
adietrichs May 3, 2021
fb8d450
core/types, miner: create tx.EffectiveTip(baseFee)
adietrichs May 3, 2021
55b0569
core/types: expand TestTransactionPriceNonceSort to cover 1559
adietrichs May 3, 2021
200279f
miner: activate 1559 for testGenerateBlockAndImport tests
adietrichs May 5, 2021
c199228
core,miner: revert naming to TransactionsByPriceAndTime
lightclient May 5, 2021
eaa7a58
core/types/transaction: update effective tip calculation logic
lightclient May 5, 2021
b42d57e
miner: update aleut to london
lightclient May 6, 2021
a8f95b8
core/types/transaction_test: use correct signer for 1559 txs
lightclient May 6, 2021
8d93c95
core/types/transaction: add back sender check
lightclient May 6, 2021
a569757
miner/worker: calculate gas target from gas limit
lightclient May 10, 2021
d286c19
miner, core: refactor CalcGasLimit
holiman May 11, 2021
0930c66
core, miner: fix miner block limit
holiman May 11, 2021
7a3ef74
core, miner: adjust block limit targetting for 1559
holiman May 12, 2021
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
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
from := 0
return func(i int, gen *BlockGen) {
block := gen.PrevBlock(i - 1)
gas := CalcGasLimit(block, block.GasLimit(), block.GasLimit())
gas := block.GasLimit()
for {
gas -= params.TxGas
if gas < params.TxGas {
Expand Down
36 changes: 30 additions & 6 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
// to keep the baseline gas above the provided floor, and increase it towards the
// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
// the gas allowance.
func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
func CalcGasLimit(parentGasUsed, parentGasLimit, gasFloor, gasCeil uint64) uint64 {
// contrib = (parentGasUsed * 3 / 2) / 1024
contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
contrib := (parentGasUsed + parentGasUsed/2) / params.GasLimitBoundDivisor

// decay = parentGasLimit / 1024 -1
decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1
decay := parentGasLimit/params.GasLimitBoundDivisor - 1

/*
strategy: gasLimit of block-to-mine is set based on parent's
Expand All @@ -120,21 +120,45 @@ func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
at that usage) the amount increased/decreased depends on how far away
from parentGasLimit * (2/3) parentGasUsed is.
*/
limit := parent.GasLimit() - decay + contrib
limit := parentGasLimit - decay + contrib
if limit < params.MinGasLimit {
limit = params.MinGasLimit
}
// If we're outside our allowed gas range, we try to hone towards them
if limit < gasFloor {
limit = parent.GasLimit() + decay
limit = parentGasLimit + decay
if limit > gasFloor {
limit = gasFloor
}
} else if limit > gasCeil {
limit = parent.GasLimit() - decay
limit = parentGasLimit - decay
if limit < gasCeil {
limit = gasCeil
}
}
return limit
}

// CalcGasLimit1559 calculates the next block gas limit under 1559 rules.
func CalcGasLimit1559(parentGasLimit, desiredLimit uint64) uint64 {
delta := parentGasLimit/params.GasLimitBoundDivisor - 1
limit := parentGasLimit
if desiredLimit < params.MinGasLimit {
desiredLimit = params.MinGasLimit
}
// If we're outside our allowed gas range, we try to hone towards them
if limit < desiredLimit {
limit = parentGasLimit + delta
if limit > desiredLimit {
limit = desiredLimit
}
return limit
}
if limit > desiredLimit {
limit = parentGasLimit - delta
if limit < desiredLimit {
limit = desiredLimit
}
}
return limit
}
33 changes: 33 additions & 0 deletions core/block_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,36 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads)
}
}

func TestCalcGasLimit1559(t *testing.T) {

for i, tc := range []struct {
pGasLimit uint64
max uint64
min uint64
}{
{20000000, 20019530, 19980470},
{40000000, 40039061, 39960939},
} {
// Increase
if have, want := CalcGasLimit1559(tc.pGasLimit, 2*tc.pGasLimit), tc.max; have != want {
t.Errorf("test %d: have %d want <%d", i, have, want)
}
// Decrease
if have, want := CalcGasLimit1559(tc.pGasLimit, 0), tc.min; have != want {
t.Errorf("test %d: have %d want >%d", i, have, want)
}
// Small decrease
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit-1), tc.pGasLimit-1; have != want {
t.Errorf("test %d: have %d want %d", i, have, want)
}
// Small increase
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit+1), tc.pGasLimit+1; have != want {
t.Errorf("test %d: have %d want %d", i, have, want)
}
// No change
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit), tc.pGasLimit; have != want {
t.Errorf("test %d: have %d want %d", i, have, want)
}
}
}
2 changes: 1 addition & 1 deletion core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
Difficulty: parent.Difficulty(),
UncleHash: parent.UncleHash(),
}),
GasLimit: CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()),
GasLimit: parent.GasLimit(),
Number: new(big.Int).Add(parent.Number(), common.Big1),
Time: time,
}
Expand Down
2 changes: 1 addition & 1 deletion core/state_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
Difficulty: parent.Difficulty(),
UncleHash: parent.UncleHash(),
}),
GasLimit: CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()),
GasLimit: parent.GasLimit(),
Number: new(big.Int).Add(parent.Number(), common.Big1),
Time: parent.Time() + 10,
UncleHash: types.EmptyUncleHash,
Expand Down
56 changes: 23 additions & 33 deletions core/tx_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,15 +279,21 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
// If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce())
if old != nil {
// threshold = oldGP * (100 + priceBump) / 100
// thresholdFeeCap = oldFC * (100 + priceBump) / 100
a := big.NewInt(100 + int64(priceBump))
a = a.Mul(a, old.GasPrice())
aFeeCap := new(big.Int).Mul(a, old.FeeCap())
aTip := a.Mul(a, old.Tip())

// thresholdTip = oldTip * (100 + priceBump) / 100
b := big.NewInt(100)
threshold := a.Div(a, b)
// Have to ensure that the new gas price is higher than the old gas
// price as well as checking the percentage threshold to ensure that
thresholdFeeCap := aFeeCap.Div(aFeeCap, b)
thresholdTip := aTip.Div(aTip, b)

// Have to ensure that either the new fee cap or tip is higher than the
// old ones as well as checking the percentage threshold to ensure that
// this is accurate for low (Wei-level) gas price replacements
if old.GasPriceCmp(tx) >= 0 || tx.GasPriceIntCmp(threshold) < 0 {
if (old.FeeCapCmp(tx) >= 0 || tx.FeeCapIntCmp(thresholdFeeCap) < 0) ||
(old.TipCmp(tx) >= 0 || tx.TipIntCmp(thresholdTip) < 0) {
return false, nil
}
}
Expand Down Expand Up @@ -413,8 +419,15 @@ func (h priceHeap) Len() int { return len(h) }
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }

func (h priceHeap) Less(i, j int) bool {
// Sort primarily by price, returning the cheaper one
switch h[i].GasPriceCmp(h[j]) {
// Sort primarily by fee cap, returning the cheaper one
switch h[i].FeeCapCmp(h[j]) {
case -1:
return true
case 1:
return false
}
// Sort secondarily by tip, returning the cheaper one
switch h[i].TipCmp(h[j]) {
case -1:
return true
case 1:
Expand Down Expand Up @@ -476,30 +489,6 @@ func (l *txPricedList) Removed(count int) {
l.Reheap()
}

// Cap finds all the transactions below the given price threshold, drops them
// from the priced list and returns them for further removal from the entire pool.
//
// Note: only remote transactions will be considered for eviction.
func (l *txPricedList) Cap(threshold *big.Int) types.Transactions {
drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
for len(*l.remotes) > 0 {
// Discard stale transactions if found during cleanup
cheapest := (*l.remotes)[0]
if l.all.GetRemote(cheapest.Hash()) == nil { // Removed or migrated
heap.Pop(l.remotes)
l.stales--
continue
}
// Stop the discards if we've reached the threshold
if cheapest.GasPriceIntCmp(threshold) >= 0 {
break
}
heap.Pop(l.remotes)
drop = append(drop, cheapest)
}
return drop
}

// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
// lowest priced (remote) transaction currently being tracked.
func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
Expand All @@ -520,7 +509,8 @@ func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
// If the remote transaction is even cheaper than the
// cheapest one tracked locally, reject it.
cheapest := []*types.Transaction(*l.remotes)[0]
return cheapest.GasPriceCmp(tx) >= 0
cmp := cheapest.FeeCapCmp(tx)
return cmp > 0 || (cmp == 0 && cheapest.TipCmp(tx) >= 0)
}

// Discard finds a number of most underpriced transactions, removes them from the
Expand Down
42 changes: 36 additions & 6 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ var (
// than some meaningful limit a user might use. This is not a consensus error
// making the transaction invalid, rather a DOS protection.
ErrOversizedData = errors.New("oversized data")

// ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a
// transaction with a tip higher than the total fee cap.
ErrTipAboveFeeCap = errors.New("tip higher than fee cap")
)

var (
Expand Down Expand Up @@ -230,6 +234,7 @@ type TxPool struct {

istanbul bool // Fork indicator whether we are in the istanbul stage.
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.

currentState *state.StateDB // Current state in the blockchain head
pendingNonces *txNoncer // Pending state tracking virtual nonces
Expand Down Expand Up @@ -426,10 +431,18 @@ func (pool *TxPool) SetGasPrice(price *big.Int) {
pool.mu.Lock()
defer pool.mu.Unlock()

old := pool.gasPrice
pool.gasPrice = price
for _, tx := range pool.priced.Cap(price) {
pool.removeTx(tx.Hash(), false)
// if the min miner fee increased, remove transactions below the new threshold
if price.Cmp(old) > 0 {
// pool.priced is sorted by FeeCap, so we have to iterate through pool.all instead
drop := pool.all.RemotesBelowTip(price)
for _, tx := range drop {
pool.removeTx(tx.Hash(), false)
}
pool.priced.Removed(len(drop))
}

log.Info("Transaction pool price threshold updated", "price", price)
}

Expand Down Expand Up @@ -540,13 +553,17 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
// Ensure feeCap is less than or equal to tip.
if pool.eip1559 && tx.FeeCapIntCmp(tx.Tip()) < 0 {
return ErrTipAboveFeeCap
}
// Make sure the transaction is signed properly.
from, err := types.Sender(pool.signer, tx)
if err != nil {
return ErrInvalidSender
}
// Drop non-local transactions under our own minimal accepted gas price
if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 {
// Drop non-local transactions under our own minimal accepted gas price or tip
if !local && tx.TipIntCmp(pool.gasPrice) < 0 {
return ErrUnderpriced
}
// Ensure the transaction adheres to nonce ordering
Expand Down Expand Up @@ -598,7 +615,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
if uint64(pool.all.Count()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue {
// If the new transaction is underpriced, don't accept it
if !isLocal && pool.priced.Underpriced(tx) {
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.FeeCap())
underpricedTxMeter.Mark(1)
return false, ErrUnderpriced
}
Expand All @@ -615,7 +632,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
}
// Kick out the underpriced remote transactions.
for _, tx := range drop {
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.FeeCap())
underpricedTxMeter.Mark(1)
pool.removeTx(tx.Hash(), false)
}
Expand Down Expand Up @@ -1205,6 +1222,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
pool.istanbul = pool.chainconfig.IsIstanbul(next)
pool.eip2718 = pool.chainconfig.IsBerlin(next)
pool.eip1559 = pool.chainconfig.IsLondon(next)
}

// promoteExecutables moves transactions that have become processable from the
Expand Down Expand Up @@ -1709,6 +1727,18 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int {
return migrated
}

// RemotesBelowTip finds all remote transactions below the given tip threshold.
func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
found := make(types.Transactions, 0, 128)
t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
if tx.TipIntCmp(threshold) < 0 {
found = append(found, tx)
}
return true
}, false, true) // Only iterate remotes
return found
}

// numSlots calculates the number of slots needed for a single transaction.
func numSlots(tx *types.Transaction) int {
return int((tx.Size() + txSlotSize - 1) / txSlotSize)
Expand Down
Loading