backend.go 12.5 KB
Newer Older
1 2 3
package eth

import (
4
	"crypto/ecdsa"
obscuren's avatar
obscuren committed
5
	"fmt"
6 7
	"io/ioutil"
	"path"
8
	"strings"
9

obscuren's avatar
obscuren committed
10
	"github.com/ethereum/ethash"
11
	"github.com/ethereum/go-ethereum/accounts"
zelig's avatar
zelig committed
12
	"github.com/ethereum/go-ethereum/blockpool"
obscuren's avatar
obscuren committed
13
	"github.com/ethereum/go-ethereum/common"
14
	"github.com/ethereum/go-ethereum/core"
15
	"github.com/ethereum/go-ethereum/core/types"
16
	"github.com/ethereum/go-ethereum/core/vm"
17
	"github.com/ethereum/go-ethereum/crypto"
obscuren's avatar
obscuren committed
18
	"github.com/ethereum/go-ethereum/ethdb"
19
	"github.com/ethereum/go-ethereum/event"
20
	"github.com/ethereum/go-ethereum/logger"
21
	"github.com/ethereum/go-ethereum/miner"
22
	"github.com/ethereum/go-ethereum/p2p"
23
	"github.com/ethereum/go-ethereum/p2p/discover"
24
	"github.com/ethereum/go-ethereum/p2p/nat"
obscuren's avatar
obscuren committed
25
	"github.com/ethereum/go-ethereum/whisper"
26 27
)

Felix Lange's avatar
Felix Lange committed
28
var (
Felix Lange's avatar
Felix Lange committed
29
	servlogger = logger.NewLogger("SERV")
30
	jsonlogger = logger.NewJsonLogger()
Felix Lange's avatar
Felix Lange committed
31 32

	defaultBootNodes = []*discover.Node{
33
		// ETH/DEV cmd/bootnode
obscuren's avatar
obscuren committed
34
		discover.MustParseNode("enode://6cdd090303f394a1cac34ecc9f7cda18127eafa2a3a06de39f6d920b0e583e062a7362097c7c65ee490a758b442acd5c80c6fce4b148c6a391e946b45131365b@54.169.166.226:30303"),
35 36
		// ETH/DEV cpp-ethereum (poc-8.ethdev.com)
		discover.MustParseNode("enode://4a44599974518ea5b0f14c31c4463692ac0329cb84851f3435e6d1b18ee4eae4aa495f846a0fa1219bd58035671881d44423876e57db2abd57254d0197da0ebe@5.1.83.226:30303"),
Felix Lange's avatar
Felix Lange committed
37 38
	}
)
39

obscuren's avatar
obscuren committed
40
type Config struct {
zelig's avatar
zelig committed
41 42 43 44
	Name            string
	ProtocolVersion int
	NetworkId       int

45 46 47 48 49
	DataDir  string
	LogFile  string
	LogLevel int
	LogJSON  string
	VmDebug  bool
obscuren's avatar
obscuren committed
50

51 52
	MaxPeers int
	Port     string
obscuren's avatar
obscuren committed
53

54 55 56 57
	// This should be a space-separated list of
	// discovery node URLs.
	BootNodes string

58 59 60 61
	// This key is used to identify the node on the network.
	// If nil, an ephemeral key is used.
	NodeKey *ecdsa.PrivateKey

62
	NAT  nat.Interface
obscuren's avatar
obscuren committed
63 64 65
	Shh  bool
	Dial bool

66
	MinerThreads   int
67
	AccountManager *accounts.Manager
68 69 70

	// NewDB is used to create databases.
	// If nil, the default is to create leveldb databases on disk.
obscuren's avatar
obscuren committed
71
	NewDB func(path string) (common.Database, error)
obscuren's avatar
obscuren committed
72 73
}

74
func (cfg *Config) parseBootNodes() []*discover.Node {
Felix Lange's avatar
Felix Lange committed
75 76 77
	if cfg.BootNodes == "" {
		return defaultBootNodes
	}
78 79
	var ns []*discover.Node
	for _, url := range strings.Split(cfg.BootNodes, " ") {
80 81 82
		if url == "" {
			continue
		}
83 84
		n, err := discover.ParseNode(url)
		if err != nil {
Felix Lange's avatar
Felix Lange committed
85
			servlogger.Errorf("Bootstrap URL %s: %v\n", url, err)
86 87 88 89 90 91
			continue
		}
		ns = append(ns, n)
	}
	return ns
}
92

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) {
	// use explicit key from command line args if set
	if cfg.NodeKey != nil {
		return cfg.NodeKey, nil
	}
	// use persistent key if present
	keyfile := path.Join(cfg.DataDir, "nodekey")
	key, err := crypto.LoadECDSA(keyfile)
	if err == nil {
		return key, nil
	}
	// no persistent key, generate and store a new one
	if key, err = crypto.GenerateKey(); err != nil {
		return nil, fmt.Errorf("could not generate server key: %v", err)
	}
	if err := ioutil.WriteFile(keyfile, crypto.FromECDSA(key), 0600); err != nil {
Felix Lange's avatar
Felix Lange committed
109
		servlogger.Errorln("could not persist nodekey: ", err)
110 111 112 113
	}
	return key, nil
}

114 115 116 117
type Ethereum struct {
	// Channel for shutting down the ethereum
	shutdownChan chan bool

118
	// DB interfaces
obscuren's avatar
obscuren committed
119 120 121
	blockDb common.Database // Block chain database
	stateDb common.Database // State changes database
	extraDb common.Database // Extra database (txs, etc)
obscuren's avatar
obscuren committed
122 123

	//*** SERVICES ***
124
	// State manager for processing new blocks and managing the over all states
125 126 127
	blockProcessor *core.BlockProcessor
	txPool         *core.TxPool
	chainManager   *core.ChainManager
zelig's avatar
zelig committed
128
	blockPool      *blockpool.BlockPool
129
	accountManager *accounts.Manager
130
	whisper        *whisper.Whisper
131
	pow            *ethash.Ethash
132

obscuren's avatar
obscuren committed
133
	net      *p2p.Server
obscuren's avatar
obscuren committed
134 135 136
	eventMux *event.TypeMux
	txSub    event.Subscription
	blockSub event.Subscription
137
	miner    *miner.Miner
138

139
	// logger logger.LogSystem
140

141 142 143 144 145 146
	Mining        bool
	DataDir       string
	clientVersion string
	ethVersionId  int
	netVersionId  int
	shhVersionId  int
147 148
}

obscuren's avatar
obscuren committed
149 150
func New(config *Config) (*Ethereum, error) {
	// Boostrap database
151 152 153 154
	logger.New(config.DataDir, config.LogFile, config.LogLevel)
	if len(config.LogJSON) > 0 {
		logger.NewJSONsystem(config.DataDir, config.LogJSON)
	}
155

156 157
	newdb := config.NewDB
	if newdb == nil {
obscuren's avatar
obscuren committed
158
		newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path) }
159 160
	}
	blockDb, err := newdb(path.Join(config.DataDir, "blockchain"))
161 162 163
	if err != nil {
		return nil, err
	}
164
	stateDb, err := newdb(path.Join(config.DataDir, "state"))
obscuren's avatar
obscuren committed
165 166 167
	if err != nil {
		return nil, err
	}
168
	extraDb, err := ethdb.NewLDBDatabase(path.Join(config.DataDir, "extra"))
obscuren's avatar
obscuren committed
169 170

	// Perform database sanity checks
171
	d, _ := blockDb.Get([]byte("ProtocolVersion"))
zelig's avatar
zelig committed
172 173
	protov := int(common.NewValue(d).Uint())
	if protov != config.ProtocolVersion && protov != 0 {
174
		path := path.Join(config.DataDir, "blockchain")
zelig's avatar
zelig committed
175
		return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, config.ProtocolVersion, path)
obscuren's avatar
obscuren committed
176
	}
177
	saveProtocolVersion(blockDb, config.ProtocolVersion)
zelig's avatar
zelig committed
178
	servlogger.Infof("Protocol Version: %v, Network Id: %v", config.ProtocolVersion, config.NetworkId)
179 180

	eth := &Ethereum{
181 182 183 184 185 186 187 188 189 190
		shutdownChan:   make(chan bool),
		blockDb:        blockDb,
		stateDb:        stateDb,
		extraDb:        extraDb,
		eventMux:       &event.TypeMux{},
		accountManager: config.AccountManager,
		DataDir:        config.DataDir,
		clientVersion:  config.Name, // TODO should separate from Name
		ethVersionId:   config.ProtocolVersion,
		netVersionId:   config.NetworkId,
191 192
	}

193
	eth.chainManager = core.NewChainManager(blockDb, stateDb, eth.EventMux())
194
	eth.pow = ethash.New(eth.chainManager)
195
	eth.txPool = core.NewTxPool(eth.EventMux())
obscuren's avatar
obscuren committed
196
	eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux())
197
	eth.chainManager.SetProcessor(eth.blockProcessor)
obscuren's avatar
obscuren committed
198
	eth.whisper = whisper.New()
199
	eth.shhVersionId = int(eth.whisper.Version())
200
	eth.miner = miner.New(eth, eth.pow, config.MinerThreads)
201 202 203

	hasBlock := eth.chainManager.HasBlock
	insertChain := eth.chainManager.InsertChain
204 205
	td := eth.chainManager.Td()
	eth.blockPool = blockpool.New(hasBlock, insertChain, eth.pow.Verify, eth.EventMux(), td)
206

207 208 209 210
	netprv, err := config.nodeKey()
	if err != nil {
		return nil, err
	}
zelig's avatar
zelig committed
211 212

	ethProto := EthProtocol(config.ProtocolVersion, config.NetworkId, eth.txPool, eth.chainManager, eth.blockPool)
213 214
	protocols := []p2p.Protocol{ethProto}
	if config.Shh {
Felix Lange's avatar
Felix Lange committed
215
		protocols = append(protocols, eth.whisper.Protocol())
216 217
	}

obscuren's avatar
obscuren committed
218
	eth.net = &p2p.Server{
219 220 221 222
		PrivateKey:     netprv,
		Name:           config.Name,
		MaxPeers:       config.MaxPeers,
		Protocols:      protocols,
223
		NAT:            config.NAT,
224 225
		NoDial:         !config.Dial,
		BootstrapNodes: config.parseBootNodes(),
obscuren's avatar
obscuren committed
226 227 228
	}
	if len(config.Port) > 0 {
		eth.net.ListenAddr = ":" + config.Port
229 230
	}

231 232
	vm.Debug = config.VmDebug

233 234 235
	return eth, nil
}

zelig's avatar
zelig committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
type NodeInfo struct {
	Name       string
	NodeUrl    string
	NodeID     string
	IP         string
	DiscPort   int // UDP listening port for discovery protocol
	TCPPort    int // TCP listening port for RLPx
	Td         string
	ListenAddr string
}

func (s *Ethereum) NodeInfo() *NodeInfo {
	node := s.net.Self()

	return &NodeInfo{
		Name:       s.Name(),
		NodeUrl:    node.String(),
		NodeID:     node.ID.String(),
		IP:         node.IP.String(),
		DiscPort:   node.DiscPort,
		TCPPort:    node.TCPPort,
		ListenAddr: s.net.ListenAddr,
		Td:         s.ChainManager().Td().String(),
	}
}

type PeerInfo struct {
	ID            string
	Name          string
	Caps          string
	RemoteAddress string
	LocalAddress  string
}

func newPeerInfo(peer *p2p.Peer) *PeerInfo {
	var caps []string
	for _, cap := range peer.Caps() {
		caps = append(caps, cap.String())
	}
	return &PeerInfo{
		ID:            peer.ID().String(),
		Name:          peer.Name(),
		Caps:          strings.Join(caps, ", "),
		RemoteAddress: peer.RemoteAddr().String(),
		LocalAddress:  peer.LocalAddr().String(),
	}
}

// PeersInfo returns an array of PeerInfo objects describing connected peers
func (s *Ethereum) PeersInfo() (peersinfo []*PeerInfo) {
	for _, peer := range s.net.Peers() {
		if peer != nil {
			peersinfo = append(peersinfo, newPeerInfo(peer))
		}
	}
	return
}

294 295 296 297 298
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
	s.chainManager.ResetWithGenesisBlock(gb)
	s.pow.UpdateCache(true)
}

299 300 301 302 303 304
func (s *Ethereum) StartMining() error {
	cb, err := s.accountManager.Coinbase()
	if err != nil {
		servlogger.Errorf("Cannot start mining without coinbase: %v\n", err)
		return fmt.Errorf("no coinbase: %v", err)
	}
obscuren's avatar
obscuren committed
305
	s.miner.Start(common.BytesToAddress(cb))
306 307 308
	return nil
}

309 310 311
func (s *Ethereum) StopMining()         { s.miner.Stop() }
func (s *Ethereum) IsMining() bool      { return s.miner.Mining() }
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
312

313
// func (s *Ethereum) Logger() logger.LogSystem             { return s.logger }
314 315 316 317 318 319 320 321
func (s *Ethereum) Name() string                         { return s.net.Name }
func (s *Ethereum) AccountManager() *accounts.Manager    { return s.accountManager }
func (s *Ethereum) ChainManager() *core.ChainManager     { return s.chainManager }
func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
func (s *Ethereum) TxPool() *core.TxPool                 { return s.txPool }
func (s *Ethereum) BlockPool() *blockpool.BlockPool      { return s.blockPool }
func (s *Ethereum) Whisper() *whisper.Whisper            { return s.whisper }
func (s *Ethereum) EventMux() *event.TypeMux             { return s.eventMux }
obscuren's avatar
obscuren committed
322 323 324
func (s *Ethereum) BlockDb() common.Database             { return s.blockDb }
func (s *Ethereum) StateDb() common.Database             { return s.stateDb }
func (s *Ethereum) ExtraDb() common.Database             { return s.extraDb }
325 326 327 328
func (s *Ethereum) IsListening() bool                    { return true } // Always listening
func (s *Ethereum) PeerCount() int                       { return s.net.PeerCount() }
func (s *Ethereum) Peers() []*p2p.Peer                   { return s.net.Peers() }
func (s *Ethereum) MaxPeers() int                        { return s.net.MaxPeers }
329 330 331 332
func (s *Ethereum) ClientVersion() string                { return s.clientVersion }
func (s *Ethereum) EthVersion() int                      { return s.ethVersionId }
func (s *Ethereum) NetVersion() int                      { return s.netVersionId }
func (s *Ethereum) ShhVersion() int                      { return s.shhVersionId }
obscuren's avatar
obscuren committed
333

334
// Start the ethereum
335
func (s *Ethereum) Start() error {
336
	jsonlogger.LogJson(&logger.LogStarting{
obscuren's avatar
obscuren committed
337
		ClientString:    s.net.Name,
338 339 340
		ProtocolVersion: ProtocolVersion,
	})

zelig's avatar
zelig committed
341 342 343 344 345
	if s.net.MaxPeers > 0 {
		err := s.net.Start()
		if err != nil {
			return err
		}
346
	}
obscuren's avatar
obscuren committed
347 348 349

	// Start services
	s.txPool.Start()
350
	s.blockPool.Start()
obscuren's avatar
obscuren committed
351 352 353 354

	if s.whisper != nil {
		s.whisper.Start()
	}
355 356 357 358 359 360

	// broadcast transactions
	s.txSub = s.eventMux.Subscribe(core.TxPreEvent{})
	go s.txBroadcastLoop()

	// broadcast mined blocks
obscuren's avatar
obscuren committed
361
	s.blockSub = s.eventMux.Subscribe(core.ChainHeadEvent{})
362 363
	go s.blockBroadcastLoop()

Felix Lange's avatar
Felix Lange committed
364
	servlogger.Infoln("Server started")
365 366 367
	return nil
}

368
func (s *Ethereum) StartForTest() {
369
	jsonlogger.LogJson(&logger.LogStarting{
370 371 372 373 374 375 376 377 378
		ClientString:    s.net.Name,
		ProtocolVersion: ProtocolVersion,
	})

	// Start services
	s.txPool.Start()
	s.blockPool.Start()
}

379 380
func (self *Ethereum) SuggestPeer(nodeURL string) error {
	n, err := discover.ParseNode(nodeURL)
381
	if err != nil {
382
		return fmt.Errorf("invalid node URL: %v", err)
383
	}
384
	self.net.SuggestPeer(n)
385 386 387 388 389
	return nil
}

func (s *Ethereum) Stop() {
	// Close the database
390 391
	defer s.blockDb.Close()
	defer s.stateDb.Close()
zelig's avatar
zelig committed
392
	defer s.extraDb.Close()
393 394 395 396 397 398 399

	s.txSub.Unsubscribe()    // quits txBroadcastLoop
	s.blockSub.Unsubscribe() // quits blockBroadcastLoop

	s.txPool.Stop()
	s.eventMux.Stop()
	s.blockPool.Stop()
obscuren's avatar
obscuren committed
400 401 402
	if s.whisper != nil {
		s.whisper.Stop()
	}
403

Felix Lange's avatar
Felix Lange committed
404
	servlogger.Infoln("Server stopped")
405 406 407 408 409 410 411 412 413 414 415 416 417 418
	close(s.shutdownChan)
}

// This function will wait for a shutdown and resumes main thread execution
func (s *Ethereum) WaitForShutdown() {
	<-s.shutdownChan
}

// now tx broadcasting is taken out of txPool
// handled here via subscription, efficiency?
func (self *Ethereum) txBroadcastLoop() {
	// automatically stops if unsubscribe
	for obj := range self.txSub.Chan() {
		event := obj.(core.TxPreEvent)
419
		self.net.Broadcast("eth", TxMsg, []*types.Transaction{event.Tx})
420 421 422 423 424
	}
}

func (self *Ethereum) blockBroadcastLoop() {
	// automatically stops if unsubscribe
obscuren's avatar
obscuren committed
425
	for obj := range self.blockSub.Chan() {
426
		switch ev := obj.(type) {
obscuren's avatar
obscuren committed
427
		case core.ChainHeadEvent:
428
			self.net.Broadcast("eth", NewBlockMsg, []interface{}{ev.Block, ev.Block.Td})
429
		}
430 431 432
	}
}

zelig's avatar
zelig committed
433
func saveProtocolVersion(db common.Database, protov int) {
434
	d, _ := db.Get([]byte("ProtocolVersion"))
obscuren's avatar
obscuren committed
435
	protocolVersion := common.NewValue(d).Uint()
436 437

	if protocolVersion == 0 {
zelig's avatar
zelig committed
438
		db.Put([]byte("ProtocolVersion"), common.NewValue(protov).Bytes())
439 440
	}
}