github.com/klaytn/klaytn@v1.12.1/work/work.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2014 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from miner/miner.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package work
    22  
    23  import (
    24  	"fmt"
    25  	"io"
    26  	"math/big"
    27  	"sync/atomic"
    28  
    29  	"github.com/klaytn/klaytn/accounts"
    30  	"github.com/klaytn/klaytn/blockchain"
    31  	"github.com/klaytn/klaytn/blockchain/state"
    32  	"github.com/klaytn/klaytn/blockchain/types"
    33  	"github.com/klaytn/klaytn/blockchain/vm"
    34  	"github.com/klaytn/klaytn/common"
    35  	"github.com/klaytn/klaytn/consensus"
    36  	"github.com/klaytn/klaytn/datasync/downloader"
    37  	"github.com/klaytn/klaytn/event"
    38  	"github.com/klaytn/klaytn/log"
    39  	"github.com/klaytn/klaytn/params"
    40  	"github.com/klaytn/klaytn/rlp"
    41  	"github.com/klaytn/klaytn/snapshot"
    42  	"github.com/klaytn/klaytn/storage/database"
    43  )
    44  
    45  var logger = log.NewModuleLogger(log.Work)
    46  
    47  //go:generate mockgen -destination=work/mocks/txpool_mock.go -package=mocks github.com/klaytn/klaytn/work TxPool
    48  // TxPool is an interface of blockchain.TxPool used by ProtocolManager and Backend.
    49  type TxPool interface {
    50  	// HandleTxMsg should add the given transactions to the pool.
    51  	HandleTxMsg(types.Transactions)
    52  
    53  	// Pending should return pending transactions.
    54  	// The slice should be modifiable by the caller.
    55  	Pending() (map[common.Address]types.Transactions, error)
    56  
    57  	CachedPendingTxsByCount(count int) types.Transactions
    58  
    59  	// SubscribeNewTxsEvent should return an event subscription of
    60  	// NewTxsEvent and send events to the given channel.
    61  	SubscribeNewTxsEvent(chan<- blockchain.NewTxsEvent) event.Subscription
    62  
    63  	GetPendingNonce(addr common.Address) uint64
    64  	AddLocal(tx *types.Transaction) error
    65  	GasPrice() *big.Int
    66  	SetGasPrice(price *big.Int)
    67  	Stop()
    68  	Get(hash common.Hash) *types.Transaction
    69  	Stats() (int, int)
    70  	Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
    71  	StartSpamThrottler(conf *blockchain.ThrottlerConfig) error
    72  	StopSpamThrottler()
    73  }
    74  
    75  // Backend wraps all methods required for mining.
    76  type Backend interface {
    77  	AccountManager() accounts.AccountManager
    78  	BlockChain() BlockChain
    79  	TxPool() TxPool
    80  	ChainDB() database.DBManager
    81  	ReBroadcastTxs(transactions types.Transactions)
    82  }
    83  
    84  // Miner creates blocks and searches for proof-of-work values.
    85  type Miner struct {
    86  	mux *event.TypeMux
    87  
    88  	worker *worker
    89  
    90  	rewardbase common.Address
    91  	mining     int32
    92  	backend    Backend
    93  	engine     consensus.Engine
    94  
    95  	canStart    int32 // can start indicates whether we can start the mining operation
    96  	shouldStart int32 // should start indicates whether we should start after sync
    97  }
    98  
    99  func New(backend Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, nodetype common.ConnType, rewardbase common.Address, TxResendUseLegacy bool) *Miner {
   100  	miner := &Miner{
   101  		backend:  backend,
   102  		mux:      mux,
   103  		engine:   engine,
   104  		worker:   newWorker(config, engine, rewardbase, backend, mux, nodetype, TxResendUseLegacy),
   105  		canStart: 1,
   106  	}
   107  	// TODO-Klaytn drop or missing tx
   108  	miner.Register(NewCpuAgent(backend.BlockChain(), engine, nodetype))
   109  	go miner.update()
   110  
   111  	return miner
   112  }
   113  
   114  // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
   115  // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
   116  // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
   117  // and halt your mining operation for as long as the DOS continues.
   118  func (self *Miner) update() {
   119  	events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
   120  out:
   121  	for ev := range events.Chan() {
   122  		switch ev.Data.(type) {
   123  		case downloader.StartEvent:
   124  			atomic.StoreInt32(&self.canStart, 0)
   125  			if self.Mining() {
   126  				self.Stop()
   127  				atomic.StoreInt32(&self.shouldStart, 1)
   128  				logger.Info("Mining aborted due to sync")
   129  			}
   130  		case downloader.DoneEvent, downloader.FailedEvent:
   131  			shouldStart := atomic.LoadInt32(&self.shouldStart) == 1
   132  
   133  			atomic.StoreInt32(&self.canStart, 1)
   134  			atomic.StoreInt32(&self.shouldStart, 0)
   135  			if shouldStart {
   136  				self.Start()
   137  			}
   138  			// unsubscribe. we're only interested in this event once
   139  			events.Unsubscribe()
   140  			// stop immediately and ignore all further pending events
   141  			break out
   142  		}
   143  	}
   144  }
   145  
   146  func (self *Miner) Start() {
   147  	atomic.StoreInt32(&self.shouldStart, 1)
   148  
   149  	if atomic.LoadInt32(&self.canStart) == 0 {
   150  		logger.Info("Network syncing, will start work afterwards")
   151  		return
   152  	}
   153  	atomic.StoreInt32(&self.mining, 1)
   154  
   155  	if self.worker.nodetype == common.CONSENSUSNODE {
   156  		logger.Info("Starting mining operation")
   157  	}
   158  	self.worker.start()
   159  	self.worker.commitNewWork()
   160  }
   161  
   162  func (self *Miner) Stop() {
   163  	self.worker.stop()
   164  	atomic.StoreInt32(&self.mining, 0)
   165  	atomic.StoreInt32(&self.shouldStart, 0)
   166  }
   167  
   168  func (self *Miner) Register(agent Agent) {
   169  	if self.Mining() {
   170  		agent.Start()
   171  	}
   172  	self.worker.register(agent)
   173  }
   174  
   175  func (self *Miner) Unregister(agent Agent) {
   176  	self.worker.unregister(agent)
   177  }
   178  
   179  func (self *Miner) Mining() bool {
   180  	return atomic.LoadInt32(&self.mining) > 0
   181  }
   182  
   183  func (self *Miner) HashRate() (tot int64) {
   184  	if pow, ok := self.engine.(consensus.PoW); ok {
   185  		tot += int64(pow.Hashrate())
   186  	}
   187  	// do we care this might race? is it worth we're rewriting some
   188  	// aspects of the worker/locking up agents so we can get an accurate
   189  	// hashrate?
   190  	for agent := range self.worker.agents {
   191  		if _, ok := agent.(*CpuAgent); !ok {
   192  			tot += agent.GetHashRate()
   193  		}
   194  	}
   195  	return
   196  }
   197  
   198  func (self *Miner) SetExtra(extra []byte) error {
   199  	// istanbul BFT
   200  	maximumExtraDataSize := params.GetMaximumExtraDataSize()
   201  	if uint64(len(extra)) > maximumExtraDataSize {
   202  		return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), maximumExtraDataSize)
   203  	}
   204  	self.worker.setExtra(extra)
   205  	return nil
   206  }
   207  
   208  // Pending returns the currently pending block and associated state.
   209  func (self *Miner) Pending() (*types.Block, *state.StateDB) {
   210  	return self.worker.pending()
   211  }
   212  
   213  // PendingBlock returns the currently pending block.
   214  //
   215  // Note, to access both the pending block and the pending state
   216  // simultaneously, please use Pending(), as the pending state can
   217  // change between multiple method calls
   218  func (self *Miner) PendingBlock() *types.Block {
   219  	return self.worker.pendingBlock()
   220  }
   221  
   222  //go:generate mockgen -destination=mocks/blockchain_mock.go -package=mocks github.com/klaytn/klaytn/work BlockChain
   223  // BlockChain is an interface of blockchain.BlockChain used by ProtocolManager.
   224  type BlockChain interface {
   225  	Genesis() *types.Block
   226  
   227  	CurrentBlock() *types.Block
   228  	CurrentFastBlock() *types.Block
   229  	HasBlock(hash common.Hash, number uint64) bool
   230  	GetBlock(hash common.Hash, number uint64) *types.Block
   231  	GetBlockByHash(hash common.Hash) *types.Block
   232  	GetBlockByNumber(number uint64) *types.Block
   233  	GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash
   234  
   235  	CurrentHeader() *types.Header
   236  	HasHeader(hash common.Hash, number uint64) bool
   237  	GetHeader(hash common.Hash, number uint64) *types.Header
   238  	GetHeaderByHash(hash common.Hash) *types.Header
   239  	GetHeaderByNumber(number uint64) *types.Header
   240  
   241  	GetTd(hash common.Hash, number uint64) *big.Int
   242  	GetTdByHash(hash common.Hash) *big.Int
   243  
   244  	GetBodyRLP(hash common.Hash) rlp.RawValue
   245  
   246  	GetReceiptsByBlockHash(blockHash common.Hash) types.Receipts
   247  
   248  	InsertChain(chain types.Blocks) (int, error)
   249  	TrieNode(hash common.Hash) ([]byte, error)
   250  	ContractCode(hash common.Hash) ([]byte, error)
   251  	ContractCodeWithPrefix(hash common.Hash) ([]byte, error)
   252  	Config() *params.ChainConfig
   253  	State() (*state.StateDB, error)
   254  	Rollback(chain []common.Hash)
   255  	InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error)
   256  	InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error)
   257  	FastSyncCommitHead(hash common.Hash) error
   258  	StateCache() state.Database
   259  
   260  	SubscribeChainEvent(ch chan<- blockchain.ChainEvent) event.Subscription
   261  	SetHead(head uint64) error
   262  	Stop()
   263  
   264  	SubscribeRemovedLogsEvent(ch chan<- blockchain.RemovedLogsEvent) event.Subscription
   265  	SubscribeChainHeadEvent(ch chan<- blockchain.ChainHeadEvent) event.Subscription
   266  	SubscribeChainSideEvent(ch chan<- blockchain.ChainSideEvent) event.Subscription
   267  	SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
   268  	IsParallelDBWrite() bool
   269  	IsSenderTxHashIndexingEnabled() bool
   270  
   271  	Processor() blockchain.Processor
   272  	BadBlocks() ([]blockchain.BadBlockArgs, error)
   273  	StateAt(root common.Hash) (*state.StateDB, error)
   274  	PrunableStateAt(root common.Hash, num uint64) (*state.StateDB, error)
   275  	StateAtWithPersistent(root common.Hash) (*state.StateDB, error)
   276  	StateAtWithGCLock(root common.Hash) (*state.StateDB, error)
   277  	Export(w io.Writer) error
   278  	ExportN(w io.Writer, first, last uint64) error
   279  	Engine() consensus.Engine
   280  	GetTxLookupInfoAndReceipt(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, *types.Receipt)
   281  	GetTxAndLookupInfoInCache(hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64)
   282  	GetBlockReceiptsInCache(blockHash common.Hash) types.Receipts
   283  	GetTxLookupInfoAndReceiptInCache(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, *types.Receipt)
   284  	GetTxAndLookupInfo(txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64)
   285  	GetLogsByHash(hash common.Hash) [][]*types.Log
   286  	ResetWithGenesisBlock(gb *types.Block) error
   287  	Validator() blockchain.Validator
   288  	HasBadBlock(hash common.Hash) bool
   289  	WriteBlockWithState(block *types.Block, receipts []*types.Receipt, stateDB *state.StateDB) (blockchain.WriteResult, error)
   290  	PostChainEvents(events []interface{}, logs []*types.Log)
   291  	ApplyTransaction(config *params.ChainConfig, author *common.Address, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg *vm.Config) (*types.Receipt, *vm.InternalTxTrace, error)
   292  
   293  	// State Migration
   294  	PrepareStateMigration() error
   295  	StartStateMigration(uint64, common.Hash) error
   296  	StopStateMigration() error
   297  	StateMigrationStatus() (bool, uint64, int, int, int, float64, error)
   298  
   299  	// Warm up
   300  	StartWarmUp(minLoad uint) error
   301  	StartContractWarmUp(contractAddr common.Address, minLoad uint) error
   302  	StopWarmUp() error
   303  
   304  	// Collect state/storage trie statistics
   305  	StartCollectingTrieStats(contractAddr common.Address) error
   306  	GetContractStorageRoot(block *types.Block, db state.Database, contractAddr common.Address) (common.ExtHash, error)
   307  
   308  	// Save trie node cache to this
   309  	SaveTrieNodeCacheToDisk() error
   310  
   311  	// KES
   312  	BlockSubscriptionLoop(pool *blockchain.TxPool)
   313  	CloseBlockSubscriptionLoop()
   314  
   315  	// read-only mode
   316  	CurrentBlockUpdateLoop(pool *blockchain.TxPool)
   317  
   318  	// Snapshot
   319  	Snapshots() *snapshot.Tree
   320  }