OSDN Git Service

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