Skip to content

Commit

Permalink
Merge pull request ethereum#93 from ethereum-optimism/fix-pending-block
Browse files Browse the repository at this point in the history
miner,eth: latest block as pending block on rollup
  • Loading branch information
protolambda authored May 26, 2023
2 parents bfdfd9b + 482400d commit bdab05c
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 3 deletions.
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ var (
utils.RollupHistoricalRPCFlag,
utils.RollupHistoricalRPCTimeoutFlag,
utils.RollupDisableTxPoolGossipFlag,
utils.RollupComputePendingBlock,
configFileFlag,
}, utils.NetworkFlags, utils.DatabasePathFlags)

Expand Down
8 changes: 8 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,11 @@ var (
Usage: "Disable transaction pool gossip.",
Category: flags.RollupCategory,
}
RollupComputePendingBlock = &cli.BoolFlag{
Name: "rollup.computependingblock",
Usage: "By default the pending block equals the latest block to save resources and not leak txs from the tx-pool, this flag enables computing of the pending block from the tx-pool instead.",
Category: flags.RollupCategory,
}

// Metrics flags
MetricsEnabledFlag = &cli.BoolFlag{
Expand Down Expand Up @@ -1696,6 +1701,9 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
if ctx.IsSet(MinerNewPayloadTimeout.Name) {
cfg.NewPayloadTimeout = ctx.Duration(MinerNewPayloadTimeout.Name)
}
if ctx.IsSet(RollupComputePendingBlock.Name) {
cfg.RollupComputePendingBlock = ctx.Bool(RollupComputePendingBlock.Name)
}
}

func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
Expand Down
6 changes: 6 additions & 0 deletions eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb := api.eth.miner.Pending()
if stateDb == nil {
return state.Dump{}, errors.New("no pending state")
}
return stateDb.RawDump(opts), nil
}
var header *types.Header
Expand Down Expand Up @@ -350,6 +353,9 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb = api.eth.miner.Pending()
if stateDb == nil {
return state.IteratorDump{}, errors.New("no pending state")
}
} else {
var header *types.Header
if number == rpc.LatestBlockNumber {
Expand Down
6 changes: 5 additions & 1 deletion eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
// Pending state is only known by the miner
if number == rpc.PendingBlockNumber {
block, state := b.eth.miner.Pending()
return state, block.Header(), nil
if block != nil {
return state, block.Header(), nil
} else {
number = rpc.LatestBlockNumber
}
}
// Otherwise resolve the block number and return its state
header, err := b.HeaderByNumber(ctx, number)
Expand Down
4 changes: 2 additions & 2 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ func (s *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockN
header, err := s.b.HeaderByNumber(ctx, number)
if header != nil && err == nil {
response := s.rpcMarshalHeader(ctx, header)
if number == rpc.PendingBlockNumber {
if number == rpc.PendingBlockNumber && s.b.ChainConfig().Optimism == nil { // don't remove info if optimism
// Pending header need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
Expand Down Expand Up @@ -803,7 +803,7 @@ func (s *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNu
block, err := s.b.BlockByNumber(ctx, number)
if block != nil && err == nil {
response, err := s.rpcMarshalBlock(ctx, block, true, fullTx)
if err == nil && number == rpc.PendingBlockNumber {
if err == nil && number == rpc.PendingBlockNumber && s.b.ChainConfig().Optimism == nil { // don't remove info if optimism
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
Expand Down
2 changes: 2 additions & 0 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type Config struct {
Noverify bool // Disable remote mining solution verification(only useful in ethash).

NewPayloadTimeout time.Duration // The maximum time allowance for creating a new payload

RollupComputePendingBlock bool // Compute the pending block from tx-pool, instead of copying the latest-block
}

// DefaultConfig contains default settings for miner.
Expand Down
28 changes: 28 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ func (w *worker) enablePreseal() {

// pending returns the pending state and corresponding block.
func (w *worker) pending() (*types.Block, *state.StateDB) {
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
return nil, nil // when not computing the pending block, there is never a pending state
}
// return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
Expand All @@ -395,6 +398,12 @@ func (w *worker) pending() (*types.Block, *state.StateDB) {

// pendingBlock returns pending block.
func (w *worker) pendingBlock() *types.Block {
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
// For compatibility when not computing a pending block, we serve the latest block as "pending"
headHeader := w.eth.BlockChain().CurrentHeader()
headBlock := w.eth.BlockChain().GetBlock(headHeader.Hash(), headHeader.Number.Uint64())
return headBlock
}
// return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
Expand All @@ -403,6 +412,9 @@ func (w *worker) pendingBlock() *types.Block {

// pendingBlockAndReceipts returns pending block and corresponding receipts.
func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) {
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
return nil, nil // when not computing the pending block, there are no pending receipts, and thus no pending logs
}
// return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
Expand Down Expand Up @@ -458,6 +470,19 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t
// newWorkLoop is a standalone goroutine to submit new sealing work upon received events.
func (w *worker) newWorkLoop(recommit time.Duration) {
defer w.wg.Done()
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
for { // do not update the pending-block, instead drain work without doing it, to keep producers from blocking.
select {
case <-w.startCh:
case <-w.chainHeadCh:
case <-w.resubmitIntervalCh:
case <-w.resubmitAdjustCh:
case <-w.exitCh:
return
}
}
}

var (
interrupt *atomic.Int32
minRecommit = recommit // minimal resubmit interval specified by user.
Expand Down Expand Up @@ -620,6 +645,9 @@ func (w *worker) mainLoop() {
}

case ev := <-w.txsCh:
if w.chainConfig.Optimism != nil && !w.config.RollupComputePendingBlock {
continue // don't update the pending-block snapshot if we are not computing the pending block
}
// Apply transactions to the pending state if we're not sealing
//
// Note all transactions received may not be continuous with transactions
Expand Down

0 comments on commit bdab05c

Please sign in to comment.