mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
beacon/engine, eth/catalyst, miner: add GetInclusionListV1
This commit is contained in:
parent
1dab670156
commit
7ce64f1d65
5 changed files with 307 additions and 7 deletions
|
|
@ -46,3 +46,23 @@ func (inclusionList *InclusionList) UnmarshalJSON(input []byte) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InclusionListToTransactions(inclusionList InclusionList) []*Transaction {
|
||||
var txs []*Transaction
|
||||
for _, encTx := range inclusionList {
|
||||
var tx Transaction
|
||||
if err := tx.UnmarshalBinary(encTx); err != nil {
|
||||
continue // Skip invalid transactions
|
||||
}
|
||||
txs = append(txs, &tx)
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func TransactionsToInclusionList(txs []*Transaction) InclusionList {
|
||||
var enc = make([][]byte, len(txs))
|
||||
for i, tx := range txs {
|
||||
enc[i], _ = tx.MarshalBinary()
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,42 @@ const (
|
|||
beaconUpdateWarnFrequency = 5 * time.Minute
|
||||
)
|
||||
|
||||
// All methods provided over the engine endpoint.
|
||||
var caps = []string{
|
||||
"engine_forkchoiceUpdatedV1",
|
||||
"engine_forkchoiceUpdatedV2",
|
||||
"engine_forkchoiceUpdatedV3",
|
||||
"engine_forkchoiceUpdatedWithWitnessV1",
|
||||
"engine_forkchoiceUpdatedWithWitnessV2",
|
||||
"engine_forkchoiceUpdatedWithWitnessV3",
|
||||
"engine_exchangeTransitionConfigurationV1",
|
||||
"engine_getPayloadV1",
|
||||
"engine_getPayloadV2",
|
||||
"engine_getPayloadV3",
|
||||
"engine_getPayloadV4",
|
||||
"engine_getPayloadV5",
|
||||
"engine_getBlobsV1",
|
||||
"engine_getBlobsV2",
|
||||
"engine_getInclusionListV1",
|
||||
"engine_newPayloadV1",
|
||||
"engine_newPayloadV2",
|
||||
"engine_newPayloadV3",
|
||||
"engine_newPayloadV4",
|
||||
"engine_newPayloadWithWitnessV1",
|
||||
"engine_newPayloadWithWitnessV2",
|
||||
"engine_newPayloadWithWitnessV3",
|
||||
"engine_newPayloadWithWitnessV4",
|
||||
"engine_executeStatelessPayloadV1",
|
||||
"engine_executeStatelessPayloadV2",
|
||||
"engine_executeStatelessPayloadV3",
|
||||
"engine_executeStatelessPayloadV4",
|
||||
"engine_getPayloadBodiesByHashV1",
|
||||
"engine_getPayloadBodiesByHashV2",
|
||||
"engine_getPayloadBodiesByRangeV1",
|
||||
"engine_getPayloadBodiesByRangeV2",
|
||||
"engine_getClientVersionV1",
|
||||
}
|
||||
|
||||
var (
|
||||
// Number of blobs requested via getBlobsV2
|
||||
getBlobsRequestedCounter = metrics.NewRegisteredCounter("engine/getblobs/requested", nil)
|
||||
|
|
@ -100,6 +136,7 @@ type ConsensusAPI struct {
|
|||
|
||||
remoteBlocks *headerQueue // Cache of remote payloads received
|
||||
localBlocks *payloadQueue // Cache of local payloads generated
|
||||
localInclusionLists *inclusionListQueue // Cache of inclusion list generated
|
||||
|
||||
// The forkchoice update and new payload method require us to return the
|
||||
// latest valid hash in an invalid chain. To support that return, we need
|
||||
|
|
@ -152,6 +189,7 @@ func newConsensusAPIWithoutHeartbeat(eth *eth.Ethereum) *ConsensusAPI {
|
|||
eth: eth,
|
||||
remoteBlocks: newHeaderQueue(),
|
||||
localBlocks: newPayloadQueue(),
|
||||
localInclusionLists: newInclusionListQueue(),
|
||||
invalidBlocksHits: make(map[common.Hash]int),
|
||||
invalidTipsets: make(map[common.Hash]*types.Header),
|
||||
}
|
||||
|
|
@ -589,6 +627,25 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) GetInclusionListV1(parentHash common.Hash) (types.InclusionList, error) {
|
||||
if inclusionList := api.localInclusionLists.get(parentHash); inclusionList != nil {
|
||||
return inclusionList, nil
|
||||
}
|
||||
|
||||
args := &miner.BuildInclusionListArgs{
|
||||
Parent: parentHash,
|
||||
}
|
||||
inclusionList, err := api.eth.Miner().BuildInclusionList(args)
|
||||
if err != nil {
|
||||
log.Error("Failed to build inclusion list", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
api.localInclusionLists.put(parentHash, inclusionList)
|
||||
|
||||
return inclusionList, nil
|
||||
}
|
||||
|
||||
// Helper for NewPayload* methods.
|
||||
var invalidStatus = engine.PayloadStatusV1{Status: engine.INVALID}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ const maxTrackedPayloads = 10
|
|||
// limit.
|
||||
const maxTrackedHeaders = 96
|
||||
|
||||
// maxTrackedInclusionLists is the maximum number of inclusion lists the execution
|
||||
// engine tracks before evicting old ones.
|
||||
const maxTrackedInclusionLists = 8
|
||||
|
||||
// payloadQueueItem represents an id->payload tuple to store until it's retrieved
|
||||
// or evicted.
|
||||
type payloadQueueItem struct {
|
||||
|
|
@ -156,3 +160,54 @@ func (q *headerQueue) get(hash common.Hash) *types.Header {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// inclusionListQueueItem represents an hash->inclusionList tuple to store until it's retrieved
|
||||
// or evicted.
|
||||
type inclusionListQueueItem struct {
|
||||
parentHash common.Hash
|
||||
inclusionList types.InclusionList
|
||||
}
|
||||
|
||||
// inclusionListQueue tracks the latest handful of constructed inclusion lists to be retrieved
|
||||
// by the beacon chain if inclusion list production is requested.
|
||||
type inclusionListQueue struct {
|
||||
inclusionLists []*inclusionListQueueItem
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// newinclusionListQueue creates a pre-initialized queue with a fixed number of slots
|
||||
// all containing empty items.
|
||||
func newInclusionListQueue() *inclusionListQueue {
|
||||
return &inclusionListQueue{
|
||||
inclusionLists: make([]*inclusionListQueueItem, maxTrackedInclusionLists),
|
||||
}
|
||||
}
|
||||
|
||||
// put inserts a new inclusion list into the queue at the given parent hash that
|
||||
// the inclusion list is built upon.
|
||||
func (q *inclusionListQueue) put(parentHash common.Hash, inclusionList types.InclusionList) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
copy(q.inclusionLists[1:], q.inclusionLists)
|
||||
q.inclusionLists[0] = &inclusionListQueueItem{
|
||||
parentHash,
|
||||
inclusionList,
|
||||
}
|
||||
}
|
||||
|
||||
// get retrieves a previously stored inclusion list item or nil if it does not exist.
|
||||
func (q *inclusionListQueue) get(parentHash common.Hash) types.InclusionList {
|
||||
q.lock.RLock()
|
||||
defer q.lock.RUnlock()
|
||||
|
||||
for _, item := range q.inclusionLists {
|
||||
if item == nil {
|
||||
return nil // no more items
|
||||
}
|
||||
if item.parentHash == parentHash {
|
||||
return item.inclusionList
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
49
miner/inclusion_list_building.go
Normal file
49
miner/inclusion_list_building.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package miner
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// BuildInclusionListArgs contains the provided parameters for building inclusion list.
|
||||
type BuildInclusionListArgs struct {
|
||||
Parent common.Hash // The hash of the parent block to build the inclusion list upon
|
||||
}
|
||||
|
||||
func (miner *Miner) BuildInclusionList(args *BuildInclusionListArgs) (types.InclusionList, error) {
|
||||
genParams := &generateParams{
|
||||
timestamp: uint64(time.Now().Unix()),
|
||||
forceTime: false,
|
||||
parentHash: args.Parent,
|
||||
coinbase: miner.config.PendingFeeRecipient,
|
||||
random: common.Hash{},
|
||||
withdrawals: []*types.Withdrawal{},
|
||||
beaconRoot: nil,
|
||||
noTxs: false,
|
||||
}
|
||||
env, err := miner.prepareWork(genParams, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := miner.fillTransactions(nil, env); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inclusionListTxs := make([]*types.Transaction, 0)
|
||||
inclusionListSize := uint64(0)
|
||||
|
||||
for _, tx := range env.txs {
|
||||
if inclusionListSize+tx.Size() > params.MaxBytesPerInclusionList {
|
||||
continue
|
||||
}
|
||||
|
||||
inclusionListTxs = append(inclusionListTxs, tx)
|
||||
inclusionListSize += tx.Size()
|
||||
}
|
||||
|
||||
return types.TransactionsToInclusionList(inclusionListTxs), nil
|
||||
}
|
||||
119
miner/inclusion_list_building_test.go
Normal file
119
miner/inclusion_list_building_test.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package miner
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testMinerBackend implements miner's Backend interface.
|
||||
type testMinerBackend struct {
|
||||
txPool *txpool.TxPool
|
||||
chain *core.BlockChain
|
||||
}
|
||||
|
||||
func (b *testMinerBackend) BlockChain() *core.BlockChain { return b.chain }
|
||||
func (b *testMinerBackend) TxPool() *txpool.TxPool { return b.txPool }
|
||||
|
||||
func newTestMinerBackend(t *testing.T, genesis *core.Genesis, engine consensus.Engine) *testMinerBackend {
|
||||
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), genesis, engine, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("core.NewBlockChain failed: %v", err)
|
||||
}
|
||||
|
||||
testTxPoolConfig := legacypool.DefaultConfig
|
||||
pool := legacypool.New(testTxPoolConfig, chain)
|
||||
txpool, err := txpool.New(testTxPoolConfig.PriceLimit, chain, []txpool.SubPool{pool})
|
||||
if err != nil {
|
||||
t.Fatalf("txpool.New failed: %v", err)
|
||||
}
|
||||
|
||||
return &testMinerBackend{
|
||||
chain: chain,
|
||||
txPool: txpool,
|
||||
}
|
||||
}
|
||||
|
||||
func newTestMiner(eth *testMinerBackend) *Miner {
|
||||
return New(eth, DefaultConfig, eth.chain.Engine())
|
||||
}
|
||||
|
||||
func newPendingTransactions(fromPrivateKey *ecdsa.PrivateKey, toAddress common.Address) []*types.Transaction {
|
||||
signer := types.LatestSigner(params.TestChainConfig)
|
||||
|
||||
tx1 := types.MustSignNewTx(fromPrivateKey, signer, &types.AccessListTx{
|
||||
ChainID: params.TestChainConfig.ChainID,
|
||||
Nonce: 0,
|
||||
To: &toAddress,
|
||||
Value: big.NewInt(1000),
|
||||
Gas: params.TxGas,
|
||||
GasPrice: big.NewInt(params.InitialBaseFee),
|
||||
})
|
||||
pendingTxs = append(pendingTxs, tx1)
|
||||
|
||||
tx2 := types.MustSignNewTx(fromPrivateKey, signer, &types.LegacyTx{
|
||||
Nonce: 1,
|
||||
To: &toAddress,
|
||||
Value: big.NewInt(1000),
|
||||
Gas: params.TxGas,
|
||||
GasPrice: big.NewInt(params.InitialBaseFee),
|
||||
})
|
||||
pendingTxs = append(pendingTxs, tx2)
|
||||
|
||||
return pendingTxs
|
||||
}
|
||||
|
||||
func TestBuildInclusionList(t *testing.T) {
|
||||
var (
|
||||
// Test accounts
|
||||
testBankKey, _ = crypto.GenerateKey()
|
||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
||||
testBankFunds = big.NewInt(1000000000000000000)
|
||||
|
||||
testUserKey, _ = crypto.GenerateKey()
|
||||
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
|
||||
|
||||
// Test genesis and consensus engine
|
||||
testGenesis = &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
|
||||
}
|
||||
testEngine = ethash.NewFaker()
|
||||
)
|
||||
|
||||
// Initialize miner and backend.
|
||||
eth := newTestMinerBackend(t, testGenesis, testEngine)
|
||||
miner := newTestMiner(eth)
|
||||
|
||||
// Add pending transactions to the pool.
|
||||
pendingTxs := newPendingTransactions(testBankKey, testUserAddress)
|
||||
eth.txPool.Add(pendingTxs, true)
|
||||
|
||||
// Build inclusion list.
|
||||
args := &BuildInclusionListArgs{
|
||||
Parent: eth.chain.CurrentBlock().Hash(),
|
||||
}
|
||||
inclusionList, err := miner.BuildInclusionList(args)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build inclusion list %v", err)
|
||||
}
|
||||
|
||||
// Verify inclusion list size.
|
||||
inclusionListSize := uint64(0)
|
||||
for _, tx := range inclusionList {
|
||||
inclusionListSize += uint64(len(tx))
|
||||
}
|
||||
require.LessOrEqual(t, inclusionListSize, params.MaxBytesPerInclusionList)
|
||||
}
|
||||
Loading…
Reference in a new issue