Skip to content

Commit a6c4627

Browse files
holimanadietrichslightclient
authored
EIP-1559: miner changes (#22896)
* core/types, miner: create TxWithMinerFee wrapper, add EIP-1559 support to TransactionsByMinerFeeAndNonce miner: set base fee when creating a new header, handle gas limit, log miner fees * all: rename to NewTransactionsByPriceAndNonce * core/types, miner: rename to NewTransactionsByPriceAndNonce + EffectiveTip miner: activate 1559 for testGenerateBlockAndImport tests * core,miner: revert naming to TransactionsByPriceAndTime * core/types/transaction: update effective tip calculation logic * miner: update aleut to london * core/types/transaction_test: use correct signer for 1559 txs + add back sender check * miner/worker: calculate gas target from gas limit * core, miner: fix block gas limits for 1559 Co-authored-by: Ansgar Dietrichs <[email protected]> Co-authored-by: [email protected] <[email protected]>
1 parent 16bc574 commit a6c4627

File tree

10 files changed

+206
-48
lines changed

10 files changed

+206
-48
lines changed

core/bench_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
112112
from := 0
113113
return func(i int, gen *BlockGen) {
114114
block := gen.PrevBlock(i - 1)
115-
gas := CalcGasLimit(block, block.GasLimit(), block.GasLimit())
115+
gas := block.GasLimit()
116116
for {
117117
gas -= params.TxGas
118118
if gas < params.TxGas {

core/block_validator.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
106106
// to keep the baseline gas above the provided floor, and increase it towards the
107107
// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
108108
// the gas allowance.
109-
func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
109+
func CalcGasLimit(parentGasUsed, parentGasLimit, gasFloor, gasCeil uint64) uint64 {
110110
// contrib = (parentGasUsed * 3 / 2) / 1024
111-
contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
111+
contrib := (parentGasUsed + parentGasUsed/2) / params.GasLimitBoundDivisor
112112

113113
// decay = parentGasLimit / 1024 -1
114-
decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1
114+
decay := parentGasLimit/params.GasLimitBoundDivisor - 1
115115

116116
/*
117117
strategy: gasLimit of block-to-mine is set based on parent's
@@ -120,21 +120,45 @@ func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
120120
at that usage) the amount increased/decreased depends on how far away
121121
from parentGasLimit * (2/3) parentGasUsed is.
122122
*/
123-
limit := parent.GasLimit() - decay + contrib
123+
limit := parentGasLimit - decay + contrib
124124
if limit < params.MinGasLimit {
125125
limit = params.MinGasLimit
126126
}
127127
// If we're outside our allowed gas range, we try to hone towards them
128128
if limit < gasFloor {
129-
limit = parent.GasLimit() + decay
129+
limit = parentGasLimit + decay
130130
if limit > gasFloor {
131131
limit = gasFloor
132132
}
133133
} else if limit > gasCeil {
134-
limit = parent.GasLimit() - decay
134+
limit = parentGasLimit - decay
135135
if limit < gasCeil {
136136
limit = gasCeil
137137
}
138138
}
139139
return limit
140140
}
141+
142+
// CalcGasLimit1559 calculates the next block gas limit under 1559 rules.
143+
func CalcGasLimit1559(parentGasLimit, desiredLimit uint64) uint64 {
144+
delta := parentGasLimit/params.GasLimitBoundDivisor - 1
145+
limit := parentGasLimit
146+
if desiredLimit < params.MinGasLimit {
147+
desiredLimit = params.MinGasLimit
148+
}
149+
// If we're outside our allowed gas range, we try to hone towards them
150+
if limit < desiredLimit {
151+
limit = parentGasLimit + delta
152+
if limit > desiredLimit {
153+
limit = desiredLimit
154+
}
155+
return limit
156+
}
157+
if limit > desiredLimit {
158+
limit = parentGasLimit - delta
159+
if limit < desiredLimit {
160+
limit = desiredLimit
161+
}
162+
}
163+
return limit
164+
}

core/block_validator_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,36 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
197197
t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads)
198198
}
199199
}
200+
201+
func TestCalcGasLimit1559(t *testing.T) {
202+
203+
for i, tc := range []struct {
204+
pGasLimit uint64
205+
max uint64
206+
min uint64
207+
}{
208+
{20000000, 20019530, 19980470},
209+
{40000000, 40039061, 39960939},
210+
} {
211+
// Increase
212+
if have, want := CalcGasLimit1559(tc.pGasLimit, 2*tc.pGasLimit), tc.max; have != want {
213+
t.Errorf("test %d: have %d want <%d", i, have, want)
214+
}
215+
// Decrease
216+
if have, want := CalcGasLimit1559(tc.pGasLimit, 0), tc.min; have != want {
217+
t.Errorf("test %d: have %d want >%d", i, have, want)
218+
}
219+
// Small decrease
220+
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit-1), tc.pGasLimit-1; have != want {
221+
t.Errorf("test %d: have %d want %d", i, have, want)
222+
}
223+
// Small increase
224+
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit+1), tc.pGasLimit+1; have != want {
225+
t.Errorf("test %d: have %d want %d", i, have, want)
226+
}
227+
// No change
228+
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit), tc.pGasLimit; have != want {
229+
t.Errorf("test %d: have %d want %d", i, have, want)
230+
}
231+
}
232+
}

core/chain_makers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
263263
Difficulty: parent.Difficulty(),
264264
UncleHash: parent.UncleHash(),
265265
}),
266-
GasLimit: CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()),
266+
GasLimit: parent.GasLimit(),
267267
Number: new(big.Int).Add(parent.Number(), common.Big1),
268268
Time: time,
269269
}

core/state_processor_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
223223
Difficulty: parent.Difficulty(),
224224
UncleHash: parent.UncleHash(),
225225
}),
226-
GasLimit: CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()),
226+
GasLimit: parent.GasLimit(),
227227
Number: new(big.Int).Add(parent.Number(), common.Big1),
228228
Time: parent.Time() + 10,
229229
UncleHash: types.EmptyUncleHash,

core/types/transaction.go

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var (
3636
ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
3737
ErrInvalidTxType = errors.New("transaction type not valid in this context")
3838
ErrTxTypeNotSupported = errors.New("transaction type not supported")
39+
ErrFeeCapTooLow = errors.New("fee cap less than base fee")
3940
errEmptyTypedTx = errors.New("empty typed transaction bytes")
4041
)
4142

@@ -299,6 +300,19 @@ func (tx *Transaction) Cost() *big.Int {
299300
return total
300301
}
301302

303+
// EffectiveTip returns the effective miner tip for the given base fee.
304+
// Returns error in case of a negative effective miner tip.
305+
func (tx *Transaction) EffectiveTip(baseFee *big.Int) (*big.Int, error) {
306+
if baseFee == nil {
307+
return tx.Tip(), nil
308+
}
309+
feeCap := tx.FeeCap()
310+
if feeCap.Cmp(baseFee) == -1 {
311+
return nil, ErrFeeCapTooLow
312+
}
313+
return math.BigMin(tx.Tip(), feeCap.Sub(feeCap, baseFee)), nil
314+
}
315+
302316
// RawSignatureValues returns the V, R, S signature values of the transaction.
303317
// The return values should not be modified by the caller.
304318
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
@@ -400,24 +414,44 @@ func (s TxByNonce) Len() int { return len(s) }
400414
func (s TxByNonce) Less(i, j int) bool { return s[i].Nonce() < s[j].Nonce() }
401415
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
402416

417+
// TxWithMinerFee wraps a transaction with its gas price or effective miner tip
418+
type TxWithMinerFee struct {
419+
tx *Transaction
420+
minerFee *big.Int
421+
}
422+
423+
// NewTxWithMinerFee creates a wrapped transaction, calculating the effective
424+
// miner tip if a base fee is provided.
425+
// Returns error in case of a negative effective miner tip.
426+
func NewTxWithMinerFee(tx *Transaction, baseFee *big.Int) (*TxWithMinerFee, error) {
427+
minerFee, err := tx.EffectiveTip(baseFee)
428+
if err != nil {
429+
return nil, err
430+
}
431+
return &TxWithMinerFee{
432+
tx: tx,
433+
minerFee: minerFee,
434+
}, nil
435+
}
436+
403437
// TxByPriceAndTime implements both the sort and the heap interface, making it useful
404438
// for all at once sorting as well as individually adding and removing elements.
405-
type TxByPriceAndTime Transactions
439+
type TxByPriceAndTime []*TxWithMinerFee
406440

407441
func (s TxByPriceAndTime) Len() int { return len(s) }
408442
func (s TxByPriceAndTime) Less(i, j int) bool {
409443
// If the prices are equal, use the time the transaction was first seen for
410444
// deterministic sorting
411-
cmp := s[i].GasPrice().Cmp(s[j].GasPrice())
445+
cmp := s[i].minerFee.Cmp(s[j].minerFee)
412446
if cmp == 0 {
413-
return s[i].time.Before(s[j].time)
447+
return s[i].tx.time.Before(s[j].tx.time)
414448
}
415449
return cmp > 0
416450
}
417451
func (s TxByPriceAndTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
418452

419453
func (s *TxByPriceAndTime) Push(x interface{}) {
420-
*s = append(*s, x.(*Transaction))
454+
*s = append(*s, x.(*TxWithMinerFee))
421455
}
422456

423457
func (s *TxByPriceAndTime) Pop() interface{} {
@@ -432,35 +466,39 @@ func (s *TxByPriceAndTime) Pop() interface{} {
432466
// transactions in a profit-maximizing sorted order, while supporting removing
433467
// entire batches of transactions for non-executable accounts.
434468
type TransactionsByPriceAndNonce struct {
435-
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
436-
heads TxByPriceAndTime // Next transaction for each unique account (price heap)
437-
signer Signer // Signer for the set of transactions
469+
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
470+
heads TxByPriceAndTime // Next transaction for each unique account (price heap)
471+
signer Signer // Signer for the set of transactions
472+
baseFee *big.Int // Current base fee
438473
}
439474

440475
// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
441476
// price sorted transactions in a nonce-honouring way.
442477
//
443478
// Note, the input map is reowned so the caller should not interact any more with
444479
// if after providing it to the constructor.
445-
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
480+
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFee *big.Int) *TransactionsByPriceAndNonce {
446481
// Initialize a price and received time based heap with the head transactions
447482
heads := make(TxByPriceAndTime, 0, len(txs))
448483
for from, accTxs := range txs {
449-
// Ensure the sender address is from the signer
450-
if acc, _ := Sender(signer, accTxs[0]); acc != from {
484+
acc, _ := Sender(signer, accTxs[0])
485+
wrapped, err := NewTxWithMinerFee(accTxs[0], baseFee)
486+
// Remove transaction if sender doesn't match from, or if wrapping fails.
487+
if acc != from || err != nil {
451488
delete(txs, from)
452489
continue
453490
}
454-
heads = append(heads, accTxs[0])
491+
heads = append(heads, wrapped)
455492
txs[from] = accTxs[1:]
456493
}
457494
heap.Init(&heads)
458495

459496
// Assemble and return the transaction set
460497
return &TransactionsByPriceAndNonce{
461-
txs: txs,
462-
heads: heads,
463-
signer: signer,
498+
txs: txs,
499+
heads: heads,
500+
signer: signer,
501+
baseFee: baseFee,
464502
}
465503
}
466504

@@ -469,18 +507,20 @@ func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
469507
if len(t.heads) == 0 {
470508
return nil
471509
}
472-
return t.heads[0]
510+
return t.heads[0].tx
473511
}
474512

475513
// Shift replaces the current best head with the next one from the same account.
476514
func (t *TransactionsByPriceAndNonce) Shift() {
477-
acc, _ := Sender(t.signer, t.heads[0])
515+
acc, _ := Sender(t.signer, t.heads[0].tx)
478516
if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
479-
t.heads[0], t.txs[acc] = txs[0], txs[1:]
480-
heap.Fix(&t.heads, 0)
481-
} else {
482-
heap.Pop(&t.heads)
517+
if wrapped, err := NewTxWithMinerFee(txs[0], t.baseFee); err == nil {
518+
t.heads[0], t.txs[acc] = wrapped, txs[1:]
519+
heap.Fix(&t.heads, 0)
520+
return
521+
}
483522
}
523+
heap.Pop(&t.heads)
484524
}
485525

486526
// Pop removes the best transaction, *not* replacing it with the next one from

core/types/transaction_test.go

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"encoding/json"
2323
"fmt"
2424
"math/big"
25+
"math/rand"
2526
"reflect"
2627
"testing"
2728
"time"
@@ -258,36 +259,77 @@ func TestRecipientNormal(t *testing.T) {
258259
}
259260
}
260261

262+
func TestTransactionPriceNonceSortLegacy(t *testing.T) {
263+
testTransactionPriceNonceSort(t, nil)
264+
}
265+
266+
func TestTransactionPriceNonceSort1559(t *testing.T) {
267+
testTransactionPriceNonceSort(t, big.NewInt(0))
268+
testTransactionPriceNonceSort(t, big.NewInt(5))
269+
testTransactionPriceNonceSort(t, big.NewInt(50))
270+
}
271+
261272
// Tests that transactions can be correctly sorted according to their price in
262273
// decreasing order, but at the same time with increasing nonces when issued by
263274
// the same account.
264-
func TestTransactionPriceNonceSort(t *testing.T) {
275+
func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
265276
// Generate a batch of accounts to start with
266277
keys := make([]*ecdsa.PrivateKey, 25)
267278
for i := 0; i < len(keys); i++ {
268279
keys[i], _ = crypto.GenerateKey()
269280
}
270-
signer := HomesteadSigner{}
281+
signer := LatestSignerForChainID(common.Big1)
271282

272283
// Generate a batch of transactions with overlapping values, but shifted nonces
273284
groups := map[common.Address]Transactions{}
285+
expectedCount := 0
274286
for start, key := range keys {
275287
addr := crypto.PubkeyToAddress(key.PublicKey)
288+
count := 25
276289
for i := 0; i < 25; i++ {
277-
tx, _ := SignTx(NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), 100, big.NewInt(int64(start+i)), nil), signer, key)
290+
var tx *Transaction
291+
feeCap := rand.Intn(50)
292+
if baseFee == nil {
293+
tx = NewTx(&LegacyTx{
294+
Nonce: uint64(start + i),
295+
To: &common.Address{},
296+
Value: big.NewInt(100),
297+
Gas: 100,
298+
GasPrice: big.NewInt(int64(feeCap)),
299+
Data: nil,
300+
})
301+
} else {
302+
tx = NewTx(&DynamicFeeTx{
303+
Nonce: uint64(start + i),
304+
To: &common.Address{},
305+
Value: big.NewInt(100),
306+
Gas: 100,
307+
FeeCap: big.NewInt(int64(feeCap)),
308+
Tip: big.NewInt(int64(rand.Intn(feeCap + 1))),
309+
Data: nil,
310+
})
311+
if count == 25 && int64(feeCap) < baseFee.Int64() {
312+
count = i
313+
}
314+
}
315+
tx, err := SignTx(tx, signer, key)
316+
if err != nil {
317+
t.Fatalf("failed to sign tx: %s", err)
318+
}
278319
groups[addr] = append(groups[addr], tx)
279320
}
321+
expectedCount += count
280322
}
281323
// Sort the transactions and cross check the nonce ordering
282-
txset := NewTransactionsByPriceAndNonce(signer, groups)
324+
txset := NewTransactionsByPriceAndNonce(signer, groups, baseFee)
283325

284326
txs := Transactions{}
285327
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
286328
txs = append(txs, tx)
287329
txset.Shift()
288330
}
289-
if len(txs) != 25*25 {
290-
t.Errorf("expected %d transactions, found %d", 25*25, len(txs))
331+
if len(txs) != expectedCount {
332+
t.Errorf("expected %d transactions, found %d", expectedCount, len(txs))
291333
}
292334
for i, txi := range txs {
293335
fromi, _ := Sender(signer, txi)
@@ -303,7 +345,12 @@ func TestTransactionPriceNonceSort(t *testing.T) {
303345
if i+1 < len(txs) {
304346
next := txs[i+1]
305347
fromNext, _ := Sender(signer, next)
306-
if fromi != fromNext && txi.GasPrice().Cmp(next.GasPrice()) < 0 {
348+
tip, err := txi.EffectiveTip(baseFee)
349+
nextTip, nextErr := next.EffectiveTip(baseFee)
350+
if err != nil || nextErr != nil {
351+
t.Errorf("error calculating effective tip")
352+
}
353+
if fromi != fromNext && tip.Cmp(nextTip) < 0 {
307354
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
308355
}
309356
}
@@ -331,7 +378,7 @@ func TestTransactionTimeSort(t *testing.T) {
331378
groups[addr] = append(groups[addr], tx)
332379
}
333380
// Sort the transactions and cross check the nonce ordering
334-
txset := NewTransactionsByPriceAndNonce(signer, groups)
381+
txset := NewTransactionsByPriceAndNonce(signer, groups, nil)
335382

336383
txs := Transactions{}
337384
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {

0 commit comments

Comments
 (0)