Skip to content
This repository was archived by the owner on Apr 11, 2021. It is now read-only.

Commit 30b541a

Browse files
gakonstben-chainsmartcontractskarlfloersch
authored
Sequencer Fee Pricing Part 2: Electric Boogaloo: Override EstimateGas to take data price into account (#273)
* fix: add nil check when estimating gas to avoid panic during contract creation * fix: revert DoEstimateGas changes introduced as a temp fix in #22 * Tweak fudge factor for Geth gas estimation (#292) * experimenting with gas fudge factor * fix formatting * try using 1m gas constant * Add log statement for the gas estimate * Add more detail to gas estimate log * one more log edit * Try new formula * Bump base gas * Even more base gas * even more * Just 1m? * one more time * Final cleanup * Minor fix * Update internal/ethapi/api.go * don't use cap-1 * Make sure data is not nil Co-authored-by: Karl Floersch <[email protected]> * feat(api): make eth_gasPrice always return 1 gwei * feat: overload estimateGas to return the data+execution fee * feat: implement static L1 Gas Price oracle * feat: allow configuring the L1GasPrice via CLI at node startup * feat: allow configuring the L1GasPrice remotely over a new private RPC namespace * Sequencer Fee Pricing Part 3, feat: Pull L1Gasprice from the Data Service (#277) * feat: pull L1GasPrice from the data transport service * refactor: split out single-iteration of sequencer loop * test(sync-service): ensure L1GasPrice is set correctly * chore: gofmt * fix: parse json response as string and test * chore: expose L1Gpo in the sync service * refactor: create helper function for calculating the rollup fee * docs: add doc for rollup tx size constant * chore(console_test): add rollup_personal to test * chore: remove empty line * chore: adjust review comments * do not cast gasUsed * adjust comment on GasPrice method * add nil check for args.Data * log gas price changes in the sync service * chore: re-order imports * fix: skip txpool max gas check for rollup txs Security Question: does this introduce a DoS vector? * chore: debug log * fix(rollup_fee): normalize charged gas by the charged gas price * remove intrinsic gas checks * move intrinsic gas check behind non-ovm codepath * remove fudging code * fix wrong gas limit Co-authored-by: Ben Jones <[email protected]> Co-authored-by: smartcontracts <[email protected]> Co-authored-by: Karl Floersch <[email protected]>
1 parent 79c1dc1 commit 30b541a

25 files changed

+382
-85
lines changed

cmd/geth/consolecmd_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
)
3232

3333
const (
34-
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rollup:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
34+
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rollup:1.0 rollup_personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
3535
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
3636
)
3737

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ var (
164164
utils.RollupStateDumpPathFlag,
165165
utils.RollupDiffDbFlag,
166166
utils.RollupMaxCalldataSizeFlag,
167+
utils.RollupL1GasPriceFlag,
167168
}
168169

169170
rpcFlags = []cli.Flag{

cmd/geth/usage.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ var AppHelpFlagGroups = []flagGroup{
7878
utils.RollupStateDumpPathFlag,
7979
utils.RollupDiffDbFlag,
8080
utils.RollupMaxCalldataSizeFlag,
81+
utils.RollupL1GasPriceFlag,
8182
},
8283
},
8384
{

cmd/utils/flags.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,12 @@ var (
879879
Value: eth.DefaultConfig.Rollup.MaxCallDataSize,
880880
EnvVar: "ROLLUP_MAX_CALLDATA_SIZE",
881881
}
882+
RollupL1GasPriceFlag = BigFlag{
883+
Name: "rollup.l1gasprice",
884+
Usage: "The L1 gas price to use for the sequencer fees",
885+
Value: eth.DefaultConfig.Rollup.L1GasPrice,
886+
EnvVar: "ROLLUP_L1_GASPRICE",
887+
}
882888
)
883889

884890
// MakeDataDir retrieves the currently requested data directory, terminating
@@ -1152,6 +1158,9 @@ func setRollup(ctx *cli.Context, cfg *rollup.Config) {
11521158
if ctx.GlobalIsSet(RollupTimstampRefreshFlag.Name) {
11531159
cfg.TimestampRefreshThreshold = ctx.GlobalDuration(RollupTimstampRefreshFlag.Name)
11541160
}
1161+
if ctx.GlobalIsSet(RollupL1GasPriceFlag.Name) {
1162+
cfg.L1GasPrice = GlobalBig(ctx, RollupL1GasPriceFlag.Name)
1163+
}
11551164
}
11561165

11571166
// setLes configures the les server and ultra light client settings from the command line flags.

core/rollup_fee.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package core
2+
3+
import (
4+
"math/big"
5+
)
6+
7+
/// ROLLUP_BASE_TX_SIZE is the encoded rollup transaction's compressed size excluding
8+
/// the variable length data.
9+
/// Ref: https://github.com/ethereum-optimism/contracts/blob/409f190518b90301db20d0d4f53760021bc203a8/contracts/optimistic-ethereum/OVM/precompiles/OVM_SequencerEntrypoint.sol#L47
10+
const ROLLUP_BASE_TX_SIZE int = 96
11+
12+
/// CalculateFee calculates the fee that must be paid to the Rollup sequencer, taking into
13+
/// account the cost of publishing data to L1.
14+
/// Returns: (ROLLUP_BASE_TX_SIZE + len(data)) * dataPrice + executionPrice * gasUsed
15+
func CalculateRollupFee(data []byte, gasUsed uint64, dataPrice, executionPrice *big.Int) *big.Int {
16+
dataLen := int64(ROLLUP_BASE_TX_SIZE + len(data))
17+
// get the data fee
18+
dataFee := new(big.Int).Mul(dataPrice, big.NewInt(dataLen))
19+
executionFee := new(big.Int).Mul(executionPrice, new(big.Int).SetUint64(gasUsed))
20+
fee := new(big.Int).Add(dataFee, executionFee)
21+
return fee
22+
}

core/rollup_fee_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package core
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
)
7+
8+
var feeTests = map[string]struct {
9+
dataLen int
10+
gasUsed uint64
11+
dataPrice int64
12+
executionPrice int64
13+
}{
14+
"simple": {10000, 10, 20, 30},
15+
"zero gas used": {10000, 0, 20, 30},
16+
"zero data price": {10000, 0, 0, 30},
17+
"zero execution price": {10000, 0, 0, 0},
18+
}
19+
20+
func TestCalculateRollupFee(t *testing.T) {
21+
for name, tt := range feeTests {
22+
t.Run(name, func(t *testing.T) {
23+
data := make([]byte, 0, tt.dataLen)
24+
fee := CalculateRollupFee(data, tt.gasUsed, big.NewInt(tt.dataPrice), big.NewInt(tt.executionPrice))
25+
26+
dataFee := uint64((ROLLUP_BASE_TX_SIZE + len(data)) * int(tt.dataPrice))
27+
executionFee := uint64(tt.executionPrice) * tt.gasUsed
28+
expectedFee := dataFee + executionFee
29+
if fee.Cmp(big.NewInt(int64(expectedFee))) != 0 {
30+
t.Errorf("rollup fee check failed: expected %d, got %s", expectedFee, fee.String())
31+
}
32+
})
33+
}
34+
}

core/state_processor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
9090
}
9191
} else {
9292
decompressor := config.StateDump.Accounts["OVM_SequencerEntrypoint"]
93-
msg, err = AsOvmMessage(tx, types.MakeSigner(config, header.Number), decompressor.Address)
93+
msg, err = AsOvmMessage(tx, types.MakeSigner(config, header.Number), decompressor.Address, header.GasLimit)
9494
if err != nil {
9595
return nil, err
9696
}

core/state_transition_ovm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func toExecutionManagerRun(evm *vm.EVM, msg Message) (Message, error) {
5858
return outputmsg, nil
5959
}
6060

61-
func AsOvmMessage(tx *types.Transaction, signer types.Signer, decompressor common.Address) (Message, error) {
61+
func AsOvmMessage(tx *types.Transaction, signer types.Signer, decompressor common.Address, gasLimit uint64) (Message, error) {
6262
msg, err := tx.AsMessage(signer)
6363
if err != nil {
6464
// This should only be allowed to pass if the transaction is in the ctc
@@ -85,7 +85,7 @@ func AsOvmMessage(tx *types.Transaction, signer types.Signer, decompressor commo
8585
msg.From(),
8686
&decompressor,
8787
tx.GetMeta().RawTransaction,
88-
msg.Gas(),
88+
gasLimit,
8989
)
9090

9191
if err != nil {

core/tx_pool.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,9 @@ var (
9292
)
9393

9494
var (
95-
evictionInterval = time.Minute // Time interval to check for evictable transactions
96-
statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
95+
evictionInterval = time.Minute // Time interval to check for evictable transactions
96+
statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
97+
gwei = big.NewInt(params.GWei) // 1 gwei, used as a flag for "rollup" transactions
9798
)
9899

99100
var (
@@ -537,10 +538,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
537538
if tx.Value().Sign() < 0 {
538539
return ErrNegativeValue
539540
}
541+
540542
// Ensure the transaction doesn't exceed the current block limit gas.
541-
if pool.currentMaxGas < tx.Gas() {
543+
// We skip this condition check if the transaction's gasPrice is set to 1gwei,
544+
// which indicates a "rollup" transaction that's paying for its data.
545+
if pool.currentMaxGas < tx.Gas() && tx.GasPrice().Cmp(gwei) != 0 {
542546
return ErrGasLimit
543547
}
548+
544549
// Make sure the transaction is signed properly
545550
from, err := types.Sender(pool.signer, tx)
546551
if err != nil {
@@ -565,14 +570,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
565570
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
566571
return ErrInsufficientFunds
567572
}
568-
}
569-
// Ensure the transaction has more gas than the basic tx fee.
570-
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul)
571-
if err != nil {
572-
return err
573-
}
574-
if tx.Gas() < intrGas {
575-
return ErrIntrinsicGas
573+
// Ensure the transaction has more gas than the basic tx fee.
574+
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul)
575+
if err != nil {
576+
return err
577+
}
578+
if tx.Gas() < intrGas {
579+
return ErrIntrinsicGas
580+
}
576581
}
577582
return nil
578583
}
@@ -585,13 +590,15 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
585590
// whitelisted, preventing any associated transaction from being dropped out of the pool
586591
// due to pricing constraints.
587592
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
593+
log.Debug("received tx", "gas", tx.Gas(), "gasprice", tx.GasPrice().Uint64())
588594
// If the transaction is already known, discard it
589595
hash := tx.Hash()
590596
if pool.all.Get(hash) != nil {
591597
log.Trace("Discarding already known transaction", "hash", hash)
592598
knownTxMeter.Mark(1)
593599
return false, fmt.Errorf("known transaction: %x", hash)
594600
}
601+
595602
// If the transaction fails basic validation, discard it
596603
if err := pool.validateTx(tx, local); err != nil {
597604
log.Trace("Discarding invalid transaction", "hash", hash, "err", err)

eth/api_backend.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type EthAPIBackend struct {
4646
extRPCEnabled bool
4747
eth *Ethereum
4848
gpo *gasprice.Oracle
49+
l1gpo *gasprice.L1Oracle
4950
verifier bool
5051
gasLimit uint64
5152
UsingOVM bool
@@ -371,6 +372,14 @@ func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
371372
return b.gpo.SuggestPrice(ctx)
372373
}
373374

375+
func (b *EthAPIBackend) SuggestDataPrice(ctx context.Context) (*big.Int, error) {
376+
return b.l1gpo.SuggestDataPrice(ctx)
377+
}
378+
379+
func (b *EthAPIBackend) SetL1GasPrice(ctx context.Context, gasPrice *big.Int) {
380+
b.l1gpo.SetL1GasPrice(gasPrice)
381+
}
382+
374383
func (b *EthAPIBackend) ChainDb() ethdb.Database {
375384
return b.eth.ChainDb()
376385
}

0 commit comments

Comments
 (0)