github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/eth/backend.go (about) 1 package eth 2 3 import ( 4 "crypto/ecdsa" 5 "fmt" 6 "strings" 7 8 "github.com/jonasnick/go-ethereum/core" 9 "github.com/jonasnick/go-ethereum/crypto" 10 "github.com/jonasnick/go-ethereum/ethdb" 11 "github.com/jonasnick/go-ethereum/ethutil" 12 "github.com/jonasnick/go-ethereum/event" 13 ethlogger "github.com/jonasnick/go-ethereum/logger" 14 "github.com/jonasnick/go-ethereum/p2p" 15 "github.com/jonasnick/go-ethereum/p2p/discover" 16 "github.com/jonasnick/go-ethereum/p2p/nat" 17 "github.com/jonasnick/go-ethereum/pow/ezp" 18 "github.com/jonasnick/go-ethereum/rpc" 19 "github.com/jonasnick/go-ethereum/whisper" 20 ) 21 22 var ( 23 logger = ethlogger.NewLogger("SERV") 24 jsonlogger = ethlogger.NewJsonLogger() 25 26 defaultBootNodes = []*discover.Node{ 27 discover.MustParseNode("enode://6cdd090303f394a1cac34ecc9f7cda18127eafa2a3a06de39f6d920b0e583e062a7362097c7c65ee490a758b442acd5c80c6fce4b148c6a391e946b45131365b@54.169.166.226:30303"), 28 } 29 ) 30 31 type Config struct { 32 Name string 33 KeyStore string 34 DataDir string 35 LogFile string 36 LogLevel int 37 KeyRing string 38 LogFormat string 39 40 MaxPeers int 41 Port string 42 43 // This should be a space-separated list of 44 // discovery node URLs. 45 BootNodes string 46 47 // This key is used to identify the node on the network. 48 // If nil, an ephemeral key is used. 49 NodeKey *ecdsa.PrivateKey 50 51 NAT nat.Interface 52 Shh bool 53 Dial bool 54 55 KeyManager *crypto.KeyManager 56 } 57 58 func (cfg *Config) parseBootNodes() []*discover.Node { 59 if cfg.BootNodes == "" { 60 return defaultBootNodes 61 } 62 var ns []*discover.Node 63 for _, url := range strings.Split(cfg.BootNodes, " ") { 64 if url == "" { 65 continue 66 } 67 n, err := discover.ParseNode(url) 68 if err != nil { 69 logger.Errorf("Bootstrap URL %s: %v\n", url, err) 70 continue 71 } 72 ns = append(ns, n) 73 } 74 return ns 75 } 76 77 type Ethereum struct { 78 // Channel for shutting down the ethereum 79 shutdownChan chan bool 80 quit chan bool 81 82 // DB interface 83 db ethutil.Database 84 blacklist p2p.Blacklist 85 86 //*** SERVICES *** 87 // State manager for processing new blocks and managing the over all states 88 blockProcessor *core.BlockProcessor 89 txPool *core.TxPool 90 chainManager *core.ChainManager 91 blockPool *BlockPool 92 whisper *whisper.Whisper 93 94 net *p2p.Server 95 eventMux *event.TypeMux 96 txSub event.Subscription 97 blockSub event.Subscription 98 99 RpcServer rpc.RpcServer 100 WsServer rpc.RpcServer 101 keyManager *crypto.KeyManager 102 103 logger ethlogger.LogSystem 104 105 Mining bool 106 } 107 108 func New(config *Config) (*Ethereum, error) { 109 // Boostrap database 110 logger := ethlogger.New(config.DataDir, config.LogFile, config.LogLevel, config.LogFormat) 111 db, err := ethdb.NewLDBDatabase("blockchain") 112 if err != nil { 113 return nil, err 114 } 115 116 // Perform database sanity checks 117 d, _ := db.Get([]byte("ProtocolVersion")) 118 protov := ethutil.NewValue(d).Uint() 119 if protov != ProtocolVersion && protov != 0 { 120 return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, ProtocolVersion, ethutil.Config.ExecPath+"/database") 121 } 122 123 // Create new keymanager 124 var keyManager *crypto.KeyManager 125 switch config.KeyStore { 126 case "db": 127 keyManager = crypto.NewDBKeyManager(db) 128 case "file": 129 keyManager = crypto.NewFileKeyManager(config.DataDir) 130 default: 131 return nil, fmt.Errorf("unknown keystore type: %s", config.KeyStore) 132 } 133 // Initialise the keyring 134 keyManager.Init(config.KeyRing, 0, false) 135 136 saveProtocolVersion(db) 137 //ethutil.Config.Db = db 138 139 eth := &Ethereum{ 140 shutdownChan: make(chan bool), 141 quit: make(chan bool), 142 db: db, 143 keyManager: keyManager, 144 blacklist: p2p.NewBlacklist(), 145 eventMux: &event.TypeMux{}, 146 logger: logger, 147 } 148 149 eth.chainManager = core.NewChainManager(db, eth.EventMux()) 150 eth.txPool = core.NewTxPool(eth.EventMux()) 151 eth.blockProcessor = core.NewBlockProcessor(db, eth.txPool, eth.chainManager, eth.EventMux()) 152 eth.chainManager.SetProcessor(eth.blockProcessor) 153 eth.whisper = whisper.New() 154 155 hasBlock := eth.chainManager.HasBlock 156 insertChain := eth.chainManager.InsertChain 157 eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify) 158 159 ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool) 160 protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()} 161 netprv := config.NodeKey 162 if netprv == nil { 163 if netprv, err = crypto.GenerateKey(); err != nil { 164 return nil, fmt.Errorf("could not generate server key: %v", err) 165 } 166 } 167 eth.net = &p2p.Server{ 168 PrivateKey: netprv, 169 Name: config.Name, 170 MaxPeers: config.MaxPeers, 171 Protocols: protocols, 172 Blacklist: eth.blacklist, 173 NAT: config.NAT, 174 NoDial: !config.Dial, 175 BootstrapNodes: config.parseBootNodes(), 176 } 177 if len(config.Port) > 0 { 178 eth.net.ListenAddr = ":" + config.Port 179 } 180 181 return eth, nil 182 } 183 184 func (s *Ethereum) KeyManager() *crypto.KeyManager { 185 return s.keyManager 186 } 187 188 func (s *Ethereum) Logger() ethlogger.LogSystem { 189 return s.logger 190 } 191 192 func (s *Ethereum) Name() string { 193 return s.net.Name 194 } 195 196 func (s *Ethereum) ChainManager() *core.ChainManager { 197 return s.chainManager 198 } 199 200 func (s *Ethereum) BlockProcessor() *core.BlockProcessor { 201 return s.blockProcessor 202 } 203 204 func (s *Ethereum) TxPool() *core.TxPool { 205 return s.txPool 206 } 207 208 func (s *Ethereum) BlockPool() *BlockPool { 209 return s.blockPool 210 } 211 212 func (s *Ethereum) Whisper() *whisper.Whisper { 213 return s.whisper 214 } 215 216 func (s *Ethereum) EventMux() *event.TypeMux { 217 return s.eventMux 218 } 219 func (self *Ethereum) Db() ethutil.Database { 220 return self.db 221 } 222 223 func (s *Ethereum) IsMining() bool { 224 return s.Mining 225 } 226 227 func (s *Ethereum) IsListening() bool { 228 // XXX TODO 229 return false 230 } 231 232 func (s *Ethereum) PeerCount() int { 233 return s.net.PeerCount() 234 } 235 236 func (s *Ethereum) Peers() []*p2p.Peer { 237 return s.net.Peers() 238 } 239 240 func (s *Ethereum) MaxPeers() int { 241 return s.net.MaxPeers 242 } 243 244 func (s *Ethereum) Coinbase() []byte { 245 return nil // TODO 246 } 247 248 // Start the ethereum 249 func (s *Ethereum) Start() error { 250 jsonlogger.LogJson(ðlogger.LogStarting{ 251 ClientString: s.net.Name, 252 Coinbase: ethutil.Bytes2Hex(s.KeyManager().Address()), 253 ProtocolVersion: ProtocolVersion, 254 LogEvent: ethlogger.LogEvent{Guid: ethutil.Bytes2Hex(crypto.FromECDSAPub(&s.net.PrivateKey.PublicKey))}, 255 }) 256 257 err := s.net.Start() 258 if err != nil { 259 return err 260 } 261 262 // Start services 263 s.txPool.Start() 264 s.blockPool.Start() 265 266 if s.whisper != nil { 267 s.whisper.Start() 268 } 269 270 // broadcast transactions 271 s.txSub = s.eventMux.Subscribe(core.TxPreEvent{}) 272 go s.txBroadcastLoop() 273 274 // broadcast mined blocks 275 s.blockSub = s.eventMux.Subscribe(core.NewMinedBlockEvent{}) 276 go s.blockBroadcastLoop() 277 278 logger.Infoln("Server started") 279 return nil 280 } 281 282 func (self *Ethereum) SuggestPeer(nodeURL string) error { 283 n, err := discover.ParseNode(nodeURL) 284 if err != nil { 285 return fmt.Errorf("invalid node URL: %v", err) 286 } 287 self.net.SuggestPeer(n) 288 return nil 289 } 290 291 func (s *Ethereum) Stop() { 292 // Close the database 293 defer s.db.Close() 294 295 close(s.quit) 296 297 s.txSub.Unsubscribe() // quits txBroadcastLoop 298 s.blockSub.Unsubscribe() // quits blockBroadcastLoop 299 300 if s.RpcServer != nil { 301 s.RpcServer.Stop() 302 } 303 if s.WsServer != nil { 304 s.WsServer.Stop() 305 } 306 s.txPool.Stop() 307 s.eventMux.Stop() 308 s.blockPool.Stop() 309 if s.whisper != nil { 310 s.whisper.Stop() 311 } 312 313 logger.Infoln("Server stopped") 314 close(s.shutdownChan) 315 } 316 317 // This function will wait for a shutdown and resumes main thread execution 318 func (s *Ethereum) WaitForShutdown() { 319 <-s.shutdownChan 320 } 321 322 // now tx broadcasting is taken out of txPool 323 // handled here via subscription, efficiency? 324 func (self *Ethereum) txBroadcastLoop() { 325 // automatically stops if unsubscribe 326 for obj := range self.txSub.Chan() { 327 event := obj.(core.TxPreEvent) 328 self.net.Broadcast("eth", TxMsg, event.Tx.RlpData()) 329 } 330 } 331 332 func (self *Ethereum) blockBroadcastLoop() { 333 // automatically stops if unsubscribe 334 for obj := range self.blockSub.Chan() { 335 switch ev := obj.(type) { 336 case core.NewMinedBlockEvent: 337 self.net.Broadcast("eth", NewBlockMsg, ev.Block.RlpData(), ev.Block.Td) 338 } 339 } 340 } 341 342 func saveProtocolVersion(db ethutil.Database) { 343 d, _ := db.Get([]byte("ProtocolVersion")) 344 protocolVersion := ethutil.NewValue(d).Uint() 345 346 if protocolVersion == 0 { 347 db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes()) 348 } 349 }