diff --git a/eth/backend.go b/eth/backend.go index 9c83e6495..216e44da0 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -495,6 +495,9 @@ func (s *Ethereum) Stop() error { if s.interopRPC != nil { s.interopRPC.Close() } + if s.miner != nil { + s.miner.Close() + } // Clean shutdown marker as the last thing before closing db s.shutdownTracker.Stop() diff --git a/miner/miner.go b/miner/miner.go index 164cbab83..7e99ec5e3 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -91,10 +91,14 @@ type Miner struct { pendingMu sync.Mutex // Lock protects the pending block backend Backend + + lifeCtxCancel context.CancelFunc + lifeCtx context.Context } // New creates a new miner with provided config. func New(eth Backend, config Config, engine consensus.Engine) *Miner { + ctx, cancel := context.WithCancel(context.Background()) return &Miner{ backend: eth, config: &config, @@ -103,6 +107,9 @@ func New(eth Backend, config Config, engine consensus.Engine) *Miner { txpool: eth.TxPool(), chain: eth.BlockChain(), pending: &pending{}, + // To interrupt background tasks that may be attached to external processes + lifeCtxCancel: cancel, + lifeCtx: ctx, } } @@ -195,3 +202,7 @@ func (miner *Miner) getPending() *newPayloadResult { miner.pending.update(header.Hash(), ret) return ret } + +func (miner *Miner) Close() { + miner.lifeCtxCancel() +} diff --git a/miner/payload_building.go b/miner/payload_building.go index d9653659e..2dd574c15 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -113,8 +113,8 @@ type Payload struct { } // newPayload initializes the payload object. -func newPayload(empty *types.Block, witness *stateless.Witness, id engine.PayloadID) *Payload { - rpcCtx, rpcCancel := context.WithCancel(context.Background()) +func newPayload(lifeCtx context.Context, empty *types.Block, witness *stateless.Witness, id engine.PayloadID) *Payload { + rpcCtx, rpcCancel := context.WithCancel(lifeCtx) payload := &Payload{ id: id, empty: empty, @@ -312,7 +312,7 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs, witness bool) (*Payload if empty.err != nil { return nil, empty.err } - payload := newPayload(empty.block, empty.witness, args.Id()) + payload := newPayload(miner.lifeCtx, empty.block, empty.witness, args.Id()) // make sure to make it appear as full, otherwise it will wait indefinitely for payload building to complete. payload.full = empty.block payload.fullFees = empty.fees @@ -341,7 +341,7 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs, witness bool) (*Payload return nil, err } - payload := newPayload(nil, nil, args.Id()) + payload := newPayload(miner.lifeCtx, nil, nil, args.Id()) // set shared interrupt fullParams.interrupt = payload.interrupt fullParams.rpcCtx = payload.rpcCtx @@ -388,6 +388,8 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs, witness bool) (*Payload var lastDuration time.Duration for { select { + case <-miner.lifeCtx.Done(): + stopReason = "miner-shutdown" case <-timer.C: // We have to prioritize the stop signal because the recommit timer // might have fired while stop also got closed. diff --git a/miner/worker.go b/miner/worker.go index 84b9c2dc2..7982f36cc 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -451,6 +451,9 @@ func (miner *Miner) checkInterop(ctx context.Context, tx *types.Transaction, fai if failed { return nil // failed txs don't persist any logs } + if tx.Rejected() { + return errors.New("transaction was previously rejected") + } b, ok := miner.backend.(BackendWithInterop) if !ok { return fmt.Errorf("cannot mine interop txs without interop backend, got backend type %T", miner.backend)