miner, cmd, eth: require explicit etherbase address (#26413)

This change introduces a breaking change to miner.etherbase is configured.

Previously, users did not need to explicitly set the  etherbase address via flag, since 'first' local account was used as etherbase automatically. This change removes the  "default first account" feature.

In Proof-of-stake world, the fee recipient address is provided by CL, and not configured in Geth any more - meaning that miner.etherbase is mostly for legacy networks(pow, clique networks etc).
This commit is contained in:
rjl493456442 2023-01-21 00:26:01 +08:00 committed by GitHub
parent 4f4a25d79f
commit 2b44ef5f93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 75 deletions

@ -146,7 +146,7 @@ func startLightServer(t *testing.T) *gethrpc {
t.Logf("Importing keys to geth") t.Logf("Importing keys to geth")
runGeth(t, "account", "import", "--datadir", datadir, "--password", "./testdata/password.txt", "--lightkdf", "./testdata/key.prv").WaitExit() runGeth(t, "account", "import", "--datadir", datadir, "--password", "./testdata/password.txt", "--lightkdf", "./testdata/key.prv").WaitExit()
account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105" account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4") server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--miner.etherbase=0x02f0d131f1f97aef08aec6e3291b957d9efe7105", "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4")
return server return server
} }

@ -538,8 +538,7 @@ var (
} }
MinerEtherbaseFlag = &cli.StringFlag{ MinerEtherbaseFlag = &cli.StringFlag{
Name: "miner.etherbase", Name: "miner.etherbase",
Usage: "Public address for block mining rewards (default = first account)", Usage: "0x prefixed public address for block mining rewards",
Value: "0",
Category: flags.MinerCategory, Category: flags.MinerCategory,
} }
MinerExtraDataFlag = &cli.StringFlag{ MinerExtraDataFlag = &cli.StringFlag{
@ -1343,25 +1342,15 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
return accs[index], nil return accs[index], nil
} }
// setEtherbase retrieves the etherbase either from the directly specified // setEtherbase retrieves the etherbase from the directly specified command line flags.
// command line flags or from the keystore if CLI indexed. func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) {
func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config) {
// Extract the current etherbase
var etherbase string
if ctx.IsSet(MinerEtherbaseFlag.Name) { if ctx.IsSet(MinerEtherbaseFlag.Name) {
etherbase = ctx.String(MinerEtherbaseFlag.Name) b, err := hexutil.Decode(ctx.String(MinerEtherbaseFlag.Name))
} if err != nil || len(b) != common.AddressLength {
// Convert the etherbase into an address and configure it log.Info("Failed to decode etherbase", "err", err)
if etherbase != "" { return
if ks != nil {
account, err := MakeAddress(ks, etherbase)
if err != nil {
Fatalf("Invalid miner etherbase: %v", err)
}
cfg.Miner.Etherbase = account.Address
} else {
Fatalf("No etherbase configured")
} }
cfg.Miner.Etherbase = common.BytesToAddress(b)
} }
} }
@ -1739,11 +1728,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(LightServeFlag.Name) && ctx.Uint64(TxLookupLimitFlag.Name) != 0 { if ctx.IsSet(LightServeFlag.Name) && ctx.Uint64(TxLookupLimitFlag.Name) != 0 {
log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited") log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited")
} }
var ks *keystore.KeyStore setEtherbase(ctx, cfg)
if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 {
ks = keystores[0].(*keystore.KeyStore)
}
setEtherbase(ctx, ks, cfg)
setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light") setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light")
setTxPool(ctx, &cfg.TxPool) setTxPool(ctx, &cfg.TxPool)
setEthash(ctx, cfg) setEthash(ctx, cfg)
@ -1921,6 +1906,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// when we're definitely concerned with only one account. // when we're definitely concerned with only one account.
passphrase = list[0] passphrase = list[0]
} }
// Unlock the developer account by local keystore.
var ks *keystore.KeyStore
if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 {
ks = keystores[0].(*keystore.KeyStore)
}
if ks == nil {
Fatalf("Keystore is not available")
}
// setEtherbase has been called above, configuring the miner address from command line flags. // setEtherbase has been called above, configuring the miner address from command line flags.
if cfg.Miner.Etherbase != (common.Address{}) { if cfg.Miner.Etherbase != (common.Address{}) {
developer = accounts.Account{Address: cfg.Miner.Etherbase} developer = accounts.Account{Address: cfg.Miner.Etherbase}

@ -329,18 +329,6 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
if etherbase != (common.Address{}) { if etherbase != (common.Address{}) {
return etherbase, nil return etherbase, nil
} }
if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
etherbase := accounts[0].Address
s.lock.Lock()
s.etherbase = etherbase
s.lock.Unlock()
log.Info("Etherbase automatically configured", "address", etherbase)
return etherbase, nil
}
}
return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
} }
@ -456,7 +444,7 @@ func (s *Ethereum) StartMining(threads int) error {
// introduced to speed sync times. // introduced to speed sync times.
atomic.StoreUint32(&s.handler.acceptTxs, 1) atomic.StoreUint32(&s.handler.acceptTxs, 1)
go s.miner.Start(eb) go s.miner.Start()
} }
return nil return nil
} }

@ -45,7 +45,7 @@ type Backend interface {
// Config is the configuration parameters of mining. // Config is the configuration parameters of mining.
type Config struct { type Config struct {
Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards (default = first account) Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards
Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash). Notify []string `toml:",omitempty"` // HTTP URL list to be notified of new work packages (only useful in ethash).
NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages NotifyFull bool `toml:",omitempty"` // Notify with pending block headers instead of work packages
ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner
@ -73,25 +73,24 @@ var DefaultConfig = Config{
// Miner creates blocks and searches for proof-of-work values. // Miner creates blocks and searches for proof-of-work values.
type Miner struct { type Miner struct {
mux *event.TypeMux mux *event.TypeMux
worker *worker eth Backend
coinbase common.Address engine consensus.Engine
eth Backend exitCh chan struct{}
engine consensus.Engine startCh chan struct{}
exitCh chan struct{} stopCh chan struct{}
startCh chan common.Address worker *worker
stopCh chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
} }
func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(header *types.Header) bool) *Miner { func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(header *types.Header) bool) *Miner {
miner := &Miner{ miner := &Miner{
eth: eth,
mux: mux, mux: mux,
eth: eth,
engine: engine, engine: engine,
exitCh: make(chan struct{}), exitCh: make(chan struct{}),
startCh: make(chan common.Address), startCh: make(chan struct{}),
stopCh: make(chan struct{}), stopCh: make(chan struct{}),
worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true), worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true),
} }
@ -138,20 +137,17 @@ func (miner *Miner) update() {
case downloader.FailedEvent: case downloader.FailedEvent:
canStart = true canStart = true
if shouldStart { if shouldStart {
miner.SetEtherbase(miner.coinbase)
miner.worker.start() miner.worker.start()
} }
case downloader.DoneEvent: case downloader.DoneEvent:
canStart = true canStart = true
if shouldStart { if shouldStart {
miner.SetEtherbase(miner.coinbase)
miner.worker.start() miner.worker.start()
} }
// Stop reacting to downloader events // Stop reacting to downloader events
events.Unsubscribe() events.Unsubscribe()
} }
case addr := <-miner.startCh: case <-miner.startCh:
miner.SetEtherbase(addr)
if canStart { if canStart {
miner.worker.start() miner.worker.start()
} }
@ -166,8 +162,8 @@ func (miner *Miner) update() {
} }
} }
func (miner *Miner) Start(coinbase common.Address) { func (miner *Miner) Start() {
miner.startCh <- coinbase miner.startCh <- struct{}{}
} }
func (miner *Miner) Stop() { func (miner *Miner) Stop() {
@ -223,7 +219,6 @@ func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
} }
func (miner *Miner) SetEtherbase(addr common.Address) { func (miner *Miner) SetEtherbase(addr common.Address) {
miner.coinbase = addr
miner.worker.setEtherbase(addr) miner.worker.setEtherbase(addr)
} }

@ -86,7 +86,8 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent)
func TestMiner(t *testing.T) { func TestMiner(t *testing.T) {
miner, mux, cleanup := createMiner(t) miner, mux, cleanup := createMiner(t)
defer cleanup(false) defer cleanup(false)
miner.Start(common.HexToAddress("0x12345"))
miner.Start()
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
// Start the downloader // Start the downloader
mux.Post(downloader.StartEvent{}) mux.Post(downloader.StartEvent{})
@ -114,7 +115,8 @@ func TestMiner(t *testing.T) {
func TestMinerDownloaderFirstFails(t *testing.T) { func TestMinerDownloaderFirstFails(t *testing.T) {
miner, mux, cleanup := createMiner(t) miner, mux, cleanup := createMiner(t)
defer cleanup(false) defer cleanup(false)
miner.Start(common.HexToAddress("0x12345"))
miner.Start()
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
// Start the downloader // Start the downloader
mux.Post(downloader.StartEvent{}) mux.Post(downloader.StartEvent{})
@ -146,7 +148,8 @@ func TestMinerDownloaderFirstFails(t *testing.T) {
func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
miner, mux, cleanup := createMiner(t) miner, mux, cleanup := createMiner(t)
defer cleanup(false) defer cleanup(false)
miner.Start(common.HexToAddress("0x12345"))
miner.Start()
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
// Start the downloader // Start the downloader
mux.Post(downloader.StartEvent{}) mux.Post(downloader.StartEvent{})
@ -159,7 +162,7 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
miner.Stop() miner.Stop()
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
miner.Start(common.HexToAddress("0x678910")) miner.Start()
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
miner.Stop() miner.Stop()
@ -170,13 +173,13 @@ func TestStartWhileDownload(t *testing.T) {
miner, mux, cleanup := createMiner(t) miner, mux, cleanup := createMiner(t)
defer cleanup(false) defer cleanup(false)
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
miner.Start(common.HexToAddress("0x12345")) miner.Start()
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
// Stop the downloader and wait for the update loop to run // Stop the downloader and wait for the update loop to run
mux.Post(downloader.StartEvent{}) mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
// Starting the miner after the downloader should not work // Starting the miner after the downloader should not work
miner.Start(common.HexToAddress("0x12345")) miner.Start()
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
} }
@ -184,7 +187,7 @@ func TestStartStopMiner(t *testing.T) {
miner, _, cleanup := createMiner(t) miner, _, cleanup := createMiner(t)
defer cleanup(false) defer cleanup(false)
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
miner.Start(common.HexToAddress("0x12345")) miner.Start()
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
miner.Stop() miner.Stop()
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
@ -194,7 +197,7 @@ func TestCloseMiner(t *testing.T) {
miner, _, cleanup := createMiner(t) miner, _, cleanup := createMiner(t)
defer cleanup(true) defer cleanup(true)
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
miner.Start(common.HexToAddress("0x12345")) miner.Start()
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
// Terminate the miner and wait for the update loop to run // Terminate the miner and wait for the update loop to run
miner.Close() miner.Close()
@ -206,21 +209,21 @@ func TestCloseMiner(t *testing.T) {
func TestMinerSetEtherbase(t *testing.T) { func TestMinerSetEtherbase(t *testing.T) {
miner, mux, cleanup := createMiner(t) miner, mux, cleanup := createMiner(t)
defer cleanup(false) defer cleanup(false)
// Start with a 'bad' mining address miner.Start()
miner.Start(common.HexToAddress("0xdead"))
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
// Start the downloader // Start the downloader
mux.Post(downloader.StartEvent{}) mux.Post(downloader.StartEvent{})
waitForMiningState(t, miner, false) waitForMiningState(t, miner, false)
// Now user tries to configure proper mining address // Now user tries to configure proper mining address
miner.Start(common.HexToAddress("0x1337")) miner.Start()
// Stop the downloader and wait for the update loop to run // Stop the downloader and wait for the update loop to run
mux.Post(downloader.DoneEvent{}) mux.Post(downloader.DoneEvent{})
waitForMiningState(t, miner, true) waitForMiningState(t, miner, true)
// The miner should now be using the good address
if got, exp := miner.coinbase, common.HexToAddress("0x1337"); got != exp { coinbase := common.HexToAddress("0xdeedbeef")
t.Fatalf("Wrong coinbase, got %x expected %x", got, exp) miner.SetEtherbase(coinbase)
if addr := miner.worker.etherbase(); addr != coinbase {
t.Fatalf("Unexpected etherbase want %x got %x", coinbase, addr)
} }
} }

@ -276,12 +276,14 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
chainConfig: chainConfig, chainConfig: chainConfig,
engine: engine, engine: engine,
eth: eth, eth: eth,
mux: mux,
chain: eth.BlockChain(), chain: eth.BlockChain(),
mux: mux,
isLocalBlock: isLocalBlock, isLocalBlock: isLocalBlock,
localUncles: make(map[common.Hash]*types.Block), localUncles: make(map[common.Hash]*types.Block),
remoteUncles: make(map[common.Hash]*types.Block), remoteUncles: make(map[common.Hash]*types.Block),
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth), unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth),
coinbase: config.Etherbase,
extra: config.ExtraData,
pendingTasks: make(map[common.Hash]*task), pendingTasks: make(map[common.Hash]*task),
txsCh: make(chan core.NewTxsEvent, txChanSize), txsCh: make(chan core.NewTxsEvent, txChanSize),
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
@ -290,8 +292,8 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
getWorkCh: make(chan *getWorkReq), getWorkCh: make(chan *getWorkReq),
taskCh: make(chan *task), taskCh: make(chan *task),
resultCh: make(chan *types.Block, resultQueueSize), resultCh: make(chan *types.Block, resultQueueSize),
exitCh: make(chan struct{}),
startCh: make(chan struct{}, 1), startCh: make(chan struct{}, 1),
exitCh: make(chan struct{}),
resubmitIntervalCh: make(chan time.Duration), resubmitIntervalCh: make(chan time.Duration),
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
} }
@ -340,6 +342,13 @@ func (w *worker) setEtherbase(addr common.Address) {
w.coinbase = addr w.coinbase = addr
} }
// etherbase retrieves the configured etherbase address.
func (w *worker) etherbase() common.Address {
w.mu.RLock()
defer w.mu.RUnlock()
return w.coinbase
}
func (w *worker) setGasCeil(ceil uint64) { func (w *worker) setGasCeil(ceil uint64) {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
@ -1114,11 +1123,11 @@ func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) {
// Set the coinbase if the worker is running or it's required // Set the coinbase if the worker is running or it's required
var coinbase common.Address var coinbase common.Address
if w.isRunning() { if w.isRunning() {
if w.coinbase == (common.Address{}) { coinbase = w.etherbase()
if coinbase == (common.Address{}) {
log.Error("Refusing to mine without etherbase") log.Error("Refusing to mine without etherbase")
return return
} }
coinbase = w.coinbase // Use the preset address as the fee recipient
} }
work, err := w.prepareWork(&generateParams{ work, err := w.prepareWork(&generateParams{
timestamp: uint64(timestamp), timestamp: uint64(timestamp),