feat(core): decode basefee params from block.extraData (#290)

* feat(core): decode basefee params from `block.extraData`

* feat: add `CalcBaseFeeOntake`
This commit is contained in:
David 2024-07-29 14:07:53 +08:00 committed by GitHub
parent 8ff161fb39
commit 83564ba6fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 99 additions and 38 deletions

View file

@ -15,13 +15,12 @@ var _ = (*blockMetadataMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (b BlockMetadata) MarshalJSON() ([]byte, error) {
type BlockMetadata struct {
Beneficiary common.Address `json:"beneficiary" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
MixHash common.Hash `json:"mixHash" gencodec:"required"`
TxList hexutil.Bytes `json:"txList" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"`
BasefeeSharingPctg uint8 `json:"basefeeSharingPctg"`
Beneficiary common.Address `json:"beneficiary" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
MixHash common.Hash `json:"mixHash" gencodec:"required"`
TxList hexutil.Bytes `json:"txList" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"`
}
var enc BlockMetadata
enc.Beneficiary = b.Beneficiary
@ -30,20 +29,18 @@ func (b BlockMetadata) MarshalJSON() ([]byte, error) {
enc.MixHash = b.MixHash
enc.TxList = b.TxList
enc.ExtraData = b.ExtraData
enc.BasefeeSharingPctg = b.BasefeeSharingPctg
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (b *BlockMetadata) UnmarshalJSON(input []byte) error {
type BlockMetadata struct {
Beneficiary *common.Address `json:"beneficiary" gencodec:"required"`
GasLimit *uint64 `json:"gasLimit" gencodec:"required"`
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
MixHash *common.Hash `json:"mixHash" gencodec:"required"`
TxList *hexutil.Bytes `json:"txList" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"`
BasefeeSharingPctg *uint8 `json:"basefeeSharingPctg"`
Beneficiary *common.Address `json:"beneficiary" gencodec:"required"`
GasLimit *uint64 `json:"gasLimit" gencodec:"required"`
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
MixHash *common.Hash `json:"mixHash" gencodec:"required"`
TxList *hexutil.Bytes `json:"txList" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"`
}
var dec BlockMetadata
if err := json.Unmarshal(input, &dec); err != nil {
@ -73,8 +70,5 @@ func (b *BlockMetadata) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'extraData' for BlockMetadata")
}
b.ExtraData = dec.ExtraData
if dec.BasefeeSharingPctg != nil {
b.BasefeeSharingPctg = *dec.BasefeeSharingPctg
}
return nil
}

View file

@ -70,9 +70,8 @@ type BlockMetadata struct {
MixHash common.Hash `json:"mixHash" gencodec:"required"`
// Extra fields required in taiko-geth.
TxList []byte `json:"txList" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"`
BasefeeSharingPctg uint8 `json:"basefeeSharingPctg"`
TxList []byte `json:"txList" gencodec:"required"`
ExtraData []byte `json:"extraData" gencodec:"required"`
}
// CHANGE(taiko): JSON type overrides for BlockMetadata.

View file

@ -93,3 +93,38 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
return math.BigMax(baseFee, common.Big0)
}
}
// CHANGE(taiko): CalcBaseFeeOntake calculates the basefee of the an ontake block header.
func CalcBaseFeeOntake(config *params.ChainConfig, parent *types.Header, parentGasTarget uint64) *big.Int {
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {
return new(big.Int).Set(parent.BaseFee)
}
var (
num = new(big.Int)
denom = new(big.Int)
)
if parent.GasUsed > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
// max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parent.GasUsed - parentGasTarget)
num.Mul(num, parent.BaseFee)
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))
baseFeeDelta := math.BigMax(num, common.Big1)
return num.Add(parent.BaseFee, baseFeeDelta)
} else {
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
// max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
num.SetUint64(parentGasTarget - parent.GasUsed)
num.Mul(num, parent.BaseFee)
num.Div(num, denom.SetUint64(parentGasTarget))
num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))
baseFee := num.Sub(parent.BaseFee, num)
return math.BigMax(baseFee, common.Big0)
}
}

View file

@ -138,10 +138,6 @@ type StateDB struct {
// Testing hooks
onCommit func(states *triestate.Set) // Hook invoked when commit is performed
// CHANGE(taiko): basefeeSharingPctg of the basefee will be sent to the block.coinbase,
// the remaining will be sent to the treasury address.
BasefeeSharingPctg uint8
}
// New creates a new state from a given trie.

View file

@ -169,8 +169,12 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
if err != nil {
return nil, err
}
// CHANGE(taiko): set `BasefeeSharingPctg` for the current message.
msg.BasefeeSharingPctg = statedb.BasefeeSharingPctg
// CHANGE(taiko): decode the basefeeSharingPctg config from the extradata, and
// add it to the Message, if its an ontake block.
if config.IsOntake(header.Number) {
basefeeSharingPctg, _ := DecodeOntakeExtraData(header.Extra)
msg.BasefeeSharingPctg = basefeeSharingPctg
}
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author)
txContext := NewEVMTxContext(msg)

View file

@ -537,3 +537,22 @@ func (st *StateTransition) getTreasuryAddress() common.Address {
suffix,
)
}
// DecodeOntakeExtraData decodes an ontake block's extradata,
// returns basefeeSharingPctg and blockGasTargetMillion configurations.
func DecodeOntakeExtraData(extradata []byte) (uint8, uint8) {
// Convert []byte to *big.Int
extra := new(big.Int).SetBytes(extradata)
// Define the masks.
blockGasTargetMillionMask := big.NewInt(0xFF) // 8 bits mask for _blockGasTargetMillion
basefeeSharingPctgMask := big.NewInt(0xFF) // 8 bits mask for _basefeeSharingPctg
// Extract _blockGasTargetMillion.
blockGasTargetMillion := new(big.Int).And(extra, blockGasTargetMillionMask).Uint64()
// Shift right by 8 bits to get the _basefeeSharingPctg part.
basefeeSharingPctg := new(big.Int).Rsh(extra, 8).And(basefeeSharingPctgMask, basefeeSharingPctgMask).Uint64()
return uint8(basefeeSharingPctg), uint8(blockGasTargetMillion)
}

View file

@ -9,6 +9,12 @@ import (
"github.com/ethereum/go-ethereum/params"
)
var (
InternalDevnetOntakeBlock = new(big.Int).SetUint64(374_400)
HeklaOntakeBlock = new(big.Int).SetUint64(720_000)
MainnetOntakeBlock = new(big.Int).SetUint64(374_400)
)
// TaikoGenesisBlock returns the Taiko network genesis block configs.
func TaikoGenesisBlock(networkID uint64) *Genesis {
chainConfig := params.TaikoChainConfig
@ -17,9 +23,11 @@ func TaikoGenesisBlock(networkID uint64) *Genesis {
switch networkID {
case params.TaikoMainnetNetworkID.Uint64():
chainConfig.ChainID = params.TaikoMainnetNetworkID
chainConfig.OntakeBlock = MainnetOntakeBlock
allocJSON = taikoGenesis.MainnetGenesisAllocJSON
case params.TaikoInternalL2ANetworkID.Uint64():
chainConfig.ChainID = params.TaikoInternalL2ANetworkID
chainConfig.OntakeBlock = InternalDevnetOntakeBlock
allocJSON = taikoGenesis.InternalL2AGenesisAllocJSON
case params.TaikoInternalL2BNetworkID.Uint64():
chainConfig.ChainID = params.TaikoInternalL2BNetworkID
@ -44,9 +52,11 @@ func TaikoGenesisBlock(networkID uint64) *Genesis {
allocJSON = taikoGenesis.KatlaGenesisAllocJSON
case params.HeklaNetworkID.Uint64():
chainConfig.ChainID = params.HeklaNetworkID
chainConfig.OntakeBlock = HeklaOntakeBlock
allocJSON = taikoGenesis.HeklaGenesisAllocJSON
default:
chainConfig.ChainID = params.TaikoInternalL2ANetworkID
chainConfig.OntakeBlock = InternalDevnetOntakeBlock
allocJSON = taikoGenesis.InternalL2AGenesisAllocJSON
}

View file

@ -382,11 +382,6 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
if payloadAttributes != nil {
// CHANGE(taiko): create a L2 block by Taiko protocol.
if isTaiko {
if payloadAttributes.BlockMetadata.BasefeeSharingPctg > 100 {
return valid(nil), engine.InvalidPayloadAttributes.With(
fmt.Errorf("invalid basefeeSharingPctg %d", payloadAttributes.BlockMetadata.BasefeeSharingPctg),
)
}
// No need to check payloadAttribute here, because all its fields are
// marked as required.
block, err := api.eth.Miner().SealBlockWith(

View file

@ -144,8 +144,7 @@ func (w *worker) sealBlockWith(
}
if len(txs) == 0 {
// A L2 block needs to have have at least one `V1TaikoL2.anchor` or
// `V1TaikoL2.invalidateBlock` transaction.
// A L2 block needs to have have at least one `TaikoL2.anchor` / `TaikoL2.anchorV2`.
return nil, fmt.Errorf("too less transactions in the block")
}
@ -170,7 +169,6 @@ func (w *worker) sealBlockWith(
defer env.discard()
env.header.GasLimit = blkMeta.GasLimit
env.state.BasefeeSharingPctg = blkMeta.BasefeeSharingPctg
// Commit transactions.
gasLimit := env.header.GasLimit

View file

@ -987,8 +987,13 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
}
// Set baseFee and GasLimit if we are on an EIP-1559 chain
if w.chainConfig.IsLondon(header.Number) {
if w.chainConfig.Taiko && genParams.baseFeePerGas != nil {
header.BaseFee = genParams.baseFeePerGas
if w.chainConfig.Taiko {
if !w.chainConfig.IsOntake(header.Number) {
header.BaseFee = genParams.baseFeePerGas
} else {
_, blockGasTargetMillion := core.DecodeOntakeExtraData(header.Extra)
header.BaseFee = eip1559.CalcBaseFeeOntake(w.chainConfig, parent, uint64(blockGasTargetMillion)*1_000_000)
}
} else {
header.BaseFee = eip1559.CalcBaseFee(w.chainConfig, parent)
if !w.chainConfig.IsLondon(parent.Number) {

View file

@ -378,7 +378,8 @@ type ChainConfig struct {
Clique *CliqueConfig `json:"clique,omitempty"`
// CHANGE(taiko): Taiko network flag.
Taiko bool `json:"taiko"`
Taiko bool `json:"taiko"`
OntakeBlock *big.Int `json:"ontakeBlock,omitempty"` // Ontake switch block (nil = no fork, 0 = already activated)
}
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
@ -595,6 +596,11 @@ func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)
}
// CHANGE(taiko): IsOntake returns whether num is either equal to the Ontake fork block or greater.
func (c *ChainConfig) IsOntake(num *big.Int) bool {
return isBlockForked(c.OntakeBlock, num)
}
// CheckCompatible checks whether scheduled fork transitions have been imported
// with a mismatching chain configuration.
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError {