Skip to content

Commit

Permalink
feat: optimize l1 gas price calculation after snow hardfork (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
redhdx authored Mar 26, 2024
1 parent a066481 commit 595048e
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 36 deletions.
1 change: 1 addition & 0 deletions op-chain-ops/genesis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,7 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
//}

var baseFee *big.Int
// Simplify the basefee when constructing the genesis block and ignore the Snow fork logic just in genesis.
if config.Fermat != nil && config.Fermat.Cmp(big.NewInt(0)) <= 0 {
baseFee = bsc.BaseFeeByNetworks(big.NewInt(int64(config.L2ChainID)))
} else {
Expand Down
34 changes: 20 additions & 14 deletions op-node/chaincfg/chains.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ func GetRollupConfig(name string) (*rollup.Config, error) {
var NetworksByName = map[string]rollup.Config{
"opBNBMainnet": OPBNBMainnet,
"opBNBTestnet": OPBNBTestnet,
"opBNBDevnet": OPBNBDevnet,
"opBNBQANet": OPBNBQANet,
}

var NetworksByChainId = map[string]rollup.Config{
"204": OPBNBMainnet,
"5611": OPBNBTestnet,
"1320": OPBNBDevnet,
"1322": OPBNBQANet,
}

func GetRollupConfigByNetwork(name string) (rollup.Config, error) {
Expand Down Expand Up @@ -141,6 +141,8 @@ var OPBNBMainnet = rollup.Config{
L1SystemConfigAddress: common.HexToAddress("0x7ac836148c14c74086d57f7828f2d065672db3b8"),
RegolithTime: u64Ptr(0),
Fermat: big.NewInt(9397477), // Nov-28-2023 06 AM +UTC
// TODO update timestamp
SnowTime: nil,
}

var OPBNBTestnet = rollup.Config{
Expand Down Expand Up @@ -172,21 +174,23 @@ var OPBNBTestnet = rollup.Config{
L1SystemConfigAddress: common.HexToAddress("0x406ac857817708eaf4ca3a82317ef4ae3d1ea23b"),
RegolithTime: u64Ptr(0),
Fermat: big.NewInt(12113000), // Nov-03-2023 06 AM +UTC
// TODO update timestamp
SnowTime: nil,
}

var OPBNBDevnet = rollup.Config{
var OPBNBQANet = rollup.Config{
Genesis: rollup.Genesis{
L1: eth.BlockID{
Hash: common.HexToHash("0x29aee50ab3edefa64219e5c9b9c07f7d1953a98f2f4003d2c6fd93abeee4b706"),
Number: 2890195,
Hash: common.HexToHash("0x3db93722c9951fe1da25dd652c6e2367674a97161df2acea322e915cab0d58ba"),
Number: 742038,
},
L2: eth.BlockID{
Hash: common.HexToHash("0x49d448b8dc98cc95e3968615ff3dbd904d9eec8252c5f52271f029896e6147ee"),
Hash: common.HexToHash("0x1cba296441b55cf9b5b306b6aef43e68e9aeff2450d68c391dec448604cf3baf"),
Number: 0,
},
L2Time: 1694166483,
L2Time: 1704856150,
SystemConfig: eth.SystemConfig{
BatcherAddr: common.HexToAddress("0x425a3598cb5e2d37213936e187914ea2059957ba"),
BatcherAddr: common.HexToAddress("0xe309831c77d5fb5f189dd97c598e26e5c014f2d6"),
Overhead: eth.Bytes32(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000834")),
Scalar: eth.Bytes32(common.HexToHash("0x00000000000000000000000000000000000000000000000000000000000f4240")),
GasLimit: 100000000,
Expand All @@ -196,13 +200,15 @@ var OPBNBDevnet = rollup.Config{
MaxSequencerDrift: 600,
SeqWindowSize: 14400,
ChannelTimeout: 1200,
L1ChainID: big.NewInt(797),
L2ChainID: big.NewInt(1320),
BatchInboxAddress: common.HexToAddress("0xff00000000000000000000000000000000000204"),
DepositContractAddress: common.HexToAddress("0xd93160096c5b65bb036b3269eb02328ddadb9856"),
L1SystemConfigAddress: common.HexToAddress("0xf053067cec8d8990de2ba9e17ec2f16c63c7bec4"),
L1ChainID: big.NewInt(714),
L2ChainID: big.NewInt(1322),
BatchInboxAddress: common.HexToAddress("0xff00000000000000000000000000000000001322"),
DepositContractAddress: common.HexToAddress("0xb7cdbce0b1f153b4cb2acc36aeb4d9d2cdda1132"),
L1SystemConfigAddress: common.HexToAddress("0x6a2607255801095b23256a341b24d31275fe2438"),
RegolithTime: u64Ptr(0),
Fermat: big.NewInt(3615117),
// Fermat: big.NewInt(3615117),
// TODO update timestamp
SnowTime: nil,
}

func u64Ptr(v uint64) *uint64 {
Expand Down
69 changes: 57 additions & 12 deletions op-node/rollup/derive/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth"
)

var (
latestBlockHash common.Hash
latestL1GasPrice *big.Int
)

// L1ReceiptsFetcher fetches L1 header info and receipts for the payload attributes derivation (the info tx and deposits)
type L1ReceiptsFetcher interface {
InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error)
Expand Down Expand Up @@ -60,18 +65,6 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
return nil, NewTemporaryError(fmt.Errorf("failed to retrieve L2 parent block: %w", err))
}

// Calculate bsc block base fee
var l1BaseFee *big.Int
if ba.cfg.IsFermat(big.NewInt(int64(l2Parent.Number + 1))) {
l1BaseFee = bsc.BaseFeeByNetworks(ba.cfg.L2ChainID)
} else {
_, transactions, err := ba.l1.InfoAndTxsByHash(ctx, epoch.Hash)
if err != nil {
return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info and txs: %w", err))
}
l1BaseFee = bsc.BaseFeeByTransactions(transactions)
}

// If the L1 origin changed this block, then we are in the first block of the epoch. In this
// case we need to fetch all transaction receipts from the L1 origin block so we can scan for
// user deposits.
Expand Down Expand Up @@ -112,6 +105,22 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
seqNumber = l2Parent.SequenceNumber + 1
}

// Calculate bsc block base fee
var l1BaseFee *big.Int
if ba.cfg.IsSnow(l2Parent.Time + ba.cfg.BlockTime) {
l1BaseFee, err = SnowL1GasPrice(ctx, ba, epoch)
if err != nil {
return nil, err
}
} else if ba.cfg.IsFermat(big.NewInt(int64(l2Parent.Number + 1))) {
l1BaseFee = bsc.BaseFeeByNetworks(ba.cfg.L2ChainID)
} else {
_, transactions, err := ba.l1.InfoAndTxsByHash(ctx, epoch.Hash)
if err != nil {
return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info and txs: %w", err))
}
l1BaseFee = bsc.BaseFeeByTransactions(transactions)
}
l1Info = bsc.NewBlockInfoBSCWrapper(l1Info, l1BaseFee)

// Sanity check the L1 origin was correctly selected to maintain the time invariant between L1 and L2
Expand Down Expand Up @@ -149,3 +158,39 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
func (ba *FetchingAttributesBuilder) CachePayloadByHash(payload *eth.ExecutionPayload) bool {
return ba.l2.CachePayloadByHash(payload)
}

func SnowL1GasPrice(ctx context.Context, ba *FetchingAttributesBuilder, epoch eth.BlockID) (*big.Int, error) {
// Consider this situation. If start a new l2 chain, starting from the block height of l1 less than CountBlockSize,
// in fact, this situation is unlikely to happen except some test cases.
if epoch.Number < bsc.CountBlockSize-1 {
return bsc.DefaultBaseFee, nil
}
if latestBlockHash == epoch.Hash {
return latestL1GasPrice, nil
}
var allMedianGasPrice []*big.Int
blockHash := epoch.Hash
for len(allMedianGasPrice) < bsc.CountBlockSize {
if blockInfo, ok := bsc.BlockInfoCache.Get(blockHash); ok {
allMedianGasPrice = append(allMedianGasPrice, blockInfo.MedianGasPrice)
blockHash = blockInfo.ParentHash
} else {
block, transactions, err := ba.l1.InfoAndTxsByHash(ctx, blockHash)
if err != nil {
return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info and txs: %w", err))
}
medianGasPrice := bsc.MedianGasPrice(transactions)
allMedianGasPrice = append(allMedianGasPrice, medianGasPrice)
newBlockInfo := bsc.BlockInfo{
BlockHash: block.Hash(),
ParentHash: block.ParentHash(),
MedianGasPrice: medianGasPrice,
}
bsc.BlockInfoCache.Add(block.Hash(), newBlockInfo)
blockHash = block.ParentHash()
}
}
latestBlockHash = epoch.Hash
latestL1GasPrice = bsc.FinalGasPrice(allMedianGasPrice)
return latestL1GasPrice, nil
}
46 changes: 38 additions & 8 deletions op-node/rollup/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import (
"math/big"
"time"

"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"

"github.com/ethereum-optimism/optimism/op-service/eth"
)

var (
Expand All @@ -36,6 +34,14 @@ var (
ErrL2ChainIDNotPositive = errors.New("L2 chain ID must be non-zero and positive")
)

// NetworkNames are user friendly names to use in the chain spec banner.
var NetworkNames = map[string]string{
"56": "bscMainnet",
"204": "opBNBMainnet",
"97": "bscTestnet",
"5611": "opBNBTestnet",
}

type Genesis struct {
// The L1 block that the rollup starts *after* (no derived transactions)
L1 eth.BlockID `json:"l1"`
Expand Down Expand Up @@ -84,6 +90,9 @@ type Config struct {
// OPBNB hard fork L2 block number
// Fermat switch block (nil = no fork, 0 = already on Fermat)
Fermat *big.Int `json:"fermat,omitempty"`
// SnowTime sets the activation time of the next network upgrade.
// Active if SnowTime != nil && L2 block timestamp >= *SnowTime, inactive otherwise.
SnowTime *uint64 `json:"snow_time,omitempty"`

// Note: below addresses are part of the block-derivation process,
// and required to be the same network-wide to stay in consensus.
Expand Down Expand Up @@ -287,6 +296,11 @@ func (c *Config) IsFermat(num *big.Int) bool {
return isBlockForked(c.Fermat, num)
}

// IsSnow returns whether the time is either equal to the Snow fork time or greater.
func (c *Config) IsSnow(time uint64) bool {
return isTimestampForked(c.SnowTime, time)
}

// isBlockForked returns whether a fork scheduled at block s is active at the
// given head block. Whilst this method is the same as isTimestampForked, they
// are explicitly separate for clearer reading.
Expand All @@ -297,20 +311,32 @@ func isBlockForked(s, head *big.Int) bool {
return s.Cmp(head) <= 0
}

// isTimestampForked returns whether a fork scheduled at timestamp s is active
// at the given head timestamp. Whilst this method is the same as isBlockForked,
// they are explicitly separate for clearer reading.
func isTimestampForked(s *uint64, head uint64) bool {
if s == nil {
return false
}
return *s <= head
}

// Description outputs a banner describing the important parts of rollup configuration in a human-readable form.
// Optionally provide a mapping of L2 chain IDs to network names to label the L2 chain with if not unknown.
// The config should be config.Check()-ed before creating a description.
func (c *Config) Description(l2Chains map[string]string) string {
// Find and report the network the user is running
var banner string
networkL2 := ""
// replace using opBNB networks
networkL2 := NetworkNames[c.L2ChainID.String()]
if l2Chains != nil {
networkL2 = l2Chains[c.L2ChainID.String()]
}
if networkL2 == "" {
networkL2 = "unknown L2"
}
networkL1 := params.NetworkNames[c.L1ChainID.String()]
// replace using bsc networks
networkL1 := NetworkNames[c.L1ChainID.String()]
if networkL1 == "" {
networkL1 = "unknown L1"
}
Expand All @@ -328,6 +354,8 @@ func (c *Config) Description(l2Chains map[string]string) string {
banner += fmt.Sprintf(" - SpanBatch: %s\n", fmtForkTimeOrUnset(c.SpanBatchTime))
banner += "OPBNB hard forks (block based):\n"
banner += fmt.Sprintf(" - Fermat: #%-8v\n", c.Fermat)
banner += "OPBNB hard forks (timestamp based):\n"
banner += fmt.Sprintf(" - Snow: %s\n", fmtForkTimeOrUnset(c.SnowTime))
// Report the protocol version
banner += fmt.Sprintf("Node supports up to OP-Stack Protocol Version: %s\n", OPStackSupport)
return banner
Expand All @@ -338,14 +366,16 @@ func (c *Config) Description(l2Chains map[string]string) string {
// The config should be config.Check()-ed before creating a description.
func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) {
// Find and report the network the user is running
networkL2 := ""
// replace using opBNB networks
networkL2 := NetworkNames[c.L2ChainID.String()]
if l2Chains != nil {
networkL2 = l2Chains[c.L2ChainID.String()]
}
if networkL2 == "" {
networkL2 = "unknown L2"
}
networkL1 := params.NetworkNames[c.L1ChainID.String()]
// replace using bsc networks
networkL1 := NetworkNames[c.L1ChainID.String()]
if networkL1 == "" {
networkL1 = "unknown L1"
}
Expand All @@ -355,7 +385,7 @@ func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) {
"l1_block_number", c.Genesis.L1.Number, "regolith_time", fmtForkTimeOrUnset(c.RegolithTime),
"canyon_time", fmtForkTimeOrUnset(c.CanyonTime),
"span_batch_time", fmtForkTimeOrUnset(c.SpanBatchTime),
"Fermat", c.Fermat,
"fermat", c.Fermat, "snow_time", fmtForkTimeOrUnset(c.SnowTime),
)
}

Expand Down
4 changes: 2 additions & 2 deletions op-node/rollup/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ func TestRandomConfigDescription(t *testing.T) {
})
t.Run("named L1", func(t *testing.T) {
config := randConfig()
config.L1ChainID = big.NewInt(5)
config.L1ChainID = big.NewInt(97)
out := config.Description(map[string]string{config.L2ChainID.String(): "foobar chain"})
require.Contains(t, out, "goerli")
require.Contains(t, out, "bscTestnet")
})
t.Run("unnamed", func(t *testing.T) {
config := randConfig()
Expand Down
47 changes: 47 additions & 0 deletions op-service/bsc/compat.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package bsc

import (
lru "github.com/hashicorp/golang-lru/v2"
"math/big"
"sort"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -14,6 +16,24 @@ var DefaultBaseFee = big.NewInt(3000000000)
var DefaultOPBNBTestnetBaseFee = big.NewInt(5000000000)
var OPBNBTestnet = big.NewInt(5611)

const (
percentile = 50
CountBlockSize = 21
BlockInfoCacheCap = 1000
)

type BlockInfo struct {
BlockHash common.Hash
ParentHash common.Hash
MedianGasPrice *big.Int
}

var BlockInfoCache *lru.Cache[common.Hash, BlockInfo]

func init() {
BlockInfoCache, _ = lru.New[common.Hash, BlockInfo](BlockInfoCacheCap)
}

type BlockInfoBSCWrapper struct {
eth.BlockInfo
baseFee *big.Int
Expand Down Expand Up @@ -78,3 +98,30 @@ func ToLegacyCallMsg(callMsg ethereum.CallMsg) ethereum.CallMsg {
Data: callMsg.Data,
}
}

func MedianGasPrice(transactions types.Transactions) *big.Int {
var nonZeroTxsGasPrice []*big.Int
for _, tx := range transactions {
if tx.GasPrice().Cmp(common.Big0) > 0 {
nonZeroTxsGasPrice = append(nonZeroTxsGasPrice, tx.GasPrice())
}
}
sort.Sort(bigIntArray(nonZeroTxsGasPrice))
medianGasPrice := DefaultBaseFee
if len(nonZeroTxsGasPrice) != 0 {
medianGasPrice = nonZeroTxsGasPrice[(len(nonZeroTxsGasPrice)-1)*percentile/100]
}
return medianGasPrice
}

func FinalGasPrice(allMedianGasPrice []*big.Int) *big.Int {
sort.Sort(bigIntArray(allMedianGasPrice))
finalGasPrice := allMedianGasPrice[(len(allMedianGasPrice)-1)*percentile/100]
return finalGasPrice
}

type bigIntArray []*big.Int

func (s bigIntArray) Len() int { return len(s) }
func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

0 comments on commit 595048e

Please sign in to comment.