mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
cmd, eth: introduce BlockPeriod type for flexible beacon chain simulation
This commit is contained in:
parent
34aac1d756
commit
45b5de3edc
4 changed files with 52 additions and 25 deletions
|
|
@ -218,7 +218,11 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||||
|
|
||||||
if ctx.IsSet(utils.DeveloperFlag.Name) {
|
if ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||||
// Start dev mode.
|
// Start dev mode.
|
||||||
simBeacon, err := catalyst.NewSimulatedBeacon(ctx.Uint64(utils.DeveloperPeriodFlag.Name), eth)
|
p, err := catalyst.ParseBlockPeriod(ctx.String(utils.DeveloperFlag.Name))
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("failed to parse developer mode block interval: %v", err)
|
||||||
|
}
|
||||||
|
simBeacon, err := catalyst.NewSimulatedBeacon(p, eth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("failed to register dev mode catalyst service: %v", err)
|
utils.Fatalf("failed to register dev mode catalyst service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,25 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const devEpochLength = 32
|
const (
|
||||||
|
devEpochLength = 32
|
||||||
|
// ManualPeriod is a special value for the block interval that disables automatic block production, and
|
||||||
|
// requires manual invocation of the Commit, Fork and AdjustTime methods.
|
||||||
|
ManualPeriod = BlockPeriod(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
type BlockPeriod time.Duration
|
||||||
|
|
||||||
|
func ParseBlockPeriod(period string) (BlockPeriod, error) {
|
||||||
|
if period == "0" {
|
||||||
|
return ManualPeriod, nil
|
||||||
|
}
|
||||||
|
d, err := time.ParseDuration(period)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return BlockPeriod(d), nil
|
||||||
|
}
|
||||||
|
|
||||||
// withdrawalQueue implements a FIFO queue which holds withdrawals that are
|
// withdrawalQueue implements a FIFO queue which holds withdrawals that are
|
||||||
// pending inclusion.
|
// pending inclusion.
|
||||||
|
|
@ -74,23 +92,26 @@ func (w *withdrawalQueue) gatherPending(maxCount int) []*types.Withdrawal {
|
||||||
type SimulatedBeacon struct {
|
type SimulatedBeacon struct {
|
||||||
shutdownCh chan struct{}
|
shutdownCh chan struct{}
|
||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
period uint64
|
period BlockPeriod
|
||||||
withdrawals withdrawalQueue
|
withdrawals withdrawalQueue
|
||||||
|
|
||||||
feeRecipient common.Address
|
feeRecipient common.Address
|
||||||
feeRecipientLock sync.Mutex // lock gates concurrent access to the feeRecipient
|
feeRecipientLock sync.Mutex // lock gates concurrent access to the feeRecipient
|
||||||
|
|
||||||
engineAPI *ConsensusAPI
|
engineAPI *ConsensusAPI
|
||||||
curForkchoiceState engine.ForkchoiceStateV1
|
curForkChoiceState engine.ForkchoiceStateV1
|
||||||
lastBlockTime uint64
|
lastBlockTime uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimulatedBeacon constructs a new simulated beacon chain.
|
// NewSimulatedBeacon constructs a new simulated beacon chain.
|
||||||
// Period sets the period in which blocks should be produced.
|
// Period sets the period in which blocks should be produced.
|
||||||
//
|
// Args:
|
||||||
// - If period is set to 0, a block is produced on every transaction.
|
// - period: the block production period, use ManualPeriod to disable automatic block production and require manual invocation of Commit, Fork and AdjustTime.
|
||||||
// via Commit, Fork and AdjustTime.
|
// - eth: the Ethereum service to which the simulated beacon should be attached.
|
||||||
func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, error) {
|
// Returns:
|
||||||
|
// - a new simulated beacon chain.
|
||||||
|
// - an error if the beacon could not be created.
|
||||||
|
func NewSimulatedBeacon(period BlockPeriod, eth *eth.Ethereum) (*SimulatedBeacon, error) {
|
||||||
block := eth.BlockChain().CurrentBlock()
|
block := eth.BlockChain().CurrentBlock()
|
||||||
current := engine.ForkchoiceStateV1{
|
current := engine.ForkchoiceStateV1{
|
||||||
HeadBlockHash: block.Hash(),
|
HeadBlockHash: block.Hash(),
|
||||||
|
|
@ -99,7 +120,7 @@ func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, err
|
||||||
}
|
}
|
||||||
engineAPI := newConsensusAPIWithoutHeartbeat(eth)
|
engineAPI := newConsensusAPIWithoutHeartbeat(eth)
|
||||||
|
|
||||||
// if genesis block, send forkchoiceUpdated to trigger transition to PoS
|
// if genesis block, send forkChoiceUpdated to trigger transition to PoS
|
||||||
if block.Number.Sign() == 0 {
|
if block.Number.Sign() == 0 {
|
||||||
if _, err := engineAPI.ForkchoiceUpdatedV2(current, nil); err != nil {
|
if _, err := engineAPI.ForkchoiceUpdatedV2(current, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -111,7 +132,7 @@ func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, err
|
||||||
shutdownCh: make(chan struct{}),
|
shutdownCh: make(chan struct{}),
|
||||||
engineAPI: engineAPI,
|
engineAPI: engineAPI,
|
||||||
lastBlockTime: block.Time,
|
lastBlockTime: block.Time,
|
||||||
curForkchoiceState: current,
|
curForkChoiceState: current,
|
||||||
withdrawals: withdrawalQueue{make(chan *types.Withdrawal, 20)},
|
withdrawals: withdrawalQueue{make(chan *types.Withdrawal, 20)},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +145,7 @@ func (c *SimulatedBeacon) setFeeRecipient(feeRecipient common.Address) {
|
||||||
|
|
||||||
// Start invokes the SimulatedBeacon life-cycle function in a goroutine.
|
// Start invokes the SimulatedBeacon life-cycle function in a goroutine.
|
||||||
func (c *SimulatedBeacon) Start() error {
|
func (c *SimulatedBeacon) Start() error {
|
||||||
if c.period == 0 {
|
if c.period == ManualPeriod {
|
||||||
// if period is set to 0, do not mine at all
|
// if period is set to 0, do not mine at all
|
||||||
// this is used in the simulated backend where blocks
|
// this is used in the simulated backend where blocks
|
||||||
// are explicitly mined via Commit, AdjustTime and Fork
|
// are explicitly mined via Commit, AdjustTime and Fork
|
||||||
|
|
@ -151,14 +172,14 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
||||||
c.feeRecipientLock.Unlock()
|
c.feeRecipientLock.Unlock()
|
||||||
|
|
||||||
// Reset to CurrentBlock in case of the chain was rewound
|
// Reset to CurrentBlock in case of the chain was rewound
|
||||||
if header := c.eth.BlockChain().CurrentBlock(); c.curForkchoiceState.HeadBlockHash != header.Hash() {
|
if header := c.eth.BlockChain().CurrentBlock(); c.curForkChoiceState.HeadBlockHash != header.Hash() {
|
||||||
finalizedHash := c.finalizedBlockHash(header.Number.Uint64())
|
finalizedHash := c.finalizedBlockHash(header.Number.Uint64())
|
||||||
c.setCurrentState(header.Hash(), *finalizedHash)
|
c.setCurrentState(header.Hash(), *finalizedHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
var random [32]byte
|
var random [32]byte
|
||||||
rand.Read(random[:])
|
rand.Read(random[:])
|
||||||
fcResponse, err := c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, &engine.PayloadAttributes{
|
fcResponse, err := c.engineAPI.forkchoiceUpdated(c.curForkChoiceState, &engine.PayloadAttributes{
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
SuggestedFeeRecipient: feeRecipient,
|
SuggestedFeeRecipient: feeRecipient,
|
||||||
Withdrawals: withdrawals,
|
Withdrawals: withdrawals,
|
||||||
|
|
@ -208,7 +229,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
|
||||||
c.setCurrentState(payload.BlockHash, finalizedHash)
|
c.setCurrentState(payload.BlockHash, finalizedHash)
|
||||||
|
|
||||||
// Mark the block containing the payload as canonical
|
// Mark the block containing the payload as canonical
|
||||||
if _, err = c.engineAPI.ForkchoiceUpdatedV2(c.curForkchoiceState, nil); err != nil {
|
if _, err = c.engineAPI.ForkchoiceUpdatedV2(c.curForkChoiceState, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.lastBlockTime = payload.Timestamp
|
c.lastBlockTime = payload.Timestamp
|
||||||
|
|
@ -227,7 +248,7 @@ func (c *SimulatedBeacon) loop() {
|
||||||
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
||||||
log.Warn("Error performing sealing work", "err", err)
|
log.Warn("Error performing sealing work", "err", err)
|
||||||
} else {
|
} else {
|
||||||
timer.Reset(time.Second * time.Duration(c.period))
|
timer.Reset(time.Duration(c.period))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -249,9 +270,9 @@ func (c *SimulatedBeacon) finalizedBlockHash(number uint64) *common.Hash {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setCurrentState sets the current forkchoice state
|
// setCurrentState sets the current fork choice state.
|
||||||
func (c *SimulatedBeacon) setCurrentState(headHash, finalizedHash common.Hash) {
|
func (c *SimulatedBeacon) setCurrentState(headHash, finalizedHash common.Hash) {
|
||||||
c.curForkchoiceState = engine.ForkchoiceStateV1{
|
c.curForkChoiceState = engine.ForkchoiceStateV1{
|
||||||
HeadBlockHash: headHash,
|
HeadBlockHash: headHash,
|
||||||
SafeBlockHash: headHash,
|
SafeBlockHash: headHash,
|
||||||
FinalizedBlockHash: finalizedHash,
|
FinalizedBlockHash: finalizedHash,
|
||||||
|
|
@ -304,7 +325,7 @@ func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error {
|
||||||
|
|
||||||
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
||||||
api := &api{sim}
|
api := &api{sim}
|
||||||
if sim.period == 0 {
|
if sim.period == ManualPeriod {
|
||||||
// mine on demand if period is set to 0
|
// mine on demand if period is set to 0
|
||||||
go api.loop()
|
go api.loop()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis) (*node.
|
||||||
t.Fatal("can't create eth service:", err)
|
t.Fatal("can't create eth service:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
simBeacon, err := NewSimulatedBeacon(1, ethservice)
|
simBeacon, err := NewSimulatedBeacon(BlockPeriod(time.Second), ethservice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("can't create simulated beacon:", err)
|
t.Fatal("can't create simulated beacon:", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // this should never happen
|
panic(err) // this should never happen
|
||||||
}
|
}
|
||||||
sim, err := newWithNode(stack, ðConf, 0)
|
sim, err := newWithNode(stack, ðConf, catalyst.ManualPeriod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // this should never happen
|
panic(err) // this should never happen
|
||||||
}
|
}
|
||||||
|
|
@ -105,17 +105,19 @@ func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config,
|
||||||
|
|
||||||
// newWithNode sets up a simulated backend on an existing node. The provided node
|
// newWithNode sets up a simulated backend on an existing node. The provided node
|
||||||
// must not be started and will be started by this method.
|
// must not be started and will be started by this method.
|
||||||
func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backend, error) {
|
func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod catalyst.BlockPeriod) (*Backend, error) {
|
||||||
backend, err := eth.New(stack, conf)
|
backend, err := eth.New(stack, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Register the filter system
|
// Register the filter system
|
||||||
filterSystem := filters.NewFilterSystem(backend.APIBackend, filters.Config{})
|
filterSystem := filters.NewFilterSystem(backend.APIBackend, filters.Config{})
|
||||||
stack.RegisterAPIs([]rpc.API{{
|
stack.RegisterAPIs([]rpc.API{
|
||||||
Namespace: "eth",
|
{
|
||||||
Service: filters.NewFilterAPI(filterSystem, false),
|
Namespace: "eth",
|
||||||
}})
|
Service: filters.NewFilterAPI(filterSystem, false),
|
||||||
|
},
|
||||||
|
})
|
||||||
// Start the node
|
// Start the node
|
||||||
if err := stack.Start(); err != nil {
|
if err := stack.Start(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue