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

import (
4
	"crypto/ecdsa"
5
	"encoding/json"
obscuren's avatar
obscuren committed
6
	"fmt"
7
	"io/ioutil"
8
	"math/big"
9
	"os"
10
	"path/filepath"
11
	"strings"
12
	"time"
13

obscuren's avatar
obscuren committed
14
	"github.com/ethereum/ethash"
15
	"github.com/ethereum/go-ethereum/accounts"
obscuren's avatar
obscuren committed
16
	"github.com/ethereum/go-ethereum/common"
17
	"github.com/ethereum/go-ethereum/common/compiler"
18
	"github.com/ethereum/go-ethereum/core"
19
	"github.com/ethereum/go-ethereum/core/types"
20
	"github.com/ethereum/go-ethereum/core/vm"
21
	"github.com/ethereum/go-ethereum/crypto"
22
	"github.com/ethereum/go-ethereum/eth/downloader"
obscuren's avatar
obscuren committed
23
	"github.com/ethereum/go-ethereum/ethdb"
24
	"github.com/ethereum/go-ethereum/event"
25
	"github.com/ethereum/go-ethereum/logger"
obscuren's avatar
obscuren committed
26
	"github.com/ethereum/go-ethereum/logger/glog"
27
	"github.com/ethereum/go-ethereum/metrics"
28
	"github.com/ethereum/go-ethereum/miner"
29
	"github.com/ethereum/go-ethereum/p2p"
30
	"github.com/ethereum/go-ethereum/p2p/discover"
31
	"github.com/ethereum/go-ethereum/p2p/nat"
obscuren's avatar
obscuren committed
32
	"github.com/ethereum/go-ethereum/whisper"
33 34
)

35 36 37 38 39 40 41 42
const (
	epochLength    = 30000
	ethashRevision = 23

	autoDAGcheckInterval = 10 * time.Hour
	autoDAGepochHeight   = epochLength / 2
)

Felix Lange's avatar
Felix Lange committed
43
var (
44
	jsonlogger = logger.NewJsonLogger()
Felix Lange's avatar
Felix Lange committed
45 46

	defaultBootNodes = []*discover.Node{
Taylor Gerring's avatar
Taylor Gerring committed
47 48
		// ETH/DEV Go Bootnodes
		discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"),
Taylor Gerring's avatar
Taylor Gerring committed
49
		discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"),
50 51
		// ETH/DEV cpp-ethereum (poc-9.ethdev.com)
		discover.MustParseNode("enode://487611428e6c99a11a9795a6abe7b529e81315ca6aad66e2a2fc76e3adf263faba0d35466c2f8f68d561dbefa8878d4df5f1f2ddb1fbeab7f42ffb8cd328bd4a@5.1.83.226:30303"),
Felix Lange's avatar
Felix Lange committed
52
	}
53

54 55
	staticNodes  = "static-nodes.json"  // Path within <datadir> to search for the static node list
	trustedNodes = "trusted-nodes.json" // Path within <datadir> to search for the trusted node list
Felix Lange's avatar
Felix Lange committed
56
)
57

obscuren's avatar
obscuren committed
58
type Config struct {
59 60 61
	Name         string
	NetworkId    int
	GenesisNonce int
zelig's avatar
zelig committed
62

63 64 65
	BlockChainVersion  int
	SkipBcVersionCheck bool // e.g. blockchain export

66 67 68 69 70 71
	DataDir   string
	LogFile   string
	Verbosity int
	LogJSON   string
	VmDebug   bool
	NatSpec   bool
72
	AutoDAG   bool
73
	PowTest   bool
obscuren's avatar
obscuren committed
74

75 76
	MaxPeers        int
	MaxPendingPeers int
77
	Discovery       bool
78
	Port            string
obscuren's avatar
obscuren committed
79

80
	// Space-separated list of discovery node URLs
81 82
	BootNodes string

83 84 85 86
	// This key is used to identify the node on the network.
	// If nil, an ephemeral key is used.
	NodeKey *ecdsa.PrivateKey

87
	NAT  nat.Interface
obscuren's avatar
obscuren committed
88 89 90
	Shh  bool
	Dial bool

zelig's avatar
zelig committed
91
	Etherbase      string
92
	GasPrice       *big.Int
93
	MinerThreads   int
94
	AccountManager *accounts.Manager
95
	SolcPath       string
96

zsfelfoldi's avatar
zsfelfoldi committed
97 98 99 100 101 102 103
	GpoMinGasPrice          *big.Int
	GpoMaxGasPrice          *big.Int
	GpoFullBlockRatio       int
	GpobaseStepDown         int
	GpobaseStepUp           int
	GpobaseCorrectionFactor int

104 105
	// NewDB is used to create databases.
	// If nil, the default is to create leveldb databases on disk.
obscuren's avatar
obscuren committed
106
	NewDB func(path string) (common.Database, error)
obscuren's avatar
obscuren committed
107 108
}

109
func (cfg *Config) parseBootNodes() []*discover.Node {
Felix Lange's avatar
Felix Lange committed
110 111 112
	if cfg.BootNodes == "" {
		return defaultBootNodes
	}
113 114
	var ns []*discover.Node
	for _, url := range strings.Split(cfg.BootNodes, " ") {
115 116 117
		if url == "" {
			continue
		}
118 119
		n, err := discover.ParseNode(url)
		if err != nil {
obscuren's avatar
obscuren committed
120
			glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
121 122 123 124 125 126
			continue
		}
		ns = append(ns, n)
	}
	return ns
}
127

128 129 130 131
// parseNodes parses a list of discovery node URLs loaded from a .json file.
func (cfg *Config) parseNodes(file string) []*discover.Node {
	// Short circuit if no node config is present
	path := filepath.Join(cfg.DataDir, file)
132
	if _, err := os.Stat(path); err != nil {
133 134
		return nil
	}
135
	// Load the nodes from the config file
136 137
	blob, err := ioutil.ReadFile(path)
	if err != nil {
138
		glog.V(logger.Error).Infof("Failed to access nodes: %v", err)
139 140 141 142
		return nil
	}
	nodelist := []string{}
	if err := json.Unmarshal(blob, &nodelist); err != nil {
143
		glog.V(logger.Error).Infof("Failed to load nodes: %v", err)
144
		return nil
145 146 147
	}
	// Interpret the list as a discovery node array
	var nodes []*discover.Node
148
	for _, url := range nodelist {
149 150 151 152 153
		if url == "" {
			continue
		}
		node, err := discover.ParseNode(url)
		if err != nil {
154
			glog.V(logger.Error).Infof("Node URL %s: %v\n", url, err)
155 156 157 158 159 160 161
			continue
		}
		nodes = append(nodes, node)
	}
	return nodes
}

162 163 164 165 166 167
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
168
	keyfile := filepath.Join(cfg.DataDir, "nodekey")
169 170 171 172 173 174 175 176
	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)
	}
177
	if err := crypto.SaveECDSA(keyfile, key); err != nil {
obscuren's avatar
obscuren committed
178
		glog.V(logger.Error).Infoln("could not persist nodekey: ", err)
179 180 181 182
	}
	return key, nil
}

183 184 185 186
type Ethereum struct {
	// Channel for shutting down the ethereum
	shutdownChan chan bool

187
	// DB interfaces
obscuren's avatar
obscuren committed
188 189 190
	blockDb common.Database // Block chain database
	stateDb common.Database // State changes database
	extraDb common.Database // Extra database (txs, etc)
191

192 193
	// Closed when databases are flushed and closed
	databasesClosed chan bool
obscuren's avatar
obscuren committed
194 195

	//*** SERVICES ***
196
	// State manager for processing new blocks and managing the over all states
197 198 199 200 201 202 203
	blockProcessor  *core.BlockProcessor
	txPool          *core.TxPool
	chainManager    *core.ChainManager
	accountManager  *accounts.Manager
	whisper         *whisper.Whisper
	pow             *ethash.Ethash
	protocolManager *ProtocolManager
204 205
	SolcPath        string
	solc            *compiler.Solidity
206

zsfelfoldi's avatar
zsfelfoldi committed
207 208 209 210 211 212 213
	GpoMinGasPrice          *big.Int
	GpoMaxGasPrice          *big.Int
	GpoFullBlockRatio       int
	GpobaseStepDown         int
	GpobaseStepUp           int
	GpobaseCorrectionFactor int

214 215 216
	net      *p2p.Server
	eventMux *event.TypeMux
	miner    *miner.Miner
217

218
	// logger logger.LogSystem
219

220
	Mining        bool
221
	MinerThreads  int
222
	NatSpec       bool
223
	DataDir       string
224
	AutoDAG       bool
225
	PowTest       bool
226
	autodagquit   chan bool
zelig's avatar
zelig committed
227
	etherbase     common.Address
228 229 230
	clientVersion string
	netVersionId  int
	shhVersionId  int
231 232
}

obscuren's avatar
obscuren committed
233
func New(config *Config) (*Ethereum, error) {
234
	// Bootstrap database
235
	logger.New(config.DataDir, config.LogFile, config.Verbosity)
236 237 238
	if len(config.LogJSON) > 0 {
		logger.NewJSONsystem(config.DataDir, config.LogJSON)
	}
239

240
	// Let the database take 3/4 of the max open files (TODO figure out a way to get the actual limit of the open files)
241
	const dbCount = 3
242
	ethdb.OpenFileLimit = 128 / (dbCount + 1)
243

244 245
	newdb := config.NewDB
	if newdb == nil {
obscuren's avatar
obscuren committed
246
		newdb = func(path string) (common.Database, error) { return ethdb.NewLDBDatabase(path) }
247
	}
248
	blockDb, err := newdb(filepath.Join(config.DataDir, "blockchain"))
249
	if err != nil {
250
		return nil, fmt.Errorf("blockchain db err: %v", err)
251
	}
252
	if db, ok := blockDb.(*ethdb.LDBDatabase); ok {
253 254 255 256 257 258 259 260
		db.GetTimer = metrics.NewTimer("eth/db/block/user/gets")
		db.PutTimer = metrics.NewTimer("eth/db/block/user/puts")
		db.MissMeter = metrics.NewMeter("eth/db/block/user/misses")
		db.ReadMeter = metrics.NewMeter("eth/db/block/user/reads")
		db.WriteMeter = metrics.NewMeter("eth/db/block/user/writes")
		db.CompTimeMeter = metrics.NewMeter("eth/db/block/compact/time")
		db.CompReadMeter = metrics.NewMeter("eth/db/block/compact/input")
		db.CompWriteMeter = metrics.NewMeter("eth/db/block/compact/output")
261
	}
262
	stateDb, err := newdb(filepath.Join(config.DataDir, "state"))
obscuren's avatar
obscuren committed
263
	if err != nil {
264
		return nil, fmt.Errorf("state db err: %v", err)
obscuren's avatar
obscuren committed
265
	}
266
	if db, ok := stateDb.(*ethdb.LDBDatabase); ok {
267 268 269 270 271 272 273 274
		db.GetTimer = metrics.NewTimer("eth/db/state/user/gets")
		db.PutTimer = metrics.NewTimer("eth/db/state/user/puts")
		db.MissMeter = metrics.NewMeter("eth/db/state/user/misses")
		db.ReadMeter = metrics.NewMeter("eth/db/state/user/reads")
		db.WriteMeter = metrics.NewMeter("eth/db/state/user/writes")
		db.CompTimeMeter = metrics.NewMeter("eth/db/state/compact/time")
		db.CompReadMeter = metrics.NewMeter("eth/db/state/compact/input")
		db.CompWriteMeter = metrics.NewMeter("eth/db/state/compact/output")
275
	}
276
	extraDb, err := newdb(filepath.Join(config.DataDir, "extra"))
277
	if err != nil {
278
		return nil, fmt.Errorf("extra db err: %v", err)
279
	}
280
	if db, ok := extraDb.(*ethdb.LDBDatabase); ok {
281 282 283 284 285 286 287 288
		db.GetTimer = metrics.NewTimer("eth/db/extra/user/gets")
		db.PutTimer = metrics.NewTimer("eth/db/extra/user/puts")
		db.MissMeter = metrics.NewMeter("eth/db/extra/user/misses")
		db.ReadMeter = metrics.NewMeter("eth/db/extra/user/reads")
		db.WriteMeter = metrics.NewMeter("eth/db/extra/user/writes")
		db.CompTimeMeter = metrics.NewMeter("eth/db/extra/compact/time")
		db.CompReadMeter = metrics.NewMeter("eth/db/extra/compact/input")
		db.CompWriteMeter = metrics.NewMeter("eth/db/extra/compact/output")
289
	}
290
	nodeDb := filepath.Join(config.DataDir, "nodes")
obscuren's avatar
obscuren committed
291 292

	// Perform database sanity checks
293 294 295 296 297 298 299 300 301 302 303 304 305 306
	/*
		// The databases were previously tied to protocol versions. Currently we
		// are moving away from this decision as approaching Frontier. The below
		// check was left in for now but should eventually be just dropped.

		d, _ := blockDb.Get([]byte("ProtocolVersion"))
		protov := int(common.NewValue(d).Uint())
		if protov != config.ProtocolVersion && protov != 0 {
			path := filepath.Join(config.DataDir, "blockchain")
			return nil, fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, config.ProtocolVersion, path)
		}
		saveProtocolVersion(blockDb, config.ProtocolVersion)
	*/
	glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId)
307

308 309 310 311 312 313 314 315 316 317
	if !config.SkipBcVersionCheck {
		b, _ := blockDb.Get([]byte("BlockchainVersion"))
		bcVersion := int(common.NewValue(b).Uint())
		if bcVersion != config.BlockChainVersion && bcVersion != 0 {
			return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, config.BlockChainVersion)
		}
		saveBlockchainVersion(blockDb, config.BlockChainVersion)
	}
	glog.V(logger.Info).Infof("Blockchain DB Version: %d", config.BlockChainVersion)

318
	eth := &Ethereum{
zsfelfoldi's avatar
zsfelfoldi committed
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
		shutdownChan:            make(chan bool),
		databasesClosed:         make(chan bool),
		blockDb:                 blockDb,
		stateDb:                 stateDb,
		extraDb:                 extraDb,
		eventMux:                &event.TypeMux{},
		accountManager:          config.AccountManager,
		DataDir:                 config.DataDir,
		etherbase:               common.HexToAddress(config.Etherbase),
		clientVersion:           config.Name, // TODO should separate from Name
		netVersionId:            config.NetworkId,
		NatSpec:                 config.NatSpec,
		MinerThreads:            config.MinerThreads,
		SolcPath:                config.SolcPath,
		AutoDAG:                 config.AutoDAG,
334
		PowTest:                 config.PowTest,
zsfelfoldi's avatar
zsfelfoldi committed
335 336 337 338 339 340
		GpoMinGasPrice:          config.GpoMinGasPrice,
		GpoMaxGasPrice:          config.GpoMaxGasPrice,
		GpoFullBlockRatio:       config.GpoFullBlockRatio,
		GpobaseStepDown:         config.GpobaseStepDown,
		GpobaseStepUp:           config.GpobaseStepUp,
		GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
341 342
	}

343 344 345 346 347 348 349 350 351
	if config.PowTest {
		glog.V(logger.Info).Infof("ethash used in test mode")
		eth.pow, err = ethash.NewForTesting()
		if err != nil {
			return nil, err
		}
	} else {
		eth.pow = ethash.New()
	}
352
	genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
353
	eth.chainManager, err = core.NewChainManager(genesis, blockDb, stateDb, extraDb, eth.pow, eth.EventMux())
354 355 356
	if err != nil {
		return nil, err
	}
357
	eth.txPool = core.NewTxPool(eth.EventMux(), eth.chainManager.State, eth.chainManager.GasLimit)
358

359
	eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.chainManager, eth.EventMux())
360
	eth.chainManager.SetProcessor(eth.blockProcessor)
361
	eth.protocolManager = NewProtocolManager(config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.chainManager)
362

363
	eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
364
	eth.miner.SetGasPrice(config.GasPrice)
365 366 367 368
	if config.Shh {
		eth.whisper = whisper.New()
		eth.shhVersionId = int(eth.whisper.Version())
	}
369

370 371 372 373
	netprv, err := config.nodeKey()
	if err != nil {
		return nil, err
	}
374
	protocols := append([]p2p.Protocol{}, eth.protocolManager.SubProtocols...)
375
	if config.Shh {
Felix Lange's avatar
Felix Lange committed
376
		protocols = append(protocols, eth.whisper.Protocol())
377
	}
obscuren's avatar
obscuren committed
378
	eth.net = &p2p.Server{
379 380 381 382
		PrivateKey:      netprv,
		Name:            config.Name,
		MaxPeers:        config.MaxPeers,
		MaxPendingPeers: config.MaxPendingPeers,
383
		Discovery:       config.Discovery,
384 385 386 387 388 389 390
		Protocols:       protocols,
		NAT:             config.NAT,
		NoDial:          !config.Dial,
		BootstrapNodes:  config.parseBootNodes(),
		StaticNodes:     config.parseNodes(staticNodes),
		TrustedNodes:    config.parseNodes(trustedNodes),
		NodeDatabase:    nodeDb,
obscuren's avatar
obscuren committed
391 392 393
	}
	if len(config.Port) > 0 {
		eth.net.ListenAddr = ":" + config.Port
394 395
	}

396 397
	vm.Debug = config.VmDebug

398 399 400
	return eth, nil
}

zelig's avatar
zelig committed
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
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(),
420 421
		DiscPort:   int(node.UDP),
		TCPPort:    int(node.TCP),
zelig's avatar
zelig committed
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
		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
}

459 460 461 462
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
	s.chainManager.ResetWithGenesisBlock(gb)
}

463
func (s *Ethereum) StartMining(threads int) error {
zelig's avatar
zelig committed
464
	eb, err := s.Etherbase()
465
	if err != nil {
zelig's avatar
zelig committed
466
		err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)
obscuren's avatar
obscuren committed
467
		glog.V(logger.Error).Infoln(err)
zelig's avatar
zelig committed
468
		return err
469
	}
zelig's avatar
zelig committed
470

471
	go s.miner.Start(eb, threads)
472 473 474
	return nil
}

zelig's avatar
zelig committed
475 476 477
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
	eb = s.etherbase
	if (eb == common.Address{}) {
478
		err = fmt.Errorf("etherbase address must be explicitly specified")
zelig's avatar
zelig committed
479
	}
480
	return
zelig's avatar
zelig committed
481 482
}

483 484 485
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 }
486

487
// func (s *Ethereum) Logger() logger.LogSystem             { return s.logger }
488 489 490 491 492 493 494
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) Whisper() *whisper.Whisper            { return s.whisper }
func (s *Ethereum) EventMux() *event.TypeMux             { return s.eventMux }
obscuren's avatar
obscuren committed
495 496 497
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 }
498 499 500 501
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 }
502
func (s *Ethereum) ClientVersion() string                { return s.clientVersion }
503
func (s *Ethereum) EthVersion() int                      { return int(s.protocolManager.SubProtocols[0].Version) }
504 505
func (s *Ethereum) NetVersion() int                      { return s.netVersionId }
func (s *Ethereum) ShhVersion() int                      { return s.shhVersionId }
506
func (s *Ethereum) Downloader() *downloader.Downloader   { return s.protocolManager.downloader }
obscuren's avatar
obscuren committed
507

508
// Start the ethereum
509
func (s *Ethereum) Start() error {
510
	jsonlogger.LogJson(&logger.LogStarting{
obscuren's avatar
obscuren committed
511
		ClientString:    s.net.Name,
512
		ProtocolVersion: s.EthVersion(),
513
	})
514 515 516
	err := s.net.Start()
	if err != nil {
		return err
517
	}
518 519 520
	// periodically flush databases
	go s.syncDatabases()

521 522 523 524
	if s.AutoDAG {
		s.StartAutoDAG()
	}

525
	s.protocolManager.Start()
obscuren's avatar
obscuren committed
526 527 528 529

	if s.whisper != nil {
		s.whisper.Start()
	}
530

obscuren's avatar
obscuren committed
531
	glog.V(logger.Info).Infoln("Server started")
532 533 534
	return nil
}

535 536
// sync databases every minute. If flushing fails we exit immediatly. The system
// may not continue under any circumstances.
537 538 539 540 541 542 543 544
func (s *Ethereum) syncDatabases() {
	ticker := time.NewTicker(1 * time.Minute)
done:
	for {
		select {
		case <-ticker.C:
			// don't change the order of database flushes
			if err := s.extraDb.Flush(); err != nil {
545
				glog.Fatalf("fatal error: flush extraDb: %v (Restart your node. We are aware of this issue)\n", err)
546 547
			}
			if err := s.stateDb.Flush(); err != nil {
548
				glog.Fatalf("fatal error: flush stateDb: %v (Restart your node. We are aware of this issue)\n", err)
549 550
			}
			if err := s.blockDb.Flush(); err != nil {
551
				glog.Fatalf("fatal error: flush blockDb: %v (Restart your node. We are aware of this issue)\n", err)
552 553 554 555 556 557 558 559 560 561 562 563 564
			}
		case <-s.shutdownChan:
			break done
		}
	}

	s.blockDb.Close()
	s.stateDb.Close()
	s.extraDb.Close()

	close(s.databasesClosed)
}

565
func (s *Ethereum) StartForTest() {
566
	jsonlogger.LogJson(&logger.LogStarting{
567
		ClientString:    s.net.Name,
568
		ProtocolVersion: s.EthVersion(),
569 570 571
	})
}

572 573 574 575
// AddPeer connects to the given node and maintains the connection until the
// server is shut down. If the connection fails for any reason, the server will
// attempt to reconnect the peer.
func (self *Ethereum) AddPeer(nodeURL string) error {
576
	n, err := discover.ParseNode(nodeURL)
577
	if err != nil {
578
		return fmt.Errorf("invalid node URL: %v", err)
579
	}
580
	self.net.AddPeer(n)
581 582 583 584
	return nil
}

func (s *Ethereum) Stop() {
585
	s.net.Stop()
586
	s.chainManager.Stop()
587
	s.protocolManager.Stop()
588 589
	s.txPool.Stop()
	s.eventMux.Stop()
obscuren's avatar
obscuren committed
590 591 592
	if s.whisper != nil {
		s.whisper.Stop()
	}
593
	s.StopAutoDAG()
594 595 596 597 598 599

	close(s.shutdownChan)
}

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

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
// StartAutoDAG() spawns a go routine that checks the DAG every autoDAGcheckInterval
// by default that is 10 times per epoch
// in epoch n, if we past autoDAGepochHeight within-epoch blocks,
// it calls ethash.MakeDAG  to pregenerate the DAG for the next epoch n+1
// if it does not exist yet as well as remove the DAG for epoch n-1
// the loop quits if autodagquit channel is closed, it can safely restart and
// stop any number of times.
// For any more sophisticated pattern of DAG generation, use CLI subcommand
// makedag
func (self *Ethereum) StartAutoDAG() {
	if self.autodagquit != nil {
		return // already started
	}
	go func() {
		glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG ON (ethash dir: %s)", ethash.DefaultDir)
		var nextEpoch uint64
		timer := time.After(0)
		self.autodagquit = make(chan bool)
		for {
			select {
			case <-timer:
				glog.V(logger.Info).Infof("checking DAG (ethash dir: %s)", ethash.DefaultDir)
				currentBlock := self.ChainManager().CurrentBlock().NumberU64()
				thisEpoch := currentBlock / epochLength
				if nextEpoch <= thisEpoch {
					if currentBlock%epochLength > autoDAGepochHeight {
						if thisEpoch > 0 {
							previousDag, previousDagFull := dagFiles(thisEpoch - 1)
							os.Remove(filepath.Join(ethash.DefaultDir, previousDag))
							os.Remove(filepath.Join(ethash.DefaultDir, previousDagFull))
							glog.V(logger.Info).Infof("removed DAG for epoch %d (%s)", thisEpoch-1, previousDag)
						}
						nextEpoch = thisEpoch + 1
						dag, _ := dagFiles(nextEpoch)
						if _, err := os.Stat(dag); os.IsNotExist(err) {
							glog.V(logger.Info).Infof("Pregenerating DAG for epoch %d (%s)", nextEpoch, dag)
							err := ethash.MakeDAG(nextEpoch*epochLength, "") // "" -> ethash.DefaultDir
							if err != nil {
								glog.V(logger.Error).Infof("Error generating DAG for epoch %d (%s)", nextEpoch, dag)
								return
							}
						} else {
							glog.V(logger.Error).Infof("DAG for epoch %d (%s)", nextEpoch, dag)
						}
					}
				}
				timer = time.After(autoDAGcheckInterval)
			case <-self.autodagquit:
				return
			}
		}
	}()
}

// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
func dagFiles(epoch uint64) (string, string) {
	seedHash, _ := ethash.GetSeedHash(epoch * epochLength)
	dag := fmt.Sprintf("full-R%d-%x", ethashRevision, seedHash[:8])
	return dag, "full-R" + dag
}

// stopAutoDAG stops automatic DAG pregeneration by quitting the loop
func (self *Ethereum) StopAutoDAG() {
	if self.autodagquit != nil {
		close(self.autodagquit)
		self.autodagquit = nil
	}
	glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
}

675 676 677 678 679 680 681 682
/*
	// The databases were previously tied to protocol versions. Currently we
	// are moving away from this decision as approaching Frontier. The below
	// code was left in for now but should eventually be just dropped.

	func saveProtocolVersion(db common.Database, protov int) {
		d, _ := db.Get([]byte("ProtocolVersion"))
		protocolVersion := common.NewValue(d).Uint()
683

684 685 686
		if protocolVersion == 0 {
			db.Put([]byte("ProtocolVersion"), common.NewValue(protov).Bytes())
		}
687
	}
688
*/
689 690 691 692 693 694 695 696 697

func saveBlockchainVersion(db common.Database, bcVersion int) {
	d, _ := db.Get([]byte("BlockchainVersion"))
	blockchainVersion := common.NewValue(d).Uint()

	if blockchainVersion == 0 {
		db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes())
	}
}
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712

func (self *Ethereum) Solc() (*compiler.Solidity, error) {
	var err error
	if self.solc == nil {
		self.solc, err = compiler.New(self.SolcPath)
	}
	return self.solc, err
}

// set in js console via admin interface or wrapper from cli flags
func (self *Ethereum) SetSolc(solcPath string) (*compiler.Solidity, error) {
	self.SolcPath = solcPath
	self.solc = nil
	return self.Solc()
}