github.com/ethereum-optimism/optimism/l2geth@v0.0.0-20230612200230-50b04ade19e3/rollup/fees/bindings/gaspriceoracle_test.go (about) 1 package bindings 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/ecdsa" 7 "errors" 8 "math/big" 9 "testing" 10 11 "github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind" 12 "github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind/backends" 13 "github.com/ethereum-optimism/optimism/l2geth/common" 14 "github.com/ethereum-optimism/optimism/l2geth/core" 15 "github.com/ethereum-optimism/optimism/l2geth/core/rawdb" 16 "github.com/ethereum-optimism/optimism/l2geth/core/types" 17 "github.com/ethereum-optimism/optimism/l2geth/crypto" 18 "github.com/ethereum-optimism/optimism/l2geth/eth/gasprice" 19 "github.com/ethereum-optimism/optimism/l2geth/ethdb" 20 "github.com/ethereum-optimism/optimism/l2geth/rollup/fees" 21 ) 22 23 // Test that the fee calculation is the same in both go and solidity 24 func TestCalculateFee(t *testing.T) { 25 key, _ := crypto.GenerateKey() 26 sim, _ := newSimulatedBackend(key) 27 chain := sim.Blockchain() 28 29 opts, _ := NewKeyedTransactor(key) 30 addr, _, gpo, err := DeployGasPriceOracle(opts, sim, opts.From) 31 if err != nil { 32 t.Fatal(err) 33 } 34 sim.Commit() 35 callopts := bind.CallOpts{} 36 37 signer := types.NewEIP155Signer(big.NewInt(1337)) 38 gasOracle := gasprice.NewRollupOracle() 39 40 // Set the L1 base fee 41 if _, err := gpo.SetL1BaseFee(opts, big.NewInt(1)); err != nil { 42 t.Fatal("cannot set 1l base fee") 43 } 44 sim.Commit() 45 46 tests := map[string]struct { 47 tx *types.Transaction 48 }{ 49 "simple": { 50 types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), []byte{}), 51 }, 52 "high-nonce": { 53 types.NewTransaction(12345678, common.Address{}, big.NewInt(0), 0, big.NewInt(0), []byte{}), 54 }, 55 "full-tx": { 56 types.NewTransaction(20, common.HexToAddress("0x"), big.NewInt(1234), 215000, big.NewInt(769109341), common.FromHex(GasPriceOracleBin)), 57 }, 58 } 59 60 for name, tt := range tests { 61 t.Run(name, func(t *testing.T) { 62 tx := tt.tx 63 raw := new(bytes.Buffer) 64 if err := tx.EncodeRLP(raw); err != nil { 65 t.Fatal("cannot rlp encode tx") 66 } 67 68 l1BaseFee, err := gpo.L1BaseFee(&callopts) 69 if err != nil { 70 t.Fatal("cannot get l1 base fee") 71 } 72 overhead, err := gpo.Overhead(&callopts) 73 if err != nil { 74 t.Fatal("cannot get overhead") 75 } 76 scalar, err := gpo.Scalar(&callopts) 77 if err != nil { 78 t.Fatal("cannot get scalar") 79 } 80 decimals, err := gpo.Decimals(&callopts) 81 if err != nil { 82 t.Fatal("cannot get decimals") 83 } 84 l2GasPrice, err := gpo.GasPrice(&callopts) 85 if err != nil { 86 t.Fatal("cannot get l2 gas price") 87 } 88 89 gasOracle.SetL1GasPrice(l1BaseFee) 90 gasOracle.SetL2GasPrice(l2GasPrice) 91 gasOracle.SetOverhead(overhead) 92 gasOracle.SetScalar(scalar, decimals) 93 94 l1Fee, err := gpo.GetL1Fee(&callopts, raw.Bytes()) 95 if err != nil { 96 t.Fatal("cannot get l1 fee") 97 } 98 99 scaled := fees.ScaleDecimals(scalar, decimals) 100 expectL1Fee := fees.CalculateL1Fee(raw.Bytes(), overhead, l1BaseFee, scaled) 101 if expectL1Fee.Cmp(l1Fee) != 0 { 102 t.Fatal("solidity does not match go") 103 } 104 105 state, err := chain.State() 106 if err != nil { 107 t.Fatal("cannot get state") 108 } 109 110 // Ignore the error here because the tx isn't signed 111 msg, _ := tx.AsMessage(signer) 112 113 l1MsgFee, err := fees.CalculateL1MsgFee(msg, state, &addr) 114 if err != nil { 115 t.Fatal(err) 116 } 117 if l1MsgFee.Cmp(expectL1Fee) != 0 { 118 t.Fatal("l1 msg fee not computed correctly") 119 } 120 121 msgFee, err := fees.CalculateTotalMsgFee(msg, state, new(big.Int).SetUint64(msg.Gas()), &addr) 122 if err != nil { 123 t.Fatal("cannot calculate total msg fee") 124 } 125 txFee, err := fees.CalculateTotalFee(tx, gasOracle) 126 if err != nil { 127 t.Fatal("cannot calculate total tx fee") 128 } 129 if msgFee.Cmp(txFee) != 0 { 130 t.Fatal("msg fee and tx fee mismatch") 131 } 132 }) 133 } 134 } 135 136 func newSimulatedBackend(key *ecdsa.PrivateKey) (*backends.SimulatedBackend, ethdb.Database) { 137 var gasLimit uint64 = 9_000_000 138 auth, _ := NewKeyedTransactor(key) 139 genAlloc := make(core.GenesisAlloc) 140 genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)} 141 db := rawdb.NewMemoryDatabase() 142 sim := backends.NewSimulatedBackendWithDatabase(db, genAlloc, gasLimit) 143 return sim, db 144 } 145 146 // NewKeyedTransactor is a utility method to easily create a transaction signer 147 // from a single private key. This was copied and modified from upstream geth 148 func NewKeyedTransactor(key *ecdsa.PrivateKey) (*bind.TransactOpts, error) { 149 keyAddr := crypto.PubkeyToAddress(key.PublicKey) 150 return &bind.TransactOpts{ 151 From: keyAddr, 152 Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { 153 if address != keyAddr { 154 return nil, errors.New("unauthorized") 155 } 156 signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) 157 if err != nil { 158 return nil, err 159 } 160 return tx.WithSignature(signer, signature) 161 }, 162 Context: context.Background(), 163 }, nil 164 }