github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/eth/gasprice/gasprice_test.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package gasprice 18 19 import ( 20 "context" 21 "crypto/sha256" 22 "fmt" 23 "math" 24 "math/big" 25 "testing" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/consensus" 29 "github.com/ethereum/go-ethereum/consensus/beacon" 30 "github.com/ethereum/go-ethereum/consensus/ethash" 31 "github.com/ethereum/go-ethereum/core" 32 "github.com/ethereum/go-ethereum/core/state" 33 "github.com/ethereum/go-ethereum/core/types" 34 "github.com/ethereum/go-ethereum/core/vm" 35 "github.com/ethereum/go-ethereum/crypto" 36 "github.com/ethereum/go-ethereum/crypto/kzg4844" 37 "github.com/ethereum/go-ethereum/event" 38 "github.com/ethereum/go-ethereum/params" 39 "github.com/ethereum/go-ethereum/rpc" 40 "github.com/holiman/uint256" 41 ) 42 43 const testHead = 32 44 45 type testBackend struct { 46 chain *core.BlockChain 47 pending bool // pending block available 48 } 49 50 func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { 51 if number > testHead { 52 return nil, nil 53 } 54 if number == rpc.EarliestBlockNumber { 55 number = 0 56 } 57 if number == rpc.FinalizedBlockNumber { 58 return b.chain.CurrentFinalBlock(), nil 59 } 60 if number == rpc.SafeBlockNumber { 61 return b.chain.CurrentSafeBlock(), nil 62 } 63 if number == rpc.LatestBlockNumber { 64 number = testHead 65 } 66 if number == rpc.PendingBlockNumber { 67 if b.pending { 68 number = testHead + 1 69 } else { 70 return nil, nil 71 } 72 } 73 return b.chain.GetHeaderByNumber(uint64(number)), nil 74 } 75 76 func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) { 77 if number > testHead { 78 return nil, nil 79 } 80 if number == rpc.EarliestBlockNumber { 81 number = 0 82 } 83 if number == rpc.FinalizedBlockNumber { 84 number = rpc.BlockNumber(b.chain.CurrentFinalBlock().Number.Uint64()) 85 } 86 if number == rpc.SafeBlockNumber { 87 number = rpc.BlockNumber(b.chain.CurrentSafeBlock().Number.Uint64()) 88 } 89 if number == rpc.LatestBlockNumber { 90 number = testHead 91 } 92 if number == rpc.PendingBlockNumber { 93 if b.pending { 94 number = testHead + 1 95 } else { 96 return nil, nil 97 } 98 } 99 return b.chain.GetBlockByNumber(uint64(number)), nil 100 } 101 102 func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { 103 return b.chain.GetReceiptsByHash(hash), nil 104 } 105 106 func (b *testBackend) Pending() (*types.Block, types.Receipts, *state.StateDB) { 107 if b.pending { 108 block := b.chain.GetBlockByNumber(testHead + 1) 109 state, _ := b.chain.StateAt(block.Root()) 110 return block, b.chain.GetReceiptsByHash(block.Hash()), state 111 } 112 return nil, nil, nil 113 } 114 115 func (b *testBackend) ChainConfig() *params.ChainConfig { 116 return b.chain.Config() 117 } 118 119 func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { 120 return nil 121 } 122 123 func (b *testBackend) teardown() { 124 b.chain.Stop() 125 } 126 127 // newTestBackend creates a test backend. OBS: don't forget to invoke tearDown 128 // after use, otherwise the blockchain instance will mem-leak via goroutines. 129 func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pending bool) *testBackend { 130 if londonBlock != nil && cancunBlock != nil && londonBlock.Cmp(cancunBlock) == 1 { 131 panic("cannot define test backend with cancun before london") 132 } 133 var ( 134 key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 135 addr = crypto.PubkeyToAddress(key.PublicKey) 136 config = *params.TestChainConfig // needs copy because it is modified below 137 gspec = &core.Genesis{ 138 Config: &config, 139 Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, 140 } 141 signer = types.LatestSigner(gspec.Config) 142 143 // Compute empty blob hash. 144 emptyBlob = kzg4844.Blob{} 145 emptyBlobCommit, _ = kzg4844.BlobToCommitment(&emptyBlob) 146 emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) 147 ) 148 config.LondonBlock = londonBlock 149 config.ArrowGlacierBlock = londonBlock 150 config.GrayGlacierBlock = londonBlock 151 var engine consensus.Engine = beacon.New(ethash.NewFaker()) 152 td := params.GenesisDifficulty.Uint64() 153 154 if cancunBlock != nil { 155 ts := gspec.Timestamp + cancunBlock.Uint64()*10 // fixed 10 sec block time in blockgen 156 config.ShanghaiTime = &ts 157 config.CancunTime = &ts 158 signer = types.LatestSigner(gspec.Config) 159 } 160 161 // Generate testing blocks 162 db, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) { 163 b.SetCoinbase(common.Address{1}) 164 165 var txdata types.TxData 166 if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 { 167 txdata = &types.DynamicFeeTx{ 168 ChainID: gspec.Config.ChainID, 169 Nonce: b.TxNonce(addr), 170 To: &common.Address{}, 171 Gas: 30000, 172 GasFeeCap: big.NewInt(100 * params.GWei), 173 GasTipCap: big.NewInt(int64(i+1) * params.GWei), 174 Data: []byte{}, 175 } 176 } else { 177 txdata = &types.LegacyTx{ 178 Nonce: b.TxNonce(addr), 179 To: &common.Address{}, 180 Gas: 21000, 181 GasPrice: big.NewInt(int64(i+1) * params.GWei), 182 Value: big.NewInt(100), 183 Data: []byte{}, 184 } 185 } 186 b.AddTx(types.MustSignNewTx(key, signer, txdata)) 187 188 if cancunBlock != nil && b.Number().Cmp(cancunBlock) >= 0 { 189 b.SetPoS() 190 191 // put more blobs in each new block 192 for j := 0; j < i && j < 6; j++ { 193 blobTx := &types.BlobTx{ 194 ChainID: uint256.MustFromBig(gspec.Config.ChainID), 195 Nonce: b.TxNonce(addr), 196 To: common.Address{}, 197 Gas: 30000, 198 GasFeeCap: uint256.NewInt(100 * params.GWei), 199 GasTipCap: uint256.NewInt(uint64(i+1) * params.GWei), 200 Data: []byte{}, 201 BlobFeeCap: uint256.NewInt(1), 202 BlobHashes: []common.Hash{emptyBlobVHash}, 203 Value: uint256.NewInt(100), 204 Sidecar: nil, 205 } 206 b.AddTx(types.MustSignNewTx(key, signer, blobTx)) 207 } 208 } 209 td += b.Difficulty().Uint64() 210 }) 211 // Construct testing chain 212 gspec.Config.TerminalTotalDifficulty = new(big.Int).SetUint64(td) 213 chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil) 214 if err != nil { 215 t.Fatalf("Failed to create local chain, %v", err) 216 } 217 if i, err := chain.InsertChain(blocks); err != nil { 218 panic(fmt.Errorf("error inserting block %d: %w", i, err)) 219 } 220 chain.SetFinalized(chain.GetBlockByNumber(25).Header()) 221 chain.SetSafe(chain.GetBlockByNumber(25).Header()) 222 223 return &testBackend{chain: chain, pending: pending} 224 } 225 226 func (b *testBackend) CurrentHeader() *types.Header { 227 return b.chain.CurrentHeader() 228 } 229 230 func (b *testBackend) GetBlockByNumber(number uint64) *types.Block { 231 return b.chain.GetBlockByNumber(number) 232 } 233 234 func TestSuggestTipCap(t *testing.T) { 235 config := Config{ 236 Blocks: 3, 237 Percentile: 60, 238 Default: big.NewInt(params.GWei), 239 } 240 var cases = []struct { 241 fork *big.Int // London fork number 242 expect *big.Int // Expected gasprice suggestion 243 }{ 244 {nil, big.NewInt(params.GWei * int64(30))}, 245 {big.NewInt(0), big.NewInt(params.GWei * int64(30))}, // Fork point in genesis 246 {big.NewInt(1), big.NewInt(params.GWei * int64(30))}, // Fork point in first block 247 {big.NewInt(32), big.NewInt(params.GWei * int64(30))}, // Fork point in last block 248 {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future 249 } 250 for _, c := range cases { 251 backend := newTestBackend(t, c.fork, nil, false) 252 oracle := NewOracle(backend, config) 253 254 // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G 255 got, err := oracle.SuggestTipCap(context.Background()) 256 backend.teardown() 257 if err != nil { 258 t.Fatalf("Failed to retrieve recommended gas price: %v", err) 259 } 260 if got.Cmp(c.expect) != 0 { 261 t.Fatalf("Gas price mismatch, want %d, got %d", c.expect, got) 262 } 263 } 264 }