Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration of Sonic's per-block base-fee adjustment #297

Merged
merged 3 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions evmcore/dummy_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func ToEvmHeader(block *inter.Block, prevHash common.Hash, rules opera.Rules) *E
baseFee := rules.Economy.MinGasPrice
if !rules.Upgrades.London {
baseFee = nil
} else if rules.Upgrades.Sonic {
baseFee = block.BaseFee
}

prevRandao := common.Hash{}
Expand All @@ -99,6 +101,7 @@ func ToEvmHeader(block *inter.Block, prevHash common.Hash, rules opera.Rules) *E
Root: block.StateRoot,
Number: big.NewInt(int64(block.Number)),
Time: block.Time,
Duration: time.Duration(block.Duration) * time.Nanosecond,
GasLimit: block.GasLimit,
GasUsed: block.GasUsed,
BaseFee: baseFee,
Expand Down Expand Up @@ -135,13 +138,13 @@ func (h *EvmHeader) EthHeader() *types.Header {
ethHeader := &types.Header{
Number: h.Number,
Coinbase: h.Coinbase,
GasLimit: 0xffffffffffff, // don't use h.GasLimit (too much bits) here to avoid parsing issues
GasLimit: h.GasLimit,
GasUsed: h.GasUsed,
Root: h.Root,
TxHash: h.TxHash,
ParentHash: h.ParentHash,
Time: uint64(h.Time.Unix()),
Extra: h.Hash.Bytes(),
Extra: inter.EncodeExtraData(h.Time.Time(), h.Duration),
BaseFee: h.BaseFee,

Difficulty: new(big.Int),
Expand Down Expand Up @@ -199,6 +202,7 @@ func (h *EvmHeader) ToJson(receipts types.Receipts) *EvmHeaderJson {
UncleHash: types.EmptyUncleHash,
Time: hexutil.Uint64(h.Time.Unix()),
TimeNano: hexutil.Uint64(h.Time),
Extra: inter.EncodeExtraData(h.Time.Time(), h.Duration),
BaseFee: (*hexutil.Big)(h.BaseFee),
Difficulty: new(hexutil.Big),
PrevRandao: h.PrevRandao,
Expand Down
25 changes: 20 additions & 5 deletions gossip/apply_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"errors"
"fmt"
"math/big"
"time"

"github.com/Fantom-foundation/go-opera/evmcore"
"github.com/Fantom-foundation/go-opera/gossip/gasprice"
"github.com/Fantom-foundation/go-opera/inter"
"github.com/Fantom-foundation/go-opera/inter/iblockproc"
"github.com/Fantom-foundation/go-opera/inter/ibr"
Expand Down Expand Up @@ -51,6 +53,9 @@ func (s *Store) ApplyGenesis(g genesis.Genesis) (err error) {
s.SetBlockEpochState(topEr.BlockState, topEr.EpochState)
s.FlushBlockEpochState()

s.SetGenesisID(g.GenesisID)
s.SetGenesisBlockIndex(topEr.BlockState.LastBlock.Idx)

// write blocks
rules := s.GetRules()
gasLimit := rules.Blocks.MaxBlockGas
Expand All @@ -60,15 +65,28 @@ func (s *Store) ApplyGenesis(g genesis.Genesis) (err error) {
WithTime(evmcore.FakeGenesisTime-1). // TODO: extend genesis generator to provide time
WithGasLimit(gasLimit).
WithStateRoot(common.Hash{}). // TODO: get proper state root from genesis data
WithBaseFee(big.NewInt(0)). // TODO: set initial base fee according to the rules
WithBaseFee(gasprice.GetInitialBaseFee(rules.Economy)).
LuisPH3 marked this conversation as resolved.
Show resolved Hide resolved
Build(),
)

baseFee := prevEs.Rules.Economy.MinGasPrice
blobGasPrice := big.NewInt(1) // TODO issue #147
var lastBlock ibr.LlrIdxFullBlockRecord
g.Blocks.ForEach(func(br ibr.LlrIdxFullBlockRecord) bool {
s.WriteFullBlockRecord(baseFee, blobGasPrice, gasLimit, br)
var duration time.Duration
if rules.Upgrades.Sonic {
block := s.GetBlock(br.Idx - 1)
if block == nil {
block = &inter.Block{BaseFee: new(big.Int)}
}
header := &evmcore.EvmHeader{
GasUsed: block.GasUsed,
BaseFee: block.BaseFee,
}
baseFee = gasprice.GetBaseFeeForNextBlock(header, rules.Economy)
duration = time.Duration(br.Time-block.Time) * time.Nanosecond
}
s.WriteFullBlockRecord(baseFee, blobGasPrice, gasLimit, duration, br)
if br.Idx > lastBlock.Idx {
lastBlock = br
}
Expand Down Expand Up @@ -124,9 +142,6 @@ func (s *Store) ApplyGenesis(g genesis.Genesis) (err error) {
s.Log.Info("StateDB imported successfully, stateRoot matches", "index", lastBlock.Idx, "root", lastBlock.StateRoot)
}

s.SetGenesisID(g.GenesisID)
s.SetGenesisBlockIndex(topEr.BlockState.LastBlock.Idx)

return nil
}

Expand Down
14 changes: 12 additions & 2 deletions gossip/blockproc/evmmodule/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/Fantom-foundation/go-opera/evmcore"
"github.com/Fantom-foundation/go-opera/gossip/blockproc"
"github.com/Fantom-foundation/go-opera/gossip/gasprice"
"github.com/Fantom-foundation/go-opera/inter/iblockproc"
"github.com/Fantom-foundation/go-opera/inter/state"
"github.com/Fantom-foundation/go-opera/opera"
Expand All @@ -32,8 +33,13 @@ func (p *EVMModule) Start(
prevrandao common.Hash,
) blockproc.EVMProcessor {
var prevBlockHash common.Hash
if block.Idx != 0 {
prevBlockHash = reader.GetHeader(common.Hash{}, uint64(block.Idx-1)).Hash
var baseFee *big.Int
if block.Idx == 0 {
baseFee = gasprice.GetInitialBaseFee(net.Economy)
} else {
header := reader.GetHeader(common.Hash{}, uint64(block.Idx-1))
prevBlockHash = header.Hash
baseFee = gasprice.GetBaseFeeForNextBlock(header, net.Economy)
}

// Start block
Expand All @@ -49,6 +55,7 @@ func (p *EVMModule) Start(
blockIdx: utils.U64toBig(uint64(block.Idx)),
prevBlockHash: prevBlockHash,
prevRandao: prevrandao,
gasBaseFee: baseFee,
}
}

Expand All @@ -62,6 +69,7 @@ type OperaEVMProcessor struct {

blockIdx *big.Int
prevBlockHash common.Hash
gasBaseFee *big.Int

gasUsed uint64

Expand All @@ -75,6 +83,8 @@ func (p *OperaEVMProcessor) evmBlockWith(txs types.Transactions) *evmcore.EvmBlo
baseFee := p.net.Economy.MinGasPrice
if !p.net.Upgrades.London {
baseFee = nil
} else if p.net.Upgrades.Sonic {
baseFee = p.gasBaseFee
}

prevRandao := common.Hash{}
Expand Down
21 changes: 16 additions & 5 deletions gossip/c_block_callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package gossip

import (
"fmt"
"github.com/Fantom-foundation/go-opera/utils/signers/gsignercache"
"math/big"
"sort"
"sync"
"sync/atomic"
"time"

"github.com/Fantom-foundation/go-opera/utils/signers/gsignercache"

"github.com/Fantom-foundation/lachesis-base/hash"
"github.com/Fantom-foundation/lachesis-base/inter/dag"
"github.com/Fantom-foundation/lachesis-base/inter/idx"
Expand Down Expand Up @@ -227,13 +228,15 @@ func consensusCallbackBeginBlockFn(
number := uint64(blockCtx.Idx)
lastBlockHeader := evmStateReader.GetHeaderByNumber(number - 1)
maxBlockGas := es.Rules.Blocks.MaxBlockGas
blockDuration := time.Duration(blockCtx.Time - bs.LastBlock.Time)
blockBuilder := inter.NewBlockBuilder().
WithEpoch(es.Epoch).
WithNumber(number).
WithParentHash(lastBlockHeader.Hash).
WithTime(blockCtx.Time).
WithPrevRandao(prevRandao).
WithGasLimit(maxBlockGas)
WithGasLimit(maxBlockGas).
WithDuration(blockDuration)

for i := range preInternalTxs {
blockBuilder.AddTransaction(
Expand Down Expand Up @@ -348,6 +351,7 @@ func consensusCallbackBeginBlockFn(

block := blockBuilder.Build()
evmBlock.Hash = block.Hash()
evmBlock.Duration = blockDuration

for _, tx := range blockBuilder.GetTransactions() {
store.evm.SetTx(tx.Hash(), tx)
Expand Down Expand Up @@ -380,9 +384,16 @@ func consensusCallbackBeginBlockFn(

now := time.Now()
blockAge := now.Sub(block.Time.Time())
log.Info("New block", "index", blockCtx.Idx, "id", block.Hash(), "gas_used",
evmBlock.GasUsed, "txs", fmt.Sprintf("%d/%d", len(evmBlock.Transactions), len(skippedTxs)),
"age", utils.PrettyDuration(blockAge), "t", utils.PrettyDuration(now.Sub(start)))
log.Info("New block",
"index", blockCtx.Idx,
"id", block.Hash(),
"gas_used", evmBlock.GasUsed,
"gas_rate", float64(evmBlock.GasUsed)/blockDuration.Seconds(),
"base_fee", evmBlock.BaseFee.String(),
"txs", fmt.Sprintf("%d/%d", len(evmBlock.Transactions), len(skippedTxs)),
"age", utils.PrettyDuration(blockAge),
"t", utils.PrettyDuration(now.Sub(start)),
LuisPH3 marked this conversation as resolved.
Show resolved Hide resolved
)
blockAgeGauge.Update(int64(blockAge.Nanoseconds()))

processedTxsMeter.Mark(int64(len(evmBlock.Transactions)))
Expand Down
6 changes: 4 additions & 2 deletions gossip/c_llr_callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gossip

import (
"math/big"
"time"

"github.com/Fantom-foundation/lachesis-base/hash"
"github.com/Fantom-foundation/lachesis-base/inter/idx"
Expand All @@ -25,7 +26,7 @@ func indexRawReceipts(s *Store, receiptsForStorage []*types.ReceiptForStorage, t
}
}

func (s *Store) WriteFullBlockRecord(baseFee *big.Int, blobGasPrice *big.Int, gasLimit uint64, br ibr.LlrIdxFullBlockRecord) {
func (s *Store) WriteFullBlockRecord(baseFee *big.Int, blobGasPrice *big.Int, gasLimit uint64, duration time.Duration, br ibr.LlrIdxFullBlockRecord) {
for _, tx := range br.Txs {
s.EvmStore().SetTx(tx.Hash(), tx)
}
Expand Down Expand Up @@ -55,7 +56,8 @@ func (s *Store) WriteFullBlockRecord(baseFee *big.Int, blobGasPrice *big.Int, ga
WithGasLimit(gasLimit).
WithGasUsed(br.GasUsed).
WithBaseFee(baseFee).
WithPrevRandao(common.Hash{1})
WithPrevRandao(common.Hash{1}).
WithDuration(duration)

for i := range br.Txs {
copy := types.Receipt(*br.Receipts[i])
Expand Down
4 changes: 3 additions & 1 deletion integration/makegenesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ type dummyHeaderReturner struct {
}

func (d dummyHeaderReturner) GetHeader(common.Hash, uint64) *evmcore.EvmHeader {
return &evmcore.EvmHeader{}
return &evmcore.EvmHeader{
BaseFee: big.NewInt(0),
}
}

func (b *GenesisBuilder) ExecuteGenesisTxs(blockProc BlockProc, genesisTxs types.Transactions) error {
Expand Down
57 changes: 53 additions & 4 deletions inter/block.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package inter

import (
"encoding/binary"
"errors"
"math/big"
"slices"
"time"
"unsafe"

"github.com/Fantom-foundation/lachesis-base/inter/idx"
Expand Down Expand Up @@ -43,6 +46,10 @@ type Block struct {
// Fields required for linking the block internally to a lachesis epoch.
Epoch idx.Epoch

// The duration of this block, being the difference between the predecessor
// block's timestamp and this block's timestamp, in nanoseconds.
Duration uint64

// The hash of this block, cached on first access.
hash common.Hash
}
Expand Down Expand Up @@ -70,10 +77,13 @@ func (b *Block) GetEthereumHeader() *types.Header {
GasLimit: b.GasLimit,
GasUsed: b.GasUsed,
Time: uint64(b.Time.Time().Unix()),
Extra: nil, // TODO: fill in extra data required for gas computation
MixDigest: b.PrevRandao,
Nonce: types.BlockNonce{}, // constant 0 in Ethereum
BaseFee: b.BaseFee,
Extra: EncodeExtraData(
b.Time.Time(),
time.Duration(b.Duration)*time.Nanosecond,
),
MixDigest: b.PrevRandao,
Nonce: types.BlockNonce{}, // constant 0 in Ethereum
BaseFee: b.BaseFee,

// Sonic does not have a beacon chain and no withdrawals.
WithdrawalsHash: &types.EmptyWithdrawalsHash,
Expand All @@ -85,6 +95,37 @@ func (b *Block) GetEthereumHeader() *types.Header {
}
}

// EncodeExtraData produces the ExtraData field encoding Sonic-specific data
// in the Ethereum block header. This data includes:
// - the nano-second part of the block's timestamp, for sub-second precision;
// - the duration of the block, in nanoseconds, defined as the time elapsed
// between the predecessor block's timestamp and this block's timestamp.
// This is used for the computation of gas rates to adjust the base fee.
func EncodeExtraData(time time.Time, duration time.Duration) []byte {
if duration < 0 {
duration = 0
}
extra := make([]byte, 12)
binary.BigEndian.PutUint32(extra[:4], uint32(time.Nanosecond()))
binary.BigEndian.PutUint64(extra[4:], uint64(duration.Nanoseconds()))
return extra
}

// DecodeExtraData decodes the ExtraData field encoding Sonic-specific data
// in the Ethereum block header. See EncodeExtraData for details.
func DecodeExtraData(extra []byte) (
nanos int,
duration time.Duration,
err error,
) {
if len(extra) != 12 {
return 0, 0, errors.New("extra data must be 12 bytes long")
}
return int(binary.BigEndian.Uint32(extra[:4])),
time.Duration(binary.BigEndian.Uint64(extra[4:])),
nil
}

func (b *Block) EstimateSize() int {
return int(unsafe.Sizeof(*b)) +
len(b.TransactionHashes)*int(unsafe.Sizeof(common.Hash{}))
Expand Down Expand Up @@ -137,6 +178,14 @@ func (b *BlockBuilder) WithTime(time Timestamp) *BlockBuilder {
return b
}

func (b *BlockBuilder) WithDuration(duration time.Duration) *BlockBuilder {
if duration < 0 {
duration = 0
}
b.block.Duration = uint64(duration.Nanoseconds())
return b
}

func (b *BlockBuilder) WithDifficulty(difficulty uint64) *BlockBuilder {
b.block.Difficulty = difficulty
return b
Expand Down
2 changes: 2 additions & 0 deletions tests/address_access_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ func TestAddressAccess(t *testing.T) {
"origin": contract.TouchOrigin,
"access list": func(ops *bind.TransactOpts) (*types.Transaction, error) {
ops.GasPrice = nil // < transactions with gas price cannot have access list
ops.GasFeeCap = big.NewInt(1e12)
ops.GasTipCap = big.NewInt(1000)
ops.AccessList = types.AccessList{
{Address: someAccountAddress},
}
Expand Down
Loading
Loading