@@ -30,6 +30,7 @@ import (
3030 "github.com/ethereum/go-ethereum/core/types"
3131 "github.com/ethereum/go-ethereum/crypto/kzg4844"
3232 "github.com/ethereum/go-ethereum/eth"
33+ "github.com/ethereum/go-ethereum/event"
3334 "github.com/ethereum/go-ethereum/log"
3435 "github.com/ethereum/go-ethereum/node"
3536 "github.com/ethereum/go-ethereum/params"
@@ -41,36 +42,47 @@ const devEpochLength = 32
4142// withdrawalQueue implements a FIFO queue which holds withdrawals that are
4243// pending inclusion.
4344type withdrawalQueue struct {
44- pending chan * types.Withdrawal
45+ pending types.Withdrawals
46+ mu sync.Mutex
47+ feed event.Feed
48+ subs event.SubscriptionScope
4549}
4650
51+ type newWithdrawalsEvent struct { Withdrawals types.Withdrawals }
52+
4753// add queues a withdrawal for future inclusion.
48- func (w * withdrawalQueue ) add (withdrawal * types.Withdrawal ) error {
49- select {
50- case w .pending <- withdrawal :
51- break
52- default :
53- return errors . New ( " withdrawal queue full" )
54- }
54+ func (w * withdrawalQueue ) Add (withdrawal * types.Withdrawal ) error {
55+ w . mu . Lock ()
56+ defer w .mu . Unlock ()
57+
58+ w . pending = append ( w . pending , withdrawal )
59+ w . feed . Send ( newWithdrawalsEvent {types. Withdrawals { withdrawal }} )
60+
5561 return nil
5662}
5763
58- // gatherPending returns a number of queued withdrawals up to a maximum count.
59- func (w * withdrawalQueue ) gatherPending (maxCount int ) []* types.Withdrawal {
60- withdrawals := []* types.Withdrawal {}
61- for {
62- select {
63- case withdrawal := <- w .pending :
64- withdrawals = append (withdrawals , withdrawal )
65- if len (withdrawals ) == maxCount {
66- return withdrawals
67- }
68- default :
69- return withdrawals
70- }
71- }
64+ // pop dequeues the specified number of withdrawals from the queue.
65+ func (w * withdrawalQueue ) Pop (count int ) types.Withdrawals {
66+ w .mu .Lock ()
67+ defer w .mu .Unlock ()
68+
69+ count = min (count , len (w .pending ))
70+ popped := w .pending [0 :count ]
71+ w .pending = w .pending [count :]
72+
73+ return popped
7274}
7375
76+ // subscribe allows a listener to be updated when new withdrawals are added to
77+ // the queue.
78+ func (w * withdrawalQueue ) Subscribe (ch chan <- newWithdrawalsEvent ) event.Subscription {
79+ sub := w .feed .Subscribe (ch )
80+ return w .subs .Track (sub )
81+ }
82+
83+ // SimulatedBeacon drives an Ethereum instance as if it were a real beacon
84+ // client. It can run in period mode where it mines a new block every period
85+ // (seconds) or on every transaction via Commit, Fork and AdjustTime.
7486type SimulatedBeacon struct {
7587 shutdownCh chan struct {}
7688 eth * eth.Ethereum
@@ -86,10 +98,6 @@ type SimulatedBeacon struct {
8698}
8799
88100// NewSimulatedBeacon constructs a new simulated beacon chain.
89- // Period sets the period in which blocks should be produced.
90- //
91- // - If period is set to 0, a block is produced on every transaction.
92- // via Commit, Fork and AdjustTime.
93101func NewSimulatedBeacon (period uint64 , eth * eth.Ethereum ) (* SimulatedBeacon , error ) {
94102 block := eth .BlockChain ().CurrentBlock ()
95103 current := engine.ForkchoiceStateV1 {
@@ -112,7 +120,6 @@ func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, err
112120 engineAPI : engineAPI ,
113121 lastBlockTime : block .Time ,
114122 curForkchoiceState : current ,
115- withdrawals : withdrawalQueue {make (chan * types.Withdrawal , 20 )},
116123 }, nil
117124}
118125
@@ -171,6 +178,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
171178 if fcResponse == engine .STATUS_SYNCING {
172179 return errors .New ("chain rewind prevented invocation of payload creation" )
173180 }
181+
174182 envelope , err := c .engineAPI .getPayload (* fcResponse .PayloadID , true )
175183 if err != nil {
176184 return err
@@ -223,8 +231,7 @@ func (c *SimulatedBeacon) loop() {
223231 case <- c .shutdownCh :
224232 return
225233 case <- timer .C :
226- withdrawals := c .withdrawals .gatherPending (10 )
227- if err := c .sealBlock (withdrawals , uint64 (time .Now ().Unix ())); err != nil {
234+ if err := c .sealBlock (c .withdrawals .Pop (10 ), uint64 (time .Now ().Unix ())); err != nil {
228235 log .Warn ("Error performing sealing work" , "err" , err )
229236 } else {
230237 timer .Reset (time .Second * time .Duration (c .period ))
@@ -260,7 +267,7 @@ func (c *SimulatedBeacon) setCurrentState(headHash, finalizedHash common.Hash) {
260267
261268// Commit seals a block on demand.
262269func (c * SimulatedBeacon ) Commit () common.Hash {
263- withdrawals := c .withdrawals .gatherPending (10 )
270+ withdrawals := c .withdrawals .Pop (10 )
264271 if err := c .sealBlock (withdrawals , uint64 (time .Now ().Unix ())); err != nil {
265272 log .Warn ("Error performing sealing work" , "err" , err )
266273 }
@@ -301,12 +308,14 @@ func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error {
301308 if parent == nil {
302309 return errors .New ("parent not found" )
303310 }
304- withdrawals := c .withdrawals .gatherPending (10 )
311+ withdrawals := c .withdrawals .Pop (10 )
305312 return c .sealBlock (withdrawals , parent .Time + uint64 (adjustment / time .Second ))
306313}
307314
315+ // RegisterSimulatedBeaconAPIs registers the simulated beacon's API with the
316+ // stack.
308317func RegisterSimulatedBeaconAPIs (stack * node.Node , sim * SimulatedBeacon ) {
309- api := & api {sim }
318+ api := & simulatedBeaconAPI {sim : sim , doCommit : make ( chan struct {}, 1 ) }
310319 if sim .period == 0 {
311320 // mine on demand if period is set to 0
312321 go api .loop ()
0 commit comments