Skip to content

Commit

Permalink
Add L1 Data Fee Deduction (#270)
Browse files Browse the repository at this point in the history
Add gas consumption for L1 data fees as well to fulfill the OP Stack
Spec and compensate the sequencer for posting data on L1. To do this, a
new decorator to the default AnteHandler to calculate the L1 data fee
is added.
  • Loading branch information
natebeauregard authored Oct 30, 2024
1 parent b0850ef commit 7956099
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 6 deletions.
Binary file modified cmd/monogen/testapp.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion cmd/monogen/testapp/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func New(
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
SignModeHandler: app.txConfig.SignModeHandler(),
})
}, app.RollupKeeper)
if err != nil {
return nil, fmt.Errorf("new ante handler: %v", err)
}
Expand Down
15 changes: 14 additions & 1 deletion x/rollup/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"fmt"

"cosmossdk.io/core/store"
"github.com/cosmos/cosmos-sdk/codec"
Expand Down Expand Up @@ -31,7 +32,7 @@ func NewKeeper(
}
}

// Helper. Prepares a `message` event with the module name and emits it
// EmitEvents prepares a `message` event with the module name and emits it
// along with the provided events.
func (k *Keeper) EmitEvents(goCtx context.Context, events sdk.Events) {
ctx := sdk.UnwrapSDKContext(goCtx)
Expand All @@ -44,3 +45,15 @@ func (k *Keeper) EmitEvents(goCtx context.Context, events sdk.Events) {

ctx.EventManager().EmitEvents(events)
}

func (k *Keeper) GetL1BlockInfo(ctx sdk.Context) (*types.L1BlockInfo, error) { //nolint:gocritic // hugeParam
l1BlockInfoBz, err := k.storeService.OpenKVStore(ctx).Get([]byte(types.KeyL1BlockInfo))
if err != nil {
return nil, fmt.Errorf("get l1 block info: %w", err)
}
var l1BlockInfo types.L1BlockInfo
if err = l1BlockInfo.Unmarshal(l1BlockInfoBz); err != nil {
return nil, fmt.Errorf("unmarshal l1 block info: %w", err)
}
return &l1BlockInfo, nil
}
19 changes: 15 additions & 4 deletions x/rollup/tx/helpers/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,27 @@ import (

sdktypes "github.com/cosmos/cosmos-sdk/types"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
rollupkeeper "github.com/polymerdao/monomer/x/rollup/keeper"
rolluptx "github.com/polymerdao/monomer/x/rollup/tx"
)

type AnteHandler struct {
authAnte sdktypes.AnteHandler
authAnteHandler sdktypes.AnteHandler
rollupKeeper *rollupkeeper.Keeper
}

func NewAnteHandler(options authante.HandlerOptions) (*AnteHandler, error) { //nolint:gocritic // hugeParam
func NewAnteHandler(
options authante.HandlerOptions, //nolint:gocritic // hugeParam
rollupKeeper *rollupkeeper.Keeper,
) (*AnteHandler, error) {
authAnteHandler, err := authante.NewAnteHandler(options)
if err != nil {
return nil, fmt.Errorf("new auth ante handler: %v", err)
}

return &AnteHandler{
authAnte: authAnteHandler,
authAnteHandler: authAnteHandler,
rollupKeeper: rollupKeeper,
}, nil
}

Expand All @@ -35,10 +42,14 @@ func (a *AnteHandler) AnteHandle(
}
return newCtx, err
default: // Unfortunately, the Cosmos SDK does not export its default tx type.
newCtx, err := a.authAnte(ctx, tx, simulate)
newCtx, err := a.authAnteHandler(ctx, tx, simulate)
if err != nil {
return newCtx, fmt.Errorf("auth ante handle: %v", err)
}
newCtx, err = rolluptx.L1DataAnteHandler(newCtx, tx, a.rollupKeeper)
if err != nil {
return newCtx, fmt.Errorf("l1 data ante handle: %v", err)
}
return newCtx, nil
}
}
83 changes: 83 additions & 0 deletions x/rollup/tx/l1_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package tx

import (
"fmt"
"math/big"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/ethereum/go-ethereum/rlp"
rollupkeeper "github.com/polymerdao/monomer/x/rollup/keeper"
)

// L1DataAnteHandler will consume gas to compensate the sequencer for posting
// the transaction to Ethereum. The gas cost is calculated based on the Ecotone
// upgrade and the sequencer is expected to post the transaction using blobs.
func L1DataAnteHandler(ctx sdk.Context, tx sdk.Tx, rollupKeeper *rollupkeeper.Keeper) (sdk.Context, error) { //nolint:gocritic // hugeparam
if rollupKeeper == nil {
return ctx, errorsmod.Wrap(sdkerrors.ErrLogic, "rollup keeper is required for l1 data ante handler")
}

l1BlockInfo, err := rollupKeeper.GetL1BlockInfo(ctx)
if err != nil {
return ctx, fmt.Errorf("get l1 block info: %w", err)
}

baseFee := new(big.Int).SetBytes(l1BlockInfo.BaseFee)
baseFeeScalar := big.NewInt(int64(l1BlockInfo.BaseFeeScalar))
blobBaseFee := new(big.Int).SetBytes(l1BlockInfo.BlobBaseFee)
blobBaseFeeScalar := big.NewInt(int64(l1BlockInfo.BlobBaseFeeScalar))

l1GasUsed, err := getL1GasUsed(tx)
if err != nil {
return ctx, fmt.Errorf("get l1 gas used: %w", err)
}

const baseFeeMultiplier = 16
const divisor = 16e6

// scaledBaseFee calculation: scaledBaseFee = l1BaseFee * l1BaseFeeScalar * 16
scaledBaseFee := new(big.Int).Mul(new(big.Int).Mul(baseFee, baseFeeScalar), big.NewInt(baseFeeMultiplier))

// scaledBlobBaseFee calculation: scaledBlobBaseFee = l1BlobBaseFee * l1BlobBaseFeeScalar
scaledBlobBaseFee := new(big.Int).Mul(blobBaseFee, blobBaseFeeScalar)

// l1DataCost calculation: l1DataCost = [l1GasUsed * (scaledBaseFee + scaledBlobBaseFee)] / 16e6
l1DataCost := new(big.Int).Div(new(big.Int).Mul(l1GasUsed, new(big.Int).Add(scaledBaseFee, scaledBlobBaseFee)), big.NewInt(divisor))

if !l1DataCost.IsUint64() {
return ctx, fmt.Errorf("l1 data cost overflow: %s", l1DataCost)
}

ctx.GasMeter().ConsumeGas(l1DataCost.Uint64(), "l1 data")

return ctx, nil
}

// getL1GasUsed calculates the compressed size of a transaction when encoded with RLP. This is used to estimate
// the gas cost of a transaction being posted to the L1 chain by the sequencer.
func getL1GasUsed(tx sdk.Tx) (*big.Int, error) {
txBz, err := rlp.EncodeToBytes(tx)
if err != nil {
return nil, fmt.Errorf("failed to rlp encode tx: %w", err)
}

const signatureCost = 68 * 16
const zeroCost = 4
const oneCost = 16
zeroCostBig := big.NewInt(zeroCost)
oneCostBig := big.NewInt(oneCost)

// l1GasUsed calculation: l1GasUsed = (zeroes * 4 + ones * 16) + signatureCost
l1GasUsed := big.NewInt(signatureCost)
for _, b := range txBz {
if b == 0 {
l1GasUsed.Add(l1GasUsed, zeroCostBig)
} else {
l1GasUsed.Add(l1GasUsed, oneCostBig)
}
}

return l1GasUsed, nil
}

0 comments on commit 7956099

Please sign in to comment.