84565dc899
closes #29475, replaces #29657, #30104 Fixes two issues. First is a deadlock where the txpool attempts to reorg, but can't complete because there are no readers left for the new txs subscription. Second, resolves a problem with on demand mode where txs may be left pending when there are more pending txs than block space. Co-authored-by: Martin Holst Swende <martin@swende.se>
106 lines
3.2 KiB
Go
106 lines
3.2 KiB
Go
// Copyright 2023 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package catalyst
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
)
|
|
|
|
// simulatedBeaconAPI provides a RPC API for SimulatedBeacon.
|
|
type simulatedBeaconAPI struct {
|
|
sim *SimulatedBeacon
|
|
}
|
|
|
|
// newSimulatedBeaconAPI returns an instance of simulatedBeaconAPI with a
|
|
// buffered commit channel. If period is zero, it starts a goroutine to handle
|
|
// new tx events.
|
|
func newSimulatedBeaconAPI(sim *SimulatedBeacon) *simulatedBeaconAPI {
|
|
api := &simulatedBeaconAPI{sim: sim}
|
|
if sim.period == 0 {
|
|
// mine on demand if period is set to 0
|
|
go api.loop()
|
|
}
|
|
return api
|
|
}
|
|
|
|
// loop is the main loop for the API when it's running in period = 0 mode. It
|
|
// ensures that block production is triggered as soon as a new withdrawal or
|
|
// transaction is received.
|
|
func (a *simulatedBeaconAPI) loop() {
|
|
var (
|
|
newTxs = make(chan core.NewTxsEvent)
|
|
newWxs = make(chan newWithdrawalsEvent)
|
|
newTxsSub = a.sim.eth.TxPool().SubscribeTransactions(newTxs, true)
|
|
newWxsSub = a.sim.withdrawals.subscribe(newWxs)
|
|
doCommit = make(chan struct{}, 1)
|
|
)
|
|
defer newTxsSub.Unsubscribe()
|
|
defer newWxsSub.Unsubscribe()
|
|
|
|
// A background thread which signals to the simulator when to commit
|
|
// based on messages over doCommit.
|
|
go func() {
|
|
for range doCommit {
|
|
a.sim.Commit()
|
|
a.sim.eth.TxPool().Sync()
|
|
|
|
// It's worth noting that in case a tx ends up in the pool listed as
|
|
// "executable", but for whatever reason the miner does not include it in
|
|
// a block -- maybe the miner is enforcing a higher tip than the pool --
|
|
// this code will spinloop.
|
|
for {
|
|
if executable, _ := a.sim.eth.TxPool().Stats(); executable == 0 {
|
|
break
|
|
}
|
|
a.sim.Commit()
|
|
}
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-a.sim.shutdownCh:
|
|
close(doCommit)
|
|
return
|
|
case <-newWxs:
|
|
select {
|
|
case doCommit <- struct{}{}:
|
|
default:
|
|
}
|
|
case <-newTxs:
|
|
select {
|
|
case doCommit <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddWithdrawal adds a withdrawal to the pending queue.
|
|
func (a *simulatedBeaconAPI) AddWithdrawal(ctx context.Context, withdrawal *types.Withdrawal) error {
|
|
return a.sim.withdrawals.add(withdrawal)
|
|
}
|
|
|
|
// SetFeeRecipient sets the fee recipient for block building purposes.
|
|
func (a *simulatedBeaconAPI) SetFeeRecipient(ctx context.Context, feeRecipient common.Address) {
|
|
a.sim.setFeeRecipient(feeRecipient)
|
|
}
|