~~~
直接执行btcd启动:
[root@bitcoin ~]# btcd 2018-07-01 15:07:34.645 [INF] BTCD: Version 0.12.0-beta 2018-07-01 15:07:34.645 [INF] BTCD: Loading block database from '/root/.btcd/data/mainnet/blocks_ffldb' 2018-07-01 15:07:34.655 [TRC] BCDB: Scan found latest block file #10 with length 479729657 2018-07-01 15:07:34.655 [INF] BTCD: Block database loaded 2018-07-01 15:07:34.661 [INF] INDX: cf index is enabled 2018-07-01 15:07:34.662 [TRC] CHAN: Serialized chain state: d97b2220ae8cbc1c881eed2faa7062205bd3d91ed0720a4831040000000000001e6503007131c90000000000090000002b80942403a92fc008 2018-07-01 15:07:34.662 [INF] CHAN: Loading block index... 2018-07-01 15:07:37.063 [DBG] INDX: Current committed filter index tip (height 222494, hash 0000000000000431480a72d01ed9d35b206270aa2fed1e881cbc8cae20227bd9) 2018-07-01 15:07:37.373 [INF] CHAN: Chain state (height 222494, hash 0000000000000431480a72d01ed9d35b206270aa2fed1e881cbc8cae20227bd9, totaltx 13185393, work 802475065101060653064) 2018-07-01 15:07:37.417 [TRC] SRVR: Starting server 2018-07-01 15:07:37.417 [TRC] RPCS: Starting RPC server 2018-07-01 15:07:37.417 [TRC] AMGR: Starting address manager 2018-07-01 15:07:37.437 [INF] RPCS: RPC server listening on [::1]:8334 2018-07-01 15:07:37.437 [INF] RPCS: RPC server listening on 127.0.0.1:8334 2018-07-01 15:07:37.500 [INF] AMGR: Loaded 4849 addresses from file '/root/.btcd/data/mainnet/peers.json' 2018-07-01 15:07:37.500 [TRC] SYNC: Starting sync manager 2018-07-01 15:07:37.500 [TRC] SRVR: Starting peer handler 2018-07-01 15:07:37.500 [TRC] CMGR: Connection manager started 2018-07-01 15:07:37.507 [TRC] AMGR: Selected 112.82.111.33:8333 from new bucket 2018-07-01 15:07:37.507 [DBG] CMGR: Attempting to connect to 112.82.111.33:8333 (reqid 1) 2018-07-01 15:07:37.508 [INF] CMGR: Server listening on 0.0.0.0:8333 2018-07-01 15:07:37.508 [INF] CMGR: Server listening on [::]:8333 2018-07-01 15:07:37.508 [TRC] AMGR: Selected 85.195.215.138:8333 from new bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 85.195.215.138:8333 (reqid 2) 2018-07-01 15:07:37.508 [TRC] AMGR: Selected 197.89.233.83:8333 from tried bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 197.89.233.83:8333 (reqid 3) 2018-07-01 15:07:37.508 [TRC] AMGR: Selected [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333 from new bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333 (reqid 4) 2018-07-01 15:07:37.508 [DBG] CMGR: Failed to connect to [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333 (reqid 4): dial tcp [2600:1f18:609f:da01:1c94:673e:4141:423f]:8333: connect: network is unreachable 2018-07-01 15:07:37.508 [TRC] AMGR: Selected 88.99.185.48:8333 from tried bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 88.99.185.48:8333 (reqid 5) 2018-07-01 15:07:37.508 [TRC] AMGR: Selected 197.89.233.83:8333 from tried bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 197.89.233.83:8333 (reqid 6) 2018-07-01 15:07:37.508 [TRC] AMGR: Selected [2002:8e00:8231::8e00:8231]:8333 from new bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to [2002:8e00:8231::8e00:8231]:8333 (reqid 7) 2018-07-01 15:07:37.508 [DBG] CMGR: Failed to connect to [2002:8e00:8231::8e00:8231]:8333 (reqid 7): dial tcp [2002:8e00:8231::8e00:8231]:8333: connect: network is unreachable 2018-07-01 15:07:37.508 [TRC] AMGR: Selected 185.9.63.169:8333 from new bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 185.9.63.169:8333 (reqid 8) 2018-07-01 15:07:37.508 [TRC] AMGR: Selected 142.169.246.147:8333 from new bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 142.169.246.147:8333 (reqid 9) 2018-07-01 15:07:37.508 [TRC] AMGR: Selected 216.157.21.35:8333 from tried bucket 2018-07-01 15:07:37.508 [DBG] CMGR: Attempting to connect to 216.157.21.35:8333 (reqid 10) 2018-07-01 15:07:37.563 [INF] CMGR: 38 addresses found from DNS seed seed.bitcoin.jonasschnelli.ch 2018-07-01 15:07:37.563 [TRC] AMGR: Added new address 88.99.187.182:8333 for a total of 4849 addresses 2018-07-01 15:07:37.563 [TRC] AMGR: Added new address 101.37.116.129:8333 for a total of 4849 addresses 2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:47f:240a:93e3:3222]:8333 for a total of 4850 addresses 2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:4a5:332b:88a1:a284]:8333 for a total of 4851 addresses 2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:10c6:3f05:a79f:85d1]:8333 for a total of 4852 addresses 2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:145f:32c3:2ee1:6a8c]:8333 for a total of 4852 addresses 2018-07-01 15:07:37.563 [TRC] AMGR: Added new address [2001:0:4137:9e76:24a5:375:a9fa:a96e]:8333 for a total of 4853 addresses
下面开始分析源码~
执行入口:
func main() { // Use all processor cores. runtime.GOMAXPROCS(runtime.NumCPU()) // Block and transaction processing can cause bursty allocations. This // limits the garbage collector from excessively overallocating during // bursts. This value was arrived at with the help of profiling live // usage. debug.SetGCPercent(10) // Up some limits. if err := limits.SetLimits(); err != nil { fmt.Fprintf(os.Stderr, "failed to set limits: %v\n", err) os.Exit(1) } if runtime.GOOS == "windows" { isService, err := winServiceMain() if err != nil { fmt.Println(err) os.Exit(1) } if isService { os.Exit(0) } } // Work around defer not working after os.Exit() if err := btcdMain(nil); err != nil { os.Exit(1) } }
runtime.GOMAXPROCS(runtime.NumCPU())
设置使用的CPU核数,默认已经设置所有
debug.SetGCPercent(10)
设置垃圾回收比率
limits.SetLimits()
limits包中有针对不同平台的三个文件:limits_plan9.go limits_unix.go limits_windows.go
设置系统资源限制
btcdMain(nil)这里才是真正干活的Main方法
btcMain方法主要逻辑:
package main import ( "net" "net/http" "os" "runtime/pprof" ) func btcdMain(serverChan chan<- *server) error { tcfg, _, err := loadConfig() interrupt := interruptListener() // Enable http profiling server if requested. if cfg.Profile != "" { go func() { listenAddr := net.JoinHostPort("", cfg.Profile) btcdLog.Infof("Profile server listening on %s", listenAddr) profileRedirect := http.RedirectHandler("/debug/pprof", http.StatusSeeOther) http.Handle("/", profileRedirect) btcdLog.Errorf("%v", http.ListenAndServe(listenAddr, nil)) }() } // Write cpu profile if requested. if cfg.CPUProfile != "" { f, err := os.Create(cfg.CPUProfile) if err != nil { btcdLog.Errorf("Unable to create cpu profile: %v", err) return err } pprof.StartCPUProfile(f) defer f.Close() defer pprof.StopCPUProfile() } // Return now if an interrupt signal was triggered. if interruptRequested(interrupt) { return nil } // Load the block database. db, err := loadBlockDB() defer func() { // Ensure the database is sync'd and closed on shutdown. btcdLog.Infof("Gracefully shutting down the database...") db.Close() }() // Create server and start it. server, err := newServer(cfg.Listeners, db, activeNetParams.Params, interrupt) defer func() { btcdLog.Infof("Gracefully shutting down the server...") server.Stop() server.WaitForShutdown() srvrLog.Infof("Server shutdown complete") }() server.Start() <-interrupt return nil }
loadConfig()
加载配置信息,使用了github.com/jessevdk/go-flags这个第三方库,可以实现从配置文件或命令行获取参数值,功能挺强大的库,近期也有一直在维护。
func interruptListener() <-chan struct{} {
这个方法主要是使用signal.Notify(interruptChannel, interruptSignals...)来监听系统interrupt的signal,如有中断则close channel。
db, err := loadBlockDB()
根据cfg.DbType配置打开数据库连接。通过代码得知支持数据库有dbTypes := []string{"ffldb", "leveldb", "sqlite"}
server, err := newServer(cfg.Listeners, db, activeNetParams.Params, interrupt)
使用配置的端口、刚连接的db及网络参数初始一个server对象。
activeNetParams.Params是硬编码参数,如下:
var MainNetParams = Params{ Name: "mainnet", Net: wire.MainNet, DefaultPort: "8333", DNSSeeds: []DNSSeed{ {"seed.bitcoin.sipa.be", true}, {"dnsseed.bluematt.me", true}, {"dnsseed.bitcoin.dashjr.org", false}, {"seed.bitcoinstats.com", true}, {"seed.bitnodes.io", false}, {"seed.bitcoin.jonasschnelli.ch", true}, }, // Chain parameters GenesisBlock: &genesisBlock,
硬编码了主网名称、默认端口、起始dns种子等。
接下来咱们详细分析newServer方法。主要处理逻辑有:
func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Params, interrupt <-chan struct{}) (*server, error) { defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom | wire.SFNodeWitness | wire.SFNodeCF services := defaultServices amgr := addrmgr.New(cfg.DataDir, btcdLookup) listeners, nat, err := initListeners(amgr, listenAddrs, services) s := server{ chainParams: chainParams, addrManager: amgr, newPeers: make(chan *serverPeer, cfg.MaxPeers), donePeers: make(chan *serverPeer, cfg.MaxPeers), banPeers: make(chan *serverPeer, cfg.MaxPeers), query: make(chan interface{}), relayInv: make(chan relayMsg, cfg.MaxPeers), broadcast: make(chan broadcastMsg, cfg.MaxPeers), quit: make(chan struct{}), modifyRebroadcastInv: make(chan interface{}), peerHeightsUpdate: make(chan updatePeerHeightsMsg), nat: nat, db: db, timeSource: blockchain.NewMedianTime(), services: services, sigCache: txscript.NewSigCache(cfg.SigCacheMaxSize), hashCache: txscript.NewHashCache(cfg.SigCacheMaxSize), cfCheckptCaches: make(map[wire.FilterType][]cfHeaderKV), } s.txIndex = indexers.NewTxIndex(db) indexes = append(indexes, s.addrIndex) indexes = append(indexes, s.cfIndex) indexManager = indexers.NewManager(db, indexes) // Merge given checkpoints with the default ones unless they are disabled. var checkpoints []chaincfg.Checkpoint if !cfg.DisableCheckpoints { checkpoints = mergeCheckpoints(s.chainParams.Checkpoints, cfg.addCheckpoints) } // Create a new block chain instance with the appropriate configuration. var err error s.chain, err = blockchain.New(&blockchain.Config{ DB: s.db, Interrupt: interrupt, ChainParams: s.chainParams, Checkpoints: checkpoints, TimeSource: s.timeSource, SigCache: s.sigCache, IndexManager: indexManager, HashCache: s.hashCache, }) if err != nil { return nil, err } // Search for a FeeEstimator state in the database. If none can be found // or if it cannot be loaded, create a new one. db.Update(func(tx database.Tx) error { metadata := tx.Metadata() feeEstimationData := metadata.Get(mempool.EstimateFeeDatabaseKey) if feeEstimationData != nil { // delete it from the database so that we don't try to restore the // same thing again somehow. metadata.Delete(mempool.EstimateFeeDatabaseKey) // If there is an error, log it and make a new fee estimator. var err error s.feeEstimator, err = mempool.RestoreFeeEstimator(feeEstimationData) if err != nil { peerLog.Errorf("Failed to restore fee estimator %v", err) } } return nil }) if s.feeEstimator == nil || s.feeEstimator.LastKnownHeight() != s.chain.BestSnapshot().Height { s.feeEstimator = mempool.NewFeeEstimator( mempool.DefaultEstimateFeeMaxRollback, mempool.DefaultEstimateFeeMinRegisteredBlocks) } txC := mempool.Config{ Policy: mempool.Policy{ DisableRelayPriority: cfg.NoRelayPriority, AcceptNonStd: cfg.RelayNonStd, FreeTxRelayLimit: cfg.FreeTxRelayLimit, MaxOrphanTxs: cfg.MaxOrphanTxs, MaxOrphanTxSize: defaultMaxOrphanTxSize, MaxSigOpCostPerTx: blockchain.MaxBlockSigOpsCost / 4, MinRelayTxFee: cfg.minRelayTxFee, MaxTxVersion: 2, }, ChainParams: chainParams, FetchUtxoView: s.chain.FetchUtxoView, BestHeight: func() int32 { return s.chain.BestSnapshot().Height }, MedianTimePast: func() time.Time { return s.chain.BestSnapshot().MedianTime }, CalcSequenceLock: func(tx *btcutil.Tx, view *blockchain.UtxoViewpoint) (*blockchain.SequenceLock, error) { return s.chain.CalcSequenceLock(tx, view, true) }, IsDeploymentActive: s.chain.IsDeploymentActive, SigCache: s.sigCache, HashCache: s.hashCache, AddrIndex: s.addrIndex, FeeEstimator: s.feeEstimator, } s.txMemPool = mempool.New(&txC) s.syncManager, err = netsync.New(&netsync.Config{ PeerNotifier: &s, Chain: s.chain, TxMemPool: s.txMemPool, ChainParams: s.chainParams, DisableCheckpoints: cfg.DisableCheckpoints, MaxPeers: cfg.MaxPeers, FeeEstimator: s.feeEstimator, }) if err != nil { return nil, err } policy := mining.Policy{ BlockMinWeight: cfg.BlockMinWeight, BlockMaxWeight: cfg.BlockMaxWeight, BlockMinSize: cfg.BlockMinSize, BlockMaxSize: cfg.BlockMaxSize, BlockPrioritySize: cfg.BlockPrioritySize, TxMinFreeFee: cfg.minRelayTxFee, } blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, s.chainParams, s.txMemPool, s.chain, s.timeSource, s.sigCache, s.hashCache) s.cpuMiner = cpuminer.New(&cpuminer.Config{ ChainParams: chainParams, BlockTemplateGenerator: blockTemplateGenerator, MiningAddrs: cfg.miningAddrs, ProcessBlock: s.syncManager.ProcessBlock, ConnectedCount: s.ConnectedCount, IsCurrent: s.syncManager.IsCurrent, }) var newAddressFunc func() (net.Addr, error) // Create a connection manager. targetOutbound := defaultTargetOutbound if cfg.MaxPeers < targetOutbound { targetOutbound = cfg.MaxPeers } cmgr, err := connmgr.New(&connmgr.Config{ Listeners: listeners, OnAccept: s.inboundPeerConnected, RetryDuration: connectionRetryInterval, TargetOutbound: uint32(targetOutbound), Dial: btcdDial, OnConnection: s.outboundPeerConnected, GetNewAddress: newAddressFunc, }) if err != nil { return nil, err } s.connManager = cmgr // Start up persistent peers. permanentPeers := cfg.ConnectPeers if len(permanentPeers) == 0 { permanentPeers = cfg.AddPeers } for _, addr := range permanentPeers { netAddr, err := addrStringToNetAddr(addr) if err != nil { return nil, err } go s.connManager.Connect(&connmgr.ConnReq{ Addr: netAddr, Permanent: true, }) } if !cfg.DisableRPC { // Setup listeners for the configured RPC listen addresses and // TLS settings. rpcListeners, err := setupRPCListeners() if err != nil { return nil, err } if len(rpcListeners) == 0 { return nil, errors.New("RPCS: No valid listen address") } s.rpcServer, err = newRPCServer(&rpcserverConfig{ Listeners: rpcListeners, StartupTime: s.startupTime, ConnMgr: &rpcConnManager{&s}, SyncMgr: &rpcSyncMgr{&s, s.syncManager}, TimeSource: s.timeSource, Chain: s.chain, ChainParams: chainParams, DB: db, TxMemPool: s.txMemPool, Generator: blockTemplateGenerator, CPUMiner: s.cpuMiner, TxIndex: s.txIndex, AddrIndex: s.addrIndex, CfIndex: s.cfIndex, FeeEstimator: s.feeEstimator, }) } return &s, nil }
defaultServices = wire.SFNodeNetwork | wire.SFNodeBloom | wire.SFNodeWitness | wire.SFNodeCF
btcd默认启用全节点、Bloom过滤器、见证隔离、提交过滤。
amgr := addrmgr.New(cfg.DataDir, btcdLookup)
创建一个网络地址管理对象,负责管理与其连接的地址(支持tor地址)。所有连接的节点数据都默认保存到peers.json文件中。
listeners, nat, err := initListeners(amgr, listenAddrs, services)
使用配置的externalIPs或upnp对外部IP监听
s := server{
创建server对象,其中有对chainParams、sigCache、hashCache等的初始化
indexManager = indexers.NewManager(db, indexes)
使用txIndex、addrIndex、cfIndex创建一个索引管理器
s.chain, err = blockchain.New(&blockchain.Config{
创建一个blockchain对象并赋值给server对象
s.txMemPool = mempool.New(&txC)创建未打包的交易内存池
s.syncManager, err = netsync.New(&netsync.Config{
新建网络同步对象,并赋值给server.syncManager
policy := mining.Policy{
新建挖矿策略
s.cpuMiner = cpuminer.New(&cpuminer.Config{
创建cpu挖矿对象
cmgr, err := connmgr.New(&connmgr.Config{
创建节点连接对象
s.rpcServer, err = newRPCServer(&rpcserverConfig{创建RPCserver
return &s, nil最后返回创建的server对象
接下来就可以执行:
server.Start()
启动代码:
// Start begins accepting connections from peers. func (s *server) Start() { // Already started? if atomic.AddInt32(&s.started, 1) != 1 { return } srvrLog.Trace("Starting server") // Server startup time. Used for the uptime command for uptime calculation. s.startupTime = time.Now().Unix() // Start the peer handler which in turn starts the address and block // managers. s.wg.Add(1) go s.peerHandler() if s.nat != nil { s.wg.Add(1) go s.upnpUpdateThread() } if !cfg.DisableRPC { s.wg.Add(1) // Start the rebroadcastHandler, which ensures user tx received by // the RPC server are rebroadcast until being included in a block. go s.rebroadcastHandler() s.rpcServer.Start() } // Start the CPU miner if generation is enabled. if cfg.Generate { s.cpuMiner.Start() } }
atomic.AddInt32(&s.started, 1) != 1
设置只启动一次
go s.peerHandler()
异步启动节点处理,包括启动地址管理、启动区块同步、启动连接管理。peerHandler处理逻辑较多,代码在最后解析。
s.rpcServer.Start()
启动rpcserver
s.cpuMiner.Start()开始CPU挖矿
peerHandler代码主要逻辑:
func (s *server) peerHandler() { s.addrManager.Start() s.syncManager.Start() srvrLog.Tracef("Starting peer handler") state := &peerState{ inboundPeers: make(map[int32]*serverPeer), persistentPeers: make(map[int32]*serverPeer), outboundPeers: make(map[int32]*serverPeer), banned: make(map[string]time.Time), outboundGroups: make(map[string]int), } if !cfg.DisableDNSSeed { // Add peers discovered through DNS to the address manager. connmgr.SeedFromDNS(activeNetParams.Params, defaultRequiredServices, btcdLookup, func(addrs []*wire.NetAddress) { // Bitcoind uses a lookup of the dns seeder here. This // is rather strange since the values looked up by the // DNS seed lookups will vary quite a lot. // to replicate this behaviour we put all addresses as // having come from the first one. s.addrManager.AddAddresses(addrs, addrs[0]) }) } go s.connManager.Start() out: for { select { // New peers connected to the server. case p := <-s.newPeers: s.handleAddPeerMsg(state, p) // Disconnected peers. case p := <-s.donePeers: s.handleDonePeerMsg(state, p) // Block accepted in mainchain or orphan, update peer height. case umsg := <-s.peerHeightsUpdate: s.handleUpdatePeerHeights(state, umsg) // Peer to ban. case p := <-s.banPeers: s.handleBanPeerMsg(state, p) // New inventory to potentially be relayed to other peers. case invMsg := <-s.relayInv: s.handleRelayInvMsg(state, invMsg) // Message to broadcast to all connected peers except those // which are excluded by the message. case bmsg := <-s.broadcast: s.handleBroadcastMsg(state, &bmsg) case qmsg := <-s.query: s.handleQuery(state, qmsg) case <-s.quit: // Disconnect all peers on server shutdown. state.forAllPeers(func(sp *serverPeer) { srvrLog.Tracef("Shutdown peer %s", sp) sp.Disconnect() }) break out } } s.connManager.Stop() s.syncManager.Stop() s.addrManager.Stop() // Drain channels before exiting so nothing is left waiting around // to send. cleanup: for { select { case <-s.newPeers: case <-s.donePeers: case <-s.peerHeightsUpdate: case <-s.relayInv: case <-s.broadcast: case <-s.query: default: break cleanup } } s.wg.Done() srvrLog.Tracef("Peer handler done") }
s.addrManager.Start()
启动节点地址管理,方法主要逻辑:
func (a *AddrManager) Start() { // Load peers we already know about from file. a.loadPeers() }loadPeers负责反序列化peers.json文件,addrManager维护了NewBucket和TriedBucket两个地址列表
s.syncManager.Start()
主要代码逻辑:
func (sm *SyncManager) Start() { go sm.blockHandler() }
blockHandler作用主要负责处理新节点、交易、块信息、头信息、节点断开等消息。代码如下:
func (sm *SyncManager) blockHandler() { out: for { select { case m := <-sm.msgChan: switch msg := m.(type) { case *newPeerMsg: sm.handleNewPeerMsg(msg.peer) case *txMsg: sm.handleTxMsg(msg) msg.reply <- struct{}{} case *blockMsg: sm.handleBlockMsg(msg) msg.reply <- struct{}{} case *invMsg: sm.handleInvMsg(msg) case *headersMsg: sm.handleHeadersMsg(msg) case *donePeerMsg: sm.handleDonePeerMsg(msg.peer) case getSyncPeerMsg: var peerID int32 if sm.syncPeer != nil { peerID = sm.syncPeer.ID() } msg.reply <- peerID case processBlockMsg: _, isOrphan, err := sm.chain.ProcessBlock( msg.block, msg.flags) if err != nil { msg.reply <- processBlockResponse{ isOrphan: false, err: err, } } msg.reply <- processBlockResponse{ isOrphan: isOrphan, err: nil, } case isCurrentMsg: msg.reply <- sm.current() case pauseMsg: // Wait until the sender unpauses the manager. <-msg.unpause default: log.Warnf("Invalid message type in block "+ "handler: %T", msg) } case <-sm.quit: break out } } sm.wg.Done() log.Trace("Block handler done") }
go s.connManager.Start()
启动连接管理,默认建立8个出口连接,负责管理连接的连接、断开。主要代码:
func (cm *ConnManager) Start() { go cm.connHandler() for i := atomic.LoadUint64(&cm.connReqCount); i < uint64(cm.cfg.TargetOutbound); i++ { go cm.NewConnReq() } }
go cm.NewConnReq()
是建立节点连接
s.cpuMiner.Start()
启动cpu挖矿
func (m *CPUMiner) Start() { go m.speedMonitor() go m.miningWorkerController() m.started = true log.Infof("CPU miner started") }
go m.miningWorkerController()
这是挖矿操作,启动多个协程进行生成区块。
func (m *CPUMiner) miningWorkerController() { var runningWorkers []chan struct{} launchWorkers := func(numWorkers uint32) { for i := uint32(0); i < numWorkers; i++ { quit := make(chan struct{}) runningWorkers = append(runningWorkers, quit) m.workerWg.Add(1) go m.generateBlocks(quit) } } // Launch the current number of workers by default. runningWorkers = make([]chan struct{}, 0, m.numWorkers) launchWorkers(m.numWorkers) }
go m.generateBlocks(quit)
这一行代码是生成区块,主要逻辑:
func (m *CPUMiner) generateBlocks(quit chan struct{}) { log.Tracef("Starting generate blocks worker") out: for { m.submitBlockLock.Lock() curHeight := m.g.BestSnapshot().Height if curHeight != 0 && !m.cfg.IsCurrent() { m.submitBlockLock.Unlock() time.Sleep(time.Second) continue } // Choose a payment address at random. rand.Seed(time.Now().UnixNano()) payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))] template, err := m.g.NewBlockTemplate(payToAddr) m.submitBlockLock.Unlock() if m.solveBlock(template.Block, curHeight+1, ticker, quit) { block := btcutil.NewBlock(template.Block) m.submitBlock(block) } } log.Tracef("Generate blocks worker done") }
m.solveBlock(template.Block, curHeight+1, ticker, quit)
进行POW计算区块hash,主要逻辑:
func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, ticker *time.Ticker, quit chan struct{}) bool { // Create some convenience variables. header := &msgBlock.Header targetDifficulty := blockchain.CompactToBig(header.Bits) // Initial state. lastGenerated := time.Now() lastTxUpdate := m.g.TxSource().LastUpdated() hashesCompleted := uint64(0) for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ { m.g.UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset) for i := uint32(0); i <= maxNonce; i++ { select { case <-quit: return false case <-ticker.C: m.updateHashes <- hashesCompleted hashesCompleted = 0 best := m.g.BestSnapshot() if !header.PrevBlock.IsEqual(&best.Hash) { return false } if lastTxUpdate != m.g.TxSource().LastUpdated() && time.Now().After(lastGenerated.Add(time.Minute)) { return false } m.g.UpdateBlockTime(msgBlock) default: } header.Nonce = i hash := header.BlockHash() hashesCompleted += 2 if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 { m.updateHashes <- hashesCompleted return true } } } return false }以上代码即是循环设置Nonce值计算hash值小于难度值
本文作者:architect.bian,欢迎收藏,转载请保留原文地址并保留版权声明!谢谢~
还没完!往下看!!!