create separate functions for IL verification for NewInclusionList engine API

This commit is contained in:
Manav Darji 2023-09-10 22:35:24 +05:30
parent 5f4dd02074
commit 068ce5c6d6
6 changed files with 118 additions and 26 deletions

View file

@ -64,7 +64,7 @@ type ExecutableData struct {
BlobGasUsed *uint64 `json:"blobGasUsed"`
ExcessBlobGas *uint64 `json:"excessBlobGas"`
Summary []*InclusionListEntry `json:"summary"`
Summary []*types.InclusionListEntry `json:"summary"`
}
// JSON type overrides for executableData.
@ -280,14 +280,7 @@ type ExecutionPayloadBodyV1 struct {
Withdrawals []*types.Withdrawal `json:"withdrawals"`
}
// InclusionListV1 is used in the response to GetInclusionListV1 and request to NewInclusionListV1
type InclusionListV1 struct {
Summary []*InclusionListEntry `json:"summary"`
Transactions []*types.Transaction `json:"transactions"`
}
// InclusionListEntry denotes a summary entry of (address, gasLimit)
type InclusionListEntry struct {
address common.Address `json:"address"`
gasLimit uint32 `json:"gasLimit"` // TODO(manav): change to uint8
type VerifiableInclusionList struct {
ParentHash common.Hash `json:"parentHash"`
InclusionList types.InclusionList `json:"inclusionList"`
}

View file

@ -2587,3 +2587,7 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load())
}
func (bc *BlockChain) VerifyInclusionList(list types.InclusionList, parent *types.Header, state *state.StateDB) bool {
return verifyInclusionList(list, parent, state, bc.Config())
}

70
core/inclusion_list.go Normal file
View file

@ -0,0 +1,70 @@
package core
import (
"math/big"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
// IL constants taken from specs here: https://github.com/potuz/consensus-specs/blob/a6c55576de059a1b2cae69848dee827f6e26e72d/specs/_features/epbs/beacon-chain.md#execution
const (
MAX_TRANSACTIONS_PER_INCLUSION_LIST = 16
MAX_GAS_PER_INCLUSION_LIST = 2_097_152 // 2^21
)
// VerifyInclusionList verifies the properties of the inclusion list and the
// transactions in it against the given `state` object.
//
// The verification involves actual execution of the transactions so
// it's the caller's responsibility to send a copy of the state object.
func verifyInclusionList(list types.InclusionList, parent *types.Header, state *state.StateDB, config *params.ChainConfig) bool {
// Validate few basic things first in the inclusion list.
if len(list.Summary) != len(list.Transactions) {
log.Debug("Inclusion list summary and transactions length mismatch")
return false
}
if len(list.Summary) > MAX_TRANSACTIONS_PER_INCLUSION_LIST {
log.Debug("Inclusion list exceeds maximum number of transactions")
return false
}
// As IL will be included in the next block, calculate the current block's base fee.
// As the current block's payload isn't revealed yet (due to ePBS), calculate
// it from parent block.
currentBaseFee := eip1559.CalcBaseFee(config, parent)
// 1.125 * currentBaseFee
gasFeeThreshold := new(big.Float).Mul(new(big.Float).SetFloat64(0.125), new(big.Float).SetInt(currentBaseFee))
// Prepare the signer object
signer := types.LatestSigner(config)
// Verify if the summary and transactions match. Also check if the txs
// have at least 12.5% higher `maxFeePerGas` than parent block's base fee.
for i, summary := range list.Summary {
tx := list.Transactions[i]
from, err := types.Sender(signer, tx)
if err != nil {
log.Debug("Failed to get sender from transaction", "err", err)
return false
}
if summary.Address != from {
log.Debug("Inclusion list summary and transaction address mismatch")
return false
}
// tx.GasFeeCap > 1.125 * parent.BaseFee
if new(big.Float).SetInt(tx.GasFeeCap()).Cmp(gasFeeThreshold) < 1 {
return false
}
}
// TODO: Execute txs. Mostly all required params are available except evm context.
return false
}

View file

@ -173,18 +173,6 @@ type Body struct {
Withdrawals []*Withdrawal `rlp:"optional"`
}
// InclusionList represents pairs of transaction summary and the transaction data itself
type InclusionList struct {
Summary []*InclusionListEntry `json:"summary"`
Transactions []*Transaction `json:"transactions"`
}
// InclusionListEntry denotes a summary entry of (address, gasLimit)
type InclusionListEntry struct {
Address common.Address `json:"address"`
GasLimit uint32 `json:"gasLimit"` // TODO(manav): change to uint8
}
// Block represents an Ethereum block.
//
// Note the Block type tries to be 'immutable', and contains certain caches that rely

View file

@ -0,0 +1,15 @@
package types
import "github.com/ethereum/go-ethereum/common"
// InclusionList represents pairs of transaction summary and the transaction data itself
type InclusionList struct {
Summary []*InclusionListEntry `json:"summary"`
Transactions []*Transaction `json:"transactions"`
}
// InclusionListEntry denotes a summary entry of (address, gasLimit)
type InclusionListEntry struct {
Address common.Address `json:"address"`
GasLimit uint32 `json:"gasLimit"` // TODO(manav): change to uint8
}

View file

@ -161,7 +161,7 @@ func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI {
// GetInclusionListV1 returns an inclusion list which contains summary + list of transactions
// which are valid for the current slot.
func (api *ConsensusAPI) GetInclusionListV1() (*engine.InclusionListV1, error) {
func (api *ConsensusAPI) GetInclusionListV1() (*types.InclusionList, error) {
// TODO(manav): Do we check here if we're on correct fork or are fully synced? If not
// we might end up delivering wrong IL. Other will reject it though but do we want to
// risk it?
@ -171,8 +171,30 @@ func (api *ConsensusAPI) GetInclusionListV1() (*engine.InclusionListV1, error) {
// NewInclusionListV1 validates whether an inclusion list (summary + txs) is
// correct for the current state or not.
func (api *ConsensusAPI) NewInclusionListV1() bool {
return false
func (api *ConsensusAPI) NewInclusionListV1(params engine.VerifiableInclusionList) bool {
log.Trace("Engine API request received", "method", "NewInclusionListV1")
if params.ParentHash == (common.Hash{}) {
log.Warn("Inclusion list verification requested with zero parent hash")
return false
}
// Check if we have parent block available or not. If not, reject the
// inclusion list.
// (TODO): Do we need to trigger a sync here? I believe not.
parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash)
if parent == nil {
log.Warn("Inclusion list verification requested with unknown parent", "hash", params.ParentHash)
return false
}
// Fetch the parent state to verify the inclusion list.
state, err := api.eth.BlockChain().StateAt(parent.Root())
if err != nil {
log.Warn("Unable to fetch parent block state, skipping verification", "err", err)
return false
}
return api.eth.BlockChain().VerifyInclusionList(params.InclusionList, parent.Header(), state.Copy())
}
// TODO: update/define executable params