Commit 65289e63 authored by Mark Tyneway's avatar Mark Tyneway Committed by Kelvin Fichter

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.
parent 09eb322e
---
'@eth-optimism/l2geth': minor
---
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
......@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rollup/fees"
"github.com/ethereum/go-ethereum/rollup/rcfg"
)
......@@ -107,11 +108,22 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, config, cfg)
// UsingOVM
// Compute the fee related information that is to be included
// on the receipt. This must happen before the state transition
// to ensure that the correct information is used.
l1Fee, l1GasPrice, l1GasUsed, scalar, err := fees.DeriveL1GasInfo(msg, statedb)
if err != nil {
return nil, err
}
// Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
return nil, err
}
// Update the state with pending changes
var root []byte
if config.IsByzantium(header.Number) {
......@@ -124,6 +136,10 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing whether the root touch-delete accounts.
receipt := types.NewReceipt(root, failed, *usedGas)
receipt.L1GasPrice = l1GasPrice
receipt.L1GasUsed = l1GasUsed
receipt.L1Fee = l1Fee
receipt.FeeScalar = scalar
receipt.TxHash = tx.Hash()
receipt.GasUsed = gas
// if the transaction created a contract, store the creation address in the receipt.
......
......@@ -27,6 +27,10 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
BlockHash common.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex hexutil.Uint `json:"transactionIndex"`
L1GasPrice *big.Int `json:"l1GasPrice" gencodec:"required"`
L1GasUsed *big.Int `json:"l1GasUsed" gencodec:"required"`
L1Fee *big.Int `json:"l1Fee" gencodec:"required"`
FeeScalar *big.Float `json:"l1FeeScalar" gencodec:"required"`
}
var enc Receipt
enc.PostState = r.PostState
......@@ -40,6 +44,10 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
enc.BlockHash = r.BlockHash
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
enc.L1GasPrice = r.L1GasPrice
enc.L1GasUsed = r.L1GasUsed
enc.L1Fee = r.L1Fee
enc.FeeScalar = r.FeeScalar
return json.Marshal(&enc)
}
......@@ -57,6 +65,10 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
BlockHash *common.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
L1GasPrice *big.Int `json:"l1GasPrice" gencodec:"required"`
L1GasUsed *big.Int `json:"l1GasUsed" gencodec:"required"`
L1Fee *big.Int `json:"l1Fee" gencodec:"required"`
FeeScalar *big.Float `json:"l1FeeScalar" gencodec:"required"`
}
var dec Receipt
if err := json.Unmarshal(input, &dec); err != nil {
......@@ -100,5 +112,21 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
if dec.TransactionIndex != nil {
r.TransactionIndex = uint(*dec.TransactionIndex)
}
if dec.L1GasPrice == nil {
return errors.New("missing required field 'l1GasPrice' for Receipt")
}
r.L1GasPrice = dec.L1GasPrice
if dec.L1GasUsed == nil {
return errors.New("missing required field 'l1GasUsed' for Receipt")
}
r.L1GasUsed = dec.L1GasUsed
if dec.L1Fee == nil {
return errors.New("missing required field 'l1Fee' for Receipt")
}
r.L1Fee = dec.L1Fee
if dec.FeeScalar == nil {
return errors.New("missing required field 'l1FeeScalar' for Receipt")
}
r.FeeScalar = dec.FeeScalar
return nil
}
......@@ -66,6 +66,12 @@ type Receipt struct {
BlockHash common.Hash `json:"blockHash,omitempty"`
BlockNumber *big.Int `json:"blockNumber,omitempty"`
TransactionIndex uint `json:"transactionIndex"`
// UsingOVM
L1GasPrice *big.Int `json:"l1GasPrice" gencodec:"required"`
L1GasUsed *big.Int `json:"l1GasUsed" gencodec:"required"`
L1Fee *big.Int `json:"l1Fee" gencodec:"required"`
FeeScalar *big.Float `json:"l1FeeScalar" gencodec:"required"`
}
type receiptMarshaling struct {
......@@ -90,6 +96,11 @@ type storedReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Logs []*LogForStorage
// UsingOVM
L1GasUsed *big.Int
L1GasPrice *big.Int
L1Fee *big.Int
FeeScalar string
}
// 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 {
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
CumulativeGasUsed: r.CumulativeGasUsed,
Logs: make([]*LogForStorage, len(r.Logs)),
L1GasUsed: r.L1GasUsed,
L1GasPrice: r.L1GasPrice,
L1Fee: r.L1Fee,
FeeScalar: r.FeeScalar.String(),
}
for i, log := range r.Logs {
enc.Logs[i] = (*LogForStorage)(log)
......@@ -233,6 +248,16 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
}
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
// UsingOVM
scalar, ok := new(big.Float).SetString(stored.FeeScalar)
if !ok {
return errors.New("cannot parse fee scalar")
}
r.L1GasUsed = stored.L1GasUsed
r.L1GasPrice = stored.L1GasPrice
r.L1Fee = stored.L1Fee
r.FeeScalar = scalar
return nil
}
......
......@@ -1438,6 +1438,11 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
"contractAddress": nil,
"logs": receipt.Logs,
"logsBloom": receipt.Bloom,
// UsingOVM
"l1GasPrice": (*hexutil.Big)(receipt.L1GasPrice),
"l1GasUsed": (*hexutil.Big)(receipt.L1GasUsed),
"l1Fee": (*hexutil.Big)(receipt.L1Fee),
"l1FeeScalar": receipt.FeeScalar.String(),
}
// Assign receipt status or post state.
......
......@@ -9,6 +9,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rollup/rcfg"
......@@ -153,6 +154,22 @@ func CalculateL1GasUsed(data []byte, overhead *big.Int) *big.Int {
return new(big.Int).Add(l1Gas, overhead)
}
// DeriveL1GasInfo reads L1 gas related information to be included
// on the receipt
func DeriveL1GasInfo(msg Message, state StateDB) (*big.Int, *big.Int, *big.Int, *big.Float, error) {
tx := asTransaction(msg)
raw, err := rlpEncode(tx)
fmt.Println(hexutil.Encode(raw))
if err != nil {
return nil, nil, nil, nil, err
}
l1GasPrice, overhead, scalar := readGPOStorageSlots(rcfg.L2GasPriceOracleAddress, state)
l1GasUsed := CalculateL1GasUsed(raw, overhead)
l1Fee := CalculateL1Fee(raw, overhead, l1GasPrice, scalar)
return l1Fee, l1GasPrice, l1GasUsed, scalar, nil
}
func readGPOStorageSlots(addr common.Address, state StateDB) (*big.Int, *big.Int, *big.Float) {
l1GasPrice := state.GetState(addr, rcfg.L1GasPriceSlot)
overhead := state.GetState(addr, rcfg.OverheadSlot)
......@@ -183,7 +200,7 @@ func rlpEncode(tx *types.Transaction) ([]byte, error) {
r, v, s := tx.RawSignatureValues()
if r.Cmp(common.Big0) != 0 || v.Cmp(common.Big0) != 0 || s.Cmp(common.Big0) != 0 {
return []byte{}, errTransactionSigned
return nil, errTransactionSigned
}
// Slice off the 0 bytes representing the signature
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment