You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
9.7 KiB

10 years ago
10 years ago
9 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
  1. package node
  2. import (
  3. "bytes"
  4. "io/ioutil"
  5. "math/rand"
  6. "net"
  7. "net/http"
  8. "strings"
  9. "time"
  10. . "github.com/tendermint/go-common"
  11. "github.com/tendermint/go-crypto"
  12. dbm "github.com/tendermint/go-db"
  13. "github.com/tendermint/go-p2p"
  14. "github.com/tendermint/go-wire"
  15. bc "github.com/tendermint/tendermint/blockchain"
  16. "github.com/tendermint/tendermint/consensus"
  17. "github.com/tendermint/tendermint/events"
  18. mempl "github.com/tendermint/tendermint/mempool"
  19. "github.com/tendermint/tendermint/proxy"
  20. "github.com/tendermint/tendermint/rpc"
  21. "github.com/tendermint/tendermint/rpc/core"
  22. "github.com/tendermint/tendermint/rpc/server"
  23. sm "github.com/tendermint/tendermint/state"
  24. "github.com/tendermint/tendermint/types"
  25. )
  26. import _ "net/http/pprof"
  27. type Node struct {
  28. sw *p2p.Switch
  29. evsw *events.EventSwitch
  30. book *p2p.AddrBook
  31. blockStore *bc.BlockStore
  32. pexReactor *p2p.PEXReactor
  33. bcReactor *bc.BlockchainReactor
  34. mempoolReactor *mempl.MempoolReactor
  35. consensusState *consensus.ConsensusState
  36. consensusReactor *consensus.ConsensusReactor
  37. privValidator *types.PrivValidator
  38. genesisDoc *types.GenesisDoc
  39. privKey crypto.PrivKeyEd25519
  40. }
  41. func NewNode() *Node {
  42. // Get BlockStore
  43. blockStoreDB := dbm.GetDB("blockstore")
  44. blockStore := bc.NewBlockStore(blockStoreDB)
  45. // Get State
  46. state := getState()
  47. // Create two proxyAppCtx connections,
  48. // one for the consensus and one for the mempool.
  49. proxyAddr := config.GetString("proxy_app")
  50. proxyAppCtxMempool := getProxyApp(proxyAddr, state.LastAppHash)
  51. proxyAppCtxConsensus := getProxyApp(proxyAddr, state.LastAppHash)
  52. // add the chainid to the global config
  53. config.Set("chain_id", state.ChainID)
  54. // Get PrivValidator
  55. privValidatorFile := config.GetString("priv_validator_file")
  56. privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
  57. // Generate node PrivKey
  58. privKey := crypto.GenPrivKeyEd25519()
  59. // Make event switch
  60. eventSwitch := events.NewEventSwitch()
  61. _, err := eventSwitch.Start()
  62. if err != nil {
  63. Exit(Fmt("Failed to start switch: %v", err))
  64. }
  65. // Make PEXReactor
  66. book := p2p.NewAddrBook(config.GetString("addrbook_file"))
  67. pexReactor := p2p.NewPEXReactor(book)
  68. // Make BlockchainReactor
  69. bcReactor := bc.NewBlockchainReactor(state.Copy(), proxyAppCtxConsensus, blockStore, config.GetBool("fast_sync"))
  70. // Make MempoolReactor
  71. mempool := mempl.NewMempool(proxyAppCtxMempool)
  72. mempoolReactor := mempl.NewMempoolReactor(mempool)
  73. // Make ConsensusReactor
  74. consensusState := consensus.NewConsensusState(state.Copy(), proxyAppCtxConsensus, blockStore, mempool)
  75. consensusReactor := consensus.NewConsensusReactor(consensusState, blockStore, config.GetBool("fast_sync"))
  76. if privValidator != nil {
  77. consensusReactor.SetPrivValidator(privValidator)
  78. }
  79. // Make p2p network switch
  80. sw := p2p.NewSwitch()
  81. sw.AddReactor("PEX", pexReactor)
  82. sw.AddReactor("MEMPOOL", mempoolReactor)
  83. sw.AddReactor("BLOCKCHAIN", bcReactor)
  84. sw.AddReactor("CONSENSUS", consensusReactor)
  85. // add the event switch to all services
  86. // they should all satisfy events.Eventable
  87. SetFireable(eventSwitch, bcReactor, mempoolReactor, consensusReactor)
  88. // run the profile server
  89. profileHost := config.GetString("prof_laddr")
  90. if profileHost != "" {
  91. go func() {
  92. log.Warn("Profile server", "error", http.ListenAndServe(profileHost, nil))
  93. }()
  94. }
  95. return &Node{
  96. sw: sw,
  97. evsw: eventSwitch,
  98. book: book,
  99. blockStore: blockStore,
  100. pexReactor: pexReactor,
  101. bcReactor: bcReactor,
  102. mempoolReactor: mempoolReactor,
  103. consensusState: consensusState,
  104. consensusReactor: consensusReactor,
  105. privValidator: privValidator,
  106. genesisDoc: state.GenesisDoc,
  107. privKey: privKey,
  108. }
  109. }
  110. // Call Start() after adding the listeners.
  111. func (n *Node) Start() error {
  112. n.book.Start()
  113. n.sw.SetNodeInfo(makeNodeInfo(n.sw, n.privKey))
  114. n.sw.SetNodePrivKey(n.privKey)
  115. _, err := n.sw.Start()
  116. return err
  117. }
  118. func (n *Node) Stop() {
  119. log.Notice("Stopping Node")
  120. // TODO: gracefully disconnect from peers.
  121. n.sw.Stop()
  122. n.book.Stop()
  123. }
  124. // Add the event switch to reactors, mempool, etc.
  125. func SetFireable(evsw *events.EventSwitch, eventables ...events.Eventable) {
  126. for _, e := range eventables {
  127. e.SetFireable(evsw)
  128. }
  129. }
  130. // Add a Listener to accept inbound peer connections.
  131. // Add listeners before starting the Node.
  132. // The first listener is the primary listener (in NodeInfo)
  133. func (n *Node) AddListener(l p2p.Listener) {
  134. log.Notice(Fmt("Added %v", l))
  135. n.sw.AddListener(l)
  136. n.book.AddOurAddress(l.ExternalAddress())
  137. }
  138. // Dial a list of seeds in random order
  139. // Spawns a go routine for each dial
  140. func (n *Node) DialSeed() {
  141. // permute the list, dial them in random order.
  142. seeds := strings.Split(config.GetString("seeds"), ",")
  143. perm := rand.Perm(len(seeds))
  144. for i := 0; i < len(perm); i++ {
  145. go func(i int) {
  146. time.Sleep(time.Duration(rand.Int63n(3000)) * time.Millisecond)
  147. j := perm[i]
  148. addr := p2p.NewNetAddressString(seeds[j])
  149. n.dialSeed(addr)
  150. }(i)
  151. }
  152. }
  153. func (n *Node) dialSeed(addr *p2p.NetAddress) {
  154. peer, err := n.sw.DialPeerWithAddress(addr)
  155. if err != nil {
  156. log.Error("Error dialing seed", "error", err)
  157. //n.book.MarkAttempt(addr)
  158. return
  159. } else {
  160. log.Notice("Connected to seed", "peer", peer)
  161. n.book.AddAddress(addr, addr)
  162. }
  163. }
  164. func (n *Node) StartRPC() (net.Listener, error) {
  165. core.SetBlockStore(n.blockStore)
  166. core.SetConsensusState(n.consensusState)
  167. core.SetConsensusReactor(n.consensusReactor)
  168. core.SetMempoolReactor(n.mempoolReactor)
  169. core.SetSwitch(n.sw)
  170. core.SetPrivValidator(n.privValidator)
  171. core.SetGenesisDoc(n.genesisDoc)
  172. listenAddr := config.GetString("rpc_laddr")
  173. mux := http.NewServeMux()
  174. wm := rpcserver.NewWebsocketManager(core.Routes, n.evsw)
  175. mux.HandleFunc("/websocket", wm.WebsocketHandler)
  176. rpcserver.RegisterRPCFuncs(mux, core.Routes)
  177. return rpcserver.StartHTTPServer(listenAddr, mux)
  178. }
  179. func (n *Node) Switch() *p2p.Switch {
  180. return n.sw
  181. }
  182. func (n *Node) BlockStore() *bc.BlockStore {
  183. return n.blockStore
  184. }
  185. func (n *Node) ConsensusState() *consensus.ConsensusState {
  186. return n.consensusState
  187. }
  188. func (n *Node) MempoolReactor() *mempl.MempoolReactor {
  189. return n.mempoolReactor
  190. }
  191. func (n *Node) EventSwitch() *events.EventSwitch {
  192. return n.evsw
  193. }
  194. func makeNodeInfo(sw *p2p.Switch, privKey crypto.PrivKeyEd25519) *p2p.NodeInfo {
  195. nodeInfo := &p2p.NodeInfo{
  196. PubKey: privKey.PubKey().(crypto.PubKeyEd25519),
  197. Moniker: config.GetString("moniker"),
  198. Network: config.GetString("chain_id"),
  199. Version: Version,
  200. Other: []string{
  201. Fmt("p2p_version=%v", p2p.Version),
  202. Fmt("rpc_version=%v", rpc.Version),
  203. Fmt("wire_version=%v", wire.Version),
  204. },
  205. }
  206. // include git hash in the nodeInfo if available
  207. if rev, err := ReadFile(config.GetString("revision_file")); err == nil {
  208. nodeInfo.Other = append(nodeInfo.Other, Fmt("revision=%v", string(rev)))
  209. }
  210. if !sw.IsListening() {
  211. return nodeInfo
  212. }
  213. p2pListener := sw.Listeners()[0]
  214. p2pHost := p2pListener.ExternalAddress().IP.String()
  215. p2pPort := p2pListener.ExternalAddress().Port
  216. rpcListenAddr := config.GetString("rpc_laddr")
  217. // We assume that the rpcListener has the same ExternalAddress.
  218. // This is probably true because both P2P and RPC listeners use UPnP,
  219. // except of course if the rpc is only bound to localhost
  220. nodeInfo.ListenAddr = Fmt("%v:%v", p2pHost, p2pPort)
  221. nodeInfo.Other = append(nodeInfo.Other, Fmt("rpc_addr=%v", rpcListenAddr))
  222. return nodeInfo
  223. }
  224. //------------------------------------------------------------------------------
  225. func RunNode() {
  226. // Wait until the genesis doc becomes available
  227. genDocFile := config.GetString("genesis_file")
  228. if !FileExists(genDocFile) {
  229. log.Notice(Fmt("Waiting for genesis file %v...", genDocFile))
  230. for {
  231. time.Sleep(time.Second)
  232. if !FileExists(genDocFile) {
  233. continue
  234. }
  235. jsonBlob, err := ioutil.ReadFile(genDocFile)
  236. if err != nil {
  237. Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
  238. }
  239. genDoc := types.GenesisDocFromJSON(jsonBlob)
  240. if genDoc.ChainID == "" {
  241. PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile))
  242. }
  243. config.Set("chain_id", genDoc.ChainID)
  244. config.Set("genesis_doc", genDoc)
  245. }
  246. }
  247. // Create & start node
  248. n := NewNode()
  249. l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp"))
  250. n.AddListener(l)
  251. err := n.Start()
  252. if err != nil {
  253. Exit(Fmt("Failed to start node: %v", err))
  254. }
  255. log.Notice("Started node", "nodeInfo", n.sw.NodeInfo())
  256. // If seedNode is provided by config, dial out.
  257. if config.GetString("seeds") != "" {
  258. n.DialSeed()
  259. }
  260. // Run the RPC server.
  261. if config.GetString("rpc_laddr") != "" {
  262. _, err := n.StartRPC()
  263. if err != nil {
  264. PanicCrisis(err)
  265. }
  266. }
  267. // Sleep forever and then...
  268. TrapSignal(func() {
  269. n.Stop()
  270. })
  271. }
  272. // Load the most recent state from "state" db,
  273. // or create a new one (and save) from genesis.
  274. func getState() *sm.State {
  275. stateDB := dbm.GetDB("state")
  276. state := sm.LoadState(stateDB)
  277. if state == nil {
  278. state = sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file"))
  279. state.Save()
  280. }
  281. return state
  282. }
  283. // Get a connection to the proxyAppCtx addr.
  284. // Check the current hash, and panic if it doesn't match.
  285. func getProxyApp(addr string, hash []byte) proxy.AppContext {
  286. proxyConn, err := Connect(addr)
  287. if err != nil {
  288. Exit(Fmt("Failed to connect to proxy for mempool: %v", err))
  289. }
  290. proxyAppCtx := proxy.NewRemoteAppContext(proxyConn, 1024)
  291. proxyAppCtx.Start()
  292. // Check the hash
  293. currentHash, err := proxyAppCtx.GetHashSync()
  294. if err != nil {
  295. PanicCrisis(Fmt("Error in getting proxyAppCtx hash: %v", err))
  296. }
  297. if !bytes.Equal(hash, currentHash) {
  298. PanicCrisis(Fmt("ProxyApp hash does not match. Expected %X, got %X", hash, currentHash))
  299. }
  300. return proxyAppCtx
  301. }