Skip to content

Commit 65289e6

Browse files
tynessmartcontracts
authored andcommitted
l2geth: add L1 gas fields to transaction receipt
The response of the RPC endpoint `eth_getTransactionReceipt` will now return 4 new fields. - `l1GasPrice` - `l1GasUsed` - `l1Fee` - `l1FeeScalar` These fields are added to the database as part of the receipt itself. This means that it is a consensus change as the serialization of the receipt has been updated. This impacts the blockhash because the block header commits to a merkle root of all of the receipts in the block. Each of the new fields on the receipt exist in the state but would require an archive node to query for as the values can change over time.
1 parent 09eb322 commit 65289e6

6 files changed

Lines changed: 97 additions & 1 deletion

File tree

.changeset/lazy-dingos-perform.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@eth-optimism/l2geth': minor
3+
---
4+
5+
Add optimistic ethereum specific fields to the receipt. These fields are related to the L1 portion of the fee. Note that this is a consensus change as it will impact the blockhash through the receipts root

l2geth/core/state_processor.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/ethereum/go-ethereum/core/vm"
2626
"github.com/ethereum/go-ethereum/crypto"
2727
"github.com/ethereum/go-ethereum/params"
28+
"github.com/ethereum/go-ethereum/rollup/fees"
2829
"github.com/ethereum/go-ethereum/rollup/rcfg"
2930
)
3031

@@ -107,11 +108,22 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
107108
// Create a new environment which holds all relevant information
108109
// about the transaction and calling mechanisms.
109110
vmenv := vm.NewEVM(context, statedb, config, cfg)
111+
112+
// UsingOVM
113+
// Compute the fee related information that is to be included
114+
// on the receipt. This must happen before the state transition
115+
// to ensure that the correct information is used.
116+
l1Fee, l1GasPrice, l1GasUsed, scalar, err := fees.DeriveL1GasInfo(msg, statedb)
117+
if err != nil {
118+
return nil, err
119+
}
120+
110121
// Apply the transaction to the current state (included in the env)
111122
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
112123
if err != nil {
113124
return nil, err
114125
}
126+
115127
// Update the state with pending changes
116128
var root []byte
117129
if config.IsByzantium(header.Number) {
@@ -124,6 +136,10 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
124136
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
125137
// based on the eip phase, we're passing whether the root touch-delete accounts.
126138
receipt := types.NewReceipt(root, failed, *usedGas)
139+
receipt.L1GasPrice = l1GasPrice
140+
receipt.L1GasUsed = l1GasUsed
141+
receipt.L1Fee = l1Fee
142+
receipt.FeeScalar = scalar
127143
receipt.TxHash = tx.Hash()
128144
receipt.GasUsed = gas
129145
// if the transaction created a contract, store the creation address in the receipt.

l2geth/core/types/gen_receipt_json.go

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

l2geth/core/types/receipt.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ type Receipt struct {
6666
BlockHash common.Hash `json:"blockHash,omitempty"`
6767
BlockNumber *big.Int `json:"blockNumber,omitempty"`
6868
TransactionIndex uint `json:"transactionIndex"`
69+
70+
// UsingOVM
71+
L1GasPrice *big.Int `json:"l1GasPrice" gencodec:"required"`
72+
L1GasUsed *big.Int `json:"l1GasUsed" gencodec:"required"`
73+
L1Fee *big.Int `json:"l1Fee" gencodec:"required"`
74+
FeeScalar *big.Float `json:"l1FeeScalar" gencodec:"required"`
6975
}
7076

7177
type receiptMarshaling struct {
@@ -90,6 +96,11 @@ type storedReceiptRLP struct {
9096
PostStateOrStatus []byte
9197
CumulativeGasUsed uint64
9298
Logs []*LogForStorage
99+
// UsingOVM
100+
L1GasUsed *big.Int
101+
L1GasPrice *big.Int
102+
L1Fee *big.Int
103+
FeeScalar string
93104
}
94105

95106
// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
@@ -191,6 +202,10 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
191202
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
192203
CumulativeGasUsed: r.CumulativeGasUsed,
193204
Logs: make([]*LogForStorage, len(r.Logs)),
205+
L1GasUsed: r.L1GasUsed,
206+
L1GasPrice: r.L1GasPrice,
207+
L1Fee: r.L1Fee,
208+
FeeScalar: r.FeeScalar.String(),
194209
}
195210
for i, log := range r.Logs {
196211
enc.Logs[i] = (*LogForStorage)(log)
@@ -233,6 +248,16 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
233248
}
234249
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
235250

251+
// UsingOVM
252+
scalar, ok := new(big.Float).SetString(stored.FeeScalar)
253+
if !ok {
254+
return errors.New("cannot parse fee scalar")
255+
}
256+
r.L1GasUsed = stored.L1GasUsed
257+
r.L1GasPrice = stored.L1GasPrice
258+
r.L1Fee = stored.L1Fee
259+
r.FeeScalar = scalar
260+
236261
return nil
237262
}
238263

l2geth/internal/ethapi/api.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,11 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
14381438
"contractAddress": nil,
14391439
"logs": receipt.Logs,
14401440
"logsBloom": receipt.Bloom,
1441+
// UsingOVM
1442+
"l1GasPrice": (*hexutil.Big)(receipt.L1GasPrice),
1443+
"l1GasUsed": (*hexutil.Big)(receipt.L1GasUsed),
1444+
"l1Fee": (*hexutil.Big)(receipt.L1Fee),
1445+
"l1FeeScalar": receipt.FeeScalar.String(),
14411446
}
14421447

14431448
// Assign receipt status or post state.

l2geth/rollup/fees/rollup_fee.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"math/big"
1010

1111
"github.com/ethereum/go-ethereum/common"
12+
"github.com/ethereum/go-ethereum/common/hexutil"
1213
"github.com/ethereum/go-ethereum/core/types"
1314
"github.com/ethereum/go-ethereum/params"
1415
"github.com/ethereum/go-ethereum/rollup/rcfg"
@@ -153,6 +154,22 @@ func CalculateL1GasUsed(data []byte, overhead *big.Int) *big.Int {
153154
return new(big.Int).Add(l1Gas, overhead)
154155
}
155156

157+
// DeriveL1GasInfo reads L1 gas related information to be included
158+
// on the receipt
159+
func DeriveL1GasInfo(msg Message, state StateDB) (*big.Int, *big.Int, *big.Int, *big.Float, error) {
160+
tx := asTransaction(msg)
161+
raw, err := rlpEncode(tx)
162+
fmt.Println(hexutil.Encode(raw))
163+
if err != nil {
164+
return nil, nil, nil, nil, err
165+
}
166+
167+
l1GasPrice, overhead, scalar := readGPOStorageSlots(rcfg.L2GasPriceOracleAddress, state)
168+
l1GasUsed := CalculateL1GasUsed(raw, overhead)
169+
l1Fee := CalculateL1Fee(raw, overhead, l1GasPrice, scalar)
170+
return l1Fee, l1GasPrice, l1GasUsed, scalar, nil
171+
}
172+
156173
func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int, *big.Float) {
157174
l1GasPrice := state.GetState(addr, rcfg.L1GasPriceSlot)
158175
overhead := state.GetState(addr, rcfg.OverheadSlot)
@@ -183,7 +200,7 @@ func rlpEncode(tx *types.Transaction) ([]byte, error) {
183200

184201
r, v, s := tx.RawSignatureValues()
185202
if r.Cmp(common.Big0) != 0 || v.Cmp(common.Big0) != 0 || s.Cmp(common.Big0) != 0 {
186-
return []byte{}, errTransactionSigned
203+
return nil, errTransactionSigned
187204
}
188205

189206
// Slice off the 0 bytes representing the signature

0 commit comments

Comments
 (0)