Skip to content

Commit

Permalink
feat: add fee distribution via L1Block contract
Browse files Browse the repository at this point in the history
DepositTx updates the ValidatorRewardRatio in the L1Block contract, which
is used to distribute the tx fee to ProtocolVault and ValidatorRewardVault.
  • Loading branch information
Pangssu committed Apr 25, 2023
1 parent c2269a5 commit b509b4b
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 12 deletions.
1 change: 1 addition & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
txContext := core.NewEVMTxContext(msg)
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
evmContext.L1CostFunc = types.NewL1CostFunc(b.config, stateDB)
evmContext.FeeDistributionFunc = types.NewFeeDistributionFunc(b.config, stateDB)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
Expand Down
2 changes: 2 additions & 0 deletions core/state_prefetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
signer = types.MakeSigner(p.config, header.Number)
)
blockContext.L1CostFunc = types.NewL1CostFunc(p.config, statedb)
blockContext.FeeDistributionFunc = types.NewFeeDistributionFunc(p.config, statedb)

// Iterate over and process the individual transactions
byzantium := p.config.IsByzantium(block.Number())
for i, tx := range block.Transactions() {
Expand Down
2 changes: 2 additions & 0 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
}
blockContext := NewEVMBlockContext(header, p.bc, nil)
blockContext.L1CostFunc = types.NewL1CostFunc(p.config, statedb)
blockContext.FeeDistributionFunc = types.NewFeeDistributionFunc(p.config, statedb)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
Expand Down Expand Up @@ -170,6 +171,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author)
blockContext.L1CostFunc = types.NewL1CostFunc(config, statedb)
blockContext.FeeDistributionFunc = types.NewFeeDistributionFunc(config, statedb)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}
30 changes: 18 additions & 12 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,21 +432,27 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) {
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
}

if st.evm.Config.NoBaseFee && st.gasFeeCap.Sign() == 0 && st.gasTipCap.Sign() == 0 {
// Skip fee payment when NoBaseFee is set and the fee fields
// are 0. This avoids a negative effectiveTip being applied to
// the coinbase when simulating calls.
} else {
fee := new(big.Int).SetUint64(st.gasUsed())
fee.Mul(fee, effectiveTip)
st.state.AddBalance(st.evm.Context.Coinbase, fee)
}

gasUsed := new(big.Int).SetUint64(st.gasUsed())
if kromaConfig := st.evm.ChainConfig().Kroma; kromaConfig != nil {
st.state.AddBalance(params.KromaBaseFeeRecipient, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee))
if cost := st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.msg); cost != nil {
blockNum := st.evm.Context.BlockNumber.Uint64()
feeDist := st.evm.Context.FeeDistributionFunc(blockNum, gasUsed, st.evm.Context.BaseFee, effectiveTip)

st.state.AddBalance(st.evm.Context.Coinbase, feeDist.Reward)
st.state.AddBalance(params.KromaBaseFeeRecipient, feeDist.Protocol)

if cost := st.evm.Context.L1CostFunc(blockNum, st.msg); cost != nil {
st.state.AddBalance(params.KromaL1FeeRecipient, cost)
}
} else {
if st.evm.Config.NoBaseFee && st.gasFeeCap.Sign() == 0 && st.gasTipCap.Sign() == 0 {
// Skip fee payment when NoBaseFee is set and the fee fields
// are 0. This avoids a negative effectiveTip being applied to
// the coinbase when simulating calls.
} else {
fee := new(big.Int)
fee.Mul(gasUsed, effectiveTip)
st.state.AddBalance(st.evm.Context.Coinbase, fee)
}
}

return &ExecutionResult{
Expand Down
68 changes: 68 additions & 0 deletions core/types/fee_distribution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package types

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

var (
ValidatorRewardRatioSlot = common.BigToHash(big.NewInt(7))
)

// FeeDistributionFunc is used in the state transition to determine the validation reward and protocol fee.
// Returns nil if not Kroma execution engine.
type FeeDistributionFunc func(blockNum uint64, gasUsed, baseFee, effectiveTip *big.Int) *FeeDistribution

type FeeDistribution struct {
Reward *big.Int
Protocol *big.Int
}

func NewFeeDistributionFunc(config *params.ChainConfig, statedb StateGetter) FeeDistributionFunc {
cacheBlockNum := ^uint64(0)
var ratio int64
return func(blockNum uint64, gasUsed, baseFee, effectiveTip *big.Int) *FeeDistribution {
if config.Kroma == nil {
return nil
}

if blockNum != cacheBlockNum {
ratio = statedb.GetState(L1BlockAddr, ValidatorRewardRatioSlot).Big().Int64()
if ratio > 10000 {
ratio = 0
}

cacheBlockNum = blockNum
}
fee := new(big.Int)
fee.Mul(gasUsed, baseFee)
fee.Add(fee, new(big.Int).Mul(gasUsed, effectiveTip))

R := big.NewRat(ratio, 10000)
reward := new(big.Int).Mul(fee, R.Num())
reward.Div(reward, R.Denom())

return &FeeDistribution{
Reward: reward,
Protocol: new(big.Int).Sub(fee, reward),
}
}
}
2 changes: 2 additions & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ type BlockContext struct {
GetHash GetHashFunc
// L1CostFunc returns the L1 cost of the rollup message, the function may be nil, or return nil
L1CostFunc types.L1CostFunc
// FeeDistributionFunc returns the distribution value of the transaction fee
FeeDistributionFunc types.FeeDistributionFunc

// Block information
Coinbase common.Address // Provides information for COINBASE
Expand Down
1 change: 1 addition & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *sta
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
context.L1CostFunc = types.NewL1CostFunc(b.eth.blockchain.Config(), state)
context.FeeDistributionFunc = types.NewFeeDistributionFunc(b.eth.blockchain.Config(), state)
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), state.Error, nil
}

Expand Down
1 change: 1 addition & 0 deletions eth/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
context.L1CostFunc = types.NewL1CostFunc(eth.blockchain.Config(), statedb)
context.FeeDistributionFunc = types.NewFeeDistributionFunc(eth.blockchain.Config(), statedb)
if idx == txIndex {
return msg, context, statedb, release, nil
}
Expand Down
6 changes: 6 additions & 0 deletions eth/tracers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
blockCtx = core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil)
)
blockCtx.L1CostFunc = types.NewL1CostFunc(api.backend.ChainConfig(), task.statedb)
blockCtx.FeeDistributionFunc = types.NewFeeDistributionFunc(api.backend.ChainConfig(), task.statedb)
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
msg, _ := tx.AsMessage(signer, task.block.BaseFee())
Expand Down Expand Up @@ -553,6 +554,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
)
vmctx.L1CostFunc = types.NewL1CostFunc(chainConfig, statedb)
vmctx.FeeDistributionFunc = types.NewFeeDistributionFunc(chainConfig, statedb)
for i, tx := range block.Transactions() {
if err := ctx.Err(); err != nil {
return nil, err
Expand Down Expand Up @@ -675,6 +677,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
for task := range jobs {
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
blockCtx.L1CostFunc = types.NewL1CostFunc(api.backend.ChainConfig(), task.statedb)
blockCtx.FeeDistributionFunc = types.NewFeeDistributionFunc(api.backend.ChainConfig(), task.statedb)
msg, _ := txs[task.index].AsMessage(signer, block.BaseFee())
txctx := &Context{
BlockHash: blockHash,
Expand All @@ -695,6 +698,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
var failed error
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
blockCtx.L1CostFunc = types.NewL1CostFunc(api.backend.ChainConfig(), statedb)
blockCtx.FeeDistributionFunc = types.NewFeeDistributionFunc(api.backend.ChainConfig(), statedb)
txloop:
for i, tx := range txs {
// Send the trace task over for execution
Expand Down Expand Up @@ -785,6 +789,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
chainConfig, canon = overrideConfig(chainConfig, config.Overrides)
}
vmctx.L1CostFunc = types.NewL1CostFunc(chainConfig, statedb)
vmctx.FeeDistributionFunc = types.NewFeeDistributionFunc(chainConfig, statedb)
for i, tx := range block.Transactions() {
// Prepare the transaction for un-traced execution
var (
Expand Down Expand Up @@ -938,6 +943,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
config.BlockOverrides.Apply(&vmctx)
}
vmctx.L1CostFunc = types.NewL1CostFunc(api.backend.ChainConfig(), statedb)
vmctx.FeeDistributionFunc = types.NewFeeDistributionFunc(api.backend.ChainConfig(), statedb)
// Execute the trace
msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions eth/tracers/api_blocktrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ func (api *API) getBlockTrace(block *types.Block, env *traceEnv) (*types.BlockTr
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
env.state.SetTxContext(tx.Hash(), i)
env.blockCtx.L1CostFunc = types.NewL1CostFunc(api.backend.ChainConfig(), env.state)
env.blockCtx.FeeDistributionFunc = types.NewFeeDistributionFunc(api.backend.ChainConfig(), env.state)
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, api.backend.ChainConfig(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
failed = err
Expand Down Expand Up @@ -227,6 +228,7 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc
}

env.blockCtx.L1CostFunc = types.NewL1CostFunc(api.backend.ChainConfig(), state)
env.blockCtx.FeeDistributionFunc = types.NewFeeDistributionFunc(api.backend.ChainConfig(), state)
tracer := vm.NewStructLogger(env.config.LogConfig)
// Run the transaction with tracing enabled.
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), state, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
Expand Down
1 change: 1 addition & 0 deletions eth/tracers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
return msg, context, statedb, release, nil
}
context.L1CostFunc = types.NewL1CostFunc(b.chainConfig, statedb)
context.FeeDistributionFunc = types.NewFeeDistributionFunc(b.chainConfig, statedb)
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
Expand Down
1 change: 1 addition & 0 deletions les/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
context.L1CostFunc = types.NewL1CostFunc(b.eth.chainConfig, state)
context.FeeDistributionFunc = types.NewFeeDistributionFunc(b.eth.chainConfig, state)
return vm.NewEVM(context, txContext, state, b.eth.chainConfig, *vmConfig), state.Error, nil
}

Expand Down
1 change: 1 addition & 0 deletions les/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
return msg, context, statedb, release, nil
}
context.L1CostFunc = types.NewL1CostFunc(leth.blockchain.Config(), statedb)
context.FeeDistributionFunc = types.NewFeeDistributionFunc(leth.blockchain.Config(), statedb)
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
Expand Down

0 comments on commit b509b4b

Please sign in to comment.