OSDN Git Service

Merge pull request #935 from Bytom/dev
[bytom/bytom.git] / node / node.go
1 package node
2
3 import (
4         "context"
5         "errors"
6         "net/http"
7         _ "net/http/pprof"
8         "os"
9         "path"
10         "path/filepath"
11         "time"
12
13         "github.com/prometheus/prometheus/util/flock"
14         log "github.com/sirupsen/logrus"
15         cmn "github.com/tendermint/tmlibs/common"
16         dbm "github.com/tendermint/tmlibs/db"
17         browser "github.com/toqueteos/webbrowser"
18
19         "github.com/bytom/accesstoken"
20         "github.com/bytom/account"
21         "github.com/bytom/api"
22         "github.com/bytom/asset"
23         "github.com/bytom/blockchain/pseudohsm"
24         "github.com/bytom/blockchain/txfeed"
25         cfg "github.com/bytom/config"
26         "github.com/bytom/consensus"
27         "github.com/bytom/database/leveldb"
28         "github.com/bytom/env"
29         "github.com/bytom/mining/cpuminer"
30         "github.com/bytom/mining/miningpool"
31         "github.com/bytom/netsync"
32         "github.com/bytom/protocol"
33         "github.com/bytom/protocol/bc"
34         "github.com/bytom/types"
35         w "github.com/bytom/wallet"
36 )
37
38 const (
39         webAddress               = "http://127.0.0.1:9888"
40         expireReservationsPeriod = time.Second
41         maxNewBlockChSize        = 1024
42 )
43
44 type Node struct {
45         cmn.BaseService
46
47         // config
48         config *cfg.Config
49
50         syncManager *netsync.SyncManager
51
52         evsw types.EventSwitch // pub/sub for services
53         //bcReactor    *bc.BlockchainReactor
54         wallet       *w.Wallet
55         accessTokens *accesstoken.CredentialStore
56         api          *api.API
57         chain        *protocol.Chain
58         txfeed       *txfeed.Tracker
59         cpuMiner     *cpuminer.CPUMiner
60         miningPool   *miningpool.MiningPool
61         miningEnable bool
62 }
63
64 func NewNode(config *cfg.Config) *Node {
65         ctx := context.Background()
66         if err := lockDataDirectory(config); err != nil {
67                 cmn.Exit("Error: " + err.Error())
68         }
69         initLogFile(config)
70         initActiveNetParams(config)
71         // Get store
72         coreDB := dbm.NewDB("core", config.DBBackend, config.DBDir())
73         store := leveldb.NewStore(coreDB)
74
75         tokenDB := dbm.NewDB("accesstoken", config.DBBackend, config.DBDir())
76         accessTokens := accesstoken.NewStore(tokenDB)
77
78         // Make event switch
79         eventSwitch := types.NewEventSwitch()
80         if _, err := eventSwitch.Start(); err != nil {
81                 cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))
82         }
83
84         txPool := protocol.NewTxPool()
85         chain, err := protocol.NewChain(store, txPool)
86         if err != nil {
87                 cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
88         }
89
90         var accounts *account.Manager = nil
91         var assets *asset.Registry = nil
92         var wallet *w.Wallet = nil
93         var txFeed *txfeed.Tracker = nil
94
95         txFeedDB := dbm.NewDB("txfeeds", config.DBBackend, config.DBDir())
96         txFeed = txfeed.NewTracker(txFeedDB, chain)
97
98         if err = txFeed.Prepare(ctx); err != nil {
99                 log.WithField("error", err).Error("start txfeed")
100                 return nil
101         }
102
103         hsm, err := pseudohsm.New(config.KeysDir())
104         if err != nil {
105                 cmn.Exit(cmn.Fmt("initialize HSM failed: %v", err))
106         }
107
108         if !config.Wallet.Disable {
109                 walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())
110                 accounts = account.NewManager(walletDB, chain)
111                 assets = asset.NewRegistry(walletDB, chain)
112                 wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain)
113                 if err != nil {
114                         log.WithField("error", err).Error("init NewWallet")
115                 }
116
117                 // trigger rescan wallet
118                 if config.Wallet.Rescan {
119                         wallet.RescanBlocks()
120                 }
121
122                 // Clean up expired UTXO reservations periodically.
123                 go accounts.ExpireReservations(ctx, expireReservationsPeriod)
124         }
125         newBlockCh := make(chan *bc.Hash, maxNewBlockChSize)
126
127         syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh)
128
129         // run the profile server
130         profileHost := config.ProfListenAddress
131         if profileHost != "" {
132                 // Profiling bytomd programs.see (https://blog.golang.org/profiling-go-programs)
133                 // go tool pprof http://profileHose/debug/pprof/heap
134                 go func() {
135                         http.ListenAndServe(profileHost, nil)
136                 }()
137         }
138
139         node := &Node{
140                 config:       config,
141                 syncManager:  syncManager,
142                 evsw:         eventSwitch,
143                 accessTokens: accessTokens,
144                 wallet:       wallet,
145                 chain:        chain,
146                 txfeed:       txFeed,
147                 miningEnable: config.Mining,
148         }
149
150         node.cpuMiner = cpuminer.NewCPUMiner(chain, accounts, txPool, newBlockCh)
151         node.miningPool = miningpool.NewMiningPool(chain, accounts, txPool, newBlockCh)
152
153         node.BaseService = *cmn.NewBaseService(nil, "Node", node)
154
155         return node
156 }
157
158 // Lock data directory after daemonization
159 func lockDataDirectory(config *cfg.Config) error {
160         _, _, err := flock.New(filepath.Join(config.RootDir, "LOCK"))
161         if err != nil {
162                 return errors.New("datadir already used by another process")
163         }
164         return nil
165 }
166
167 func initActiveNetParams(config *cfg.Config) {
168         var exist bool
169         consensus.ActiveNetParams, exist = consensus.NetParams[config.ChainID]
170         if !exist {
171                 cmn.Exit(cmn.Fmt("chain_id[%v] don't exist", config.ChainID))
172         }
173 }
174
175 func initLogFile(config *cfg.Config) {
176         logFilePath := filepath.Join(config.RootDir, "log")
177         cmn.EnsureDir(logFilePath, 0700)
178         logFileName := path.Join(logFilePath, config.LogName)
179         file, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
180         if err == nil {
181                 log.SetOutput(file)
182         } else {
183                 log.Info("Failed to open log file, using default")
184         }
185 }
186
187 // Lanch web broser or not
188 func launchWebBrowser() {
189         log.Info("Launching System Browser with :", webAddress)
190         if err := browser.Open(webAddress); err != nil {
191                 log.Error(err.Error())
192                 return
193         }
194 }
195
196 func (n *Node) initAndstartApiServer() {
197         n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)
198
199         listenAddr := env.String("LISTEN", n.config.ApiAddress)
200         env.Parse()
201         n.api.StartServer(*listenAddr)
202 }
203
204 func (n *Node) OnStart() error {
205         if n.miningEnable {
206                 n.cpuMiner.Start()
207         }
208         if !n.config.VaultMode {
209                 n.syncManager.Start()
210         }
211         n.initAndstartApiServer()
212         if !n.config.Web.Closed {
213                 launchWebBrowser()
214         }
215
216         return nil
217 }
218
219 func (n *Node) OnStop() {
220         n.BaseService.OnStop()
221         if n.miningEnable {
222                 n.cpuMiner.Stop()
223         }
224         if !n.config.VaultMode {
225                 n.syncManager.Stop()
226         }
227 }
228
229 func (n *Node) RunForever() {
230         // Sleep forever and then...
231         cmn.TrapSignal(func() {
232                 n.Stop()
233         })
234 }
235
236 func (n *Node) EventSwitch() types.EventSwitch {
237         return n.evsw
238 }
239
240 func (n *Node) SyncManager() *netsync.SyncManager {
241         return n.syncManager
242 }
243
244 func (n *Node) MiningPool() *miningpool.MiningPool {
245         return n.miningPool
246 }