backend.go 23.6 KB
Newer Older
F
Felix Lange 已提交
1
// Copyright 2014 The go-ethereum Authors
2
// This file is part of the go-ethereum library.
F
Felix Lange 已提交
3
//
4
// The go-ethereum library is free software: you can redistribute it and/or modify
F
Felix Lange 已提交
5 6 7 8
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
9
// The go-ethereum library is distributed in the hope that it will be useful,
F
Felix Lange 已提交
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
F
Felix Lange 已提交
12 13 14
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
15
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
F
Felix Lange 已提交
16

17
// Package eth implements the Ethereum protocol.
18 19 20
package eth

import (
21
	"bytes"
22
	"crypto/ecdsa"
23
	"encoding/json"
O
obscuren 已提交
24
	"fmt"
25
	"io/ioutil"
26
	"math/big"
27
	"os"
28
	"path/filepath"
29
	"strings"
30
	"time"
31

O
obscuren 已提交
32
	"github.com/ethereum/ethash"
33
	"github.com/ethereum/go-ethereum/accounts"
O
obscuren 已提交
34
	"github.com/ethereum/go-ethereum/common"
35
	"github.com/ethereum/go-ethereum/common/compiler"
36
	"github.com/ethereum/go-ethereum/core"
37
	"github.com/ethereum/go-ethereum/core/state"
F
Felix Lange 已提交
38
	"github.com/ethereum/go-ethereum/core/types"
T
Taylor Gerring 已提交
39
	"github.com/ethereum/go-ethereum/core/vm"
40
	"github.com/ethereum/go-ethereum/crypto"
41
	"github.com/ethereum/go-ethereum/eth/downloader"
O
obscuren 已提交
42
	"github.com/ethereum/go-ethereum/ethdb"
43
	"github.com/ethereum/go-ethereum/event"
44
	"github.com/ethereum/go-ethereum/logger"
O
obscuren 已提交
45
	"github.com/ethereum/go-ethereum/logger/glog"
46
	"github.com/ethereum/go-ethereum/miner"
47
	"github.com/ethereum/go-ethereum/p2p"
48
	"github.com/ethereum/go-ethereum/p2p/discover"
49
	"github.com/ethereum/go-ethereum/p2p/nat"
O
obscuren 已提交
50
	"github.com/ethereum/go-ethereum/whisper"
51 52
)

53 54 55 56 57 58 59 60
const (
	epochLength    = 30000
	ethashRevision = 23

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

F
Felix Lange 已提交
61
var (
62
	jsonlogger = logger.NewJsonLogger()
F
Felix Lange 已提交
63 64

	defaultBootNodes = []*discover.Node{
T
Taylor Gerring 已提交
65
		// ETH/DEV Go Bootnodes
T
Taylor Gerring 已提交
66 67 68
		discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE
		discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"),  // BR
		discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"),  // SG
F
Felix Lange 已提交
69
		// ETH/DEV cpp-ethereum (poc-9.ethdev.com)
T
Taylor Gerring 已提交
70
		discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"),
F
Felix Lange 已提交
71
	}
72

73 74 75 76
	defaultTestNetBootNodes = []*discover.Node{
		discover.MustParseNode("enode://5374c1bff8df923d3706357eeb4983cd29a63be40a269aaa2296ee5f3b2119a8978c0ed68b8f6fc84aad0df18790417daadf91a4bfbb786a16c9b0a199fa254a@92.51.165.126:30303"),
	}

77 78
	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
F
Felix Lange 已提交
79
)
80

O
obscuren 已提交
81
type Config struct {
82
	DevMode bool
83
	TestNet bool
84

85 86 87
	Name         string
	NetworkId    int
	GenesisNonce int
88 89
	GenesisFile  string
	GenesisBlock *types.Block // used by block tests
90
	Olympic      bool
Z
zelig 已提交
91

92 93
	BlockChainVersion  int
	SkipBcVersionCheck bool // e.g. blockchain export
94
	DatabaseCache      int
95

96 97 98 99 100 101
	DataDir   string
	LogFile   string
	Verbosity int
	LogJSON   string
	VmDebug   bool
	NatSpec   bool
102
	AutoDAG   bool
Z
zelig 已提交
103
	PowTest   bool
104
	ExtraData []byte
O
obscuren 已提交
105

106 107
	MaxPeers        int
	MaxPendingPeers int
108
	Discovery       bool
109
	Port            string
O
obscuren 已提交
110

111
	// Space-separated list of discovery node URLs
112 113
	BootNodes string

114 115 116 117
	// This key is used to identify the node on the network.
	// If nil, an ephemeral key is used.
	NodeKey *ecdsa.PrivateKey

118
	NAT  nat.Interface
O
Merge  
obscuren 已提交
119 120 121
	Shh  bool
	Dial bool

122
	Etherbase      common.Address
123
	GasPrice       *big.Int
124
	MinerThreads   int
125
	AccountManager *accounts.Manager
126
	SolcPath       string
127

Z
zsfelfoldi 已提交
128 129 130 131 132 133 134
	GpoMinGasPrice          *big.Int
	GpoMaxGasPrice          *big.Int
	GpoFullBlockRatio       int
	GpobaseStepDown         int
	GpobaseStepUp           int
	GpobaseCorrectionFactor int

135 136
	// NewDB is used to create databases.
	// If nil, the default is to create leveldb databases on disk.
137
	NewDB func(path string) (ethdb.Database, error)
O
obscuren 已提交
138 139
}

140
func (cfg *Config) parseBootNodes() []*discover.Node {
F
Felix Lange 已提交
141
	if cfg.BootNodes == "" {
142 143 144 145
		if cfg.TestNet {
			return defaultTestNetBootNodes
		}

F
Felix Lange 已提交
146 147
		return defaultBootNodes
	}
148 149
	var ns []*discover.Node
	for _, url := range strings.Split(cfg.BootNodes, " ") {
150 151 152
		if url == "" {
			continue
		}
153 154
		n, err := discover.ParseNode(url)
		if err != nil {
O
obscuren 已提交
155
			glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
156 157 158 159 160 161
			continue
		}
		ns = append(ns, n)
	}
	return ns
}
162

163 164 165 166
// 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)
167
	if _, err := os.Stat(path); err != nil {
168 169
		return nil
	}
170
	// Load the nodes from the config file
171 172
	blob, err := ioutil.ReadFile(path)
	if err != nil {
173
		glog.V(logger.Error).Infof("Failed to access nodes: %v", err)
174 175 176 177
		return nil
	}
	nodelist := []string{}
	if err := json.Unmarshal(blob, &nodelist); err != nil {
178
		glog.V(logger.Error).Infof("Failed to load nodes: %v", err)
179
		return nil
180 181 182
	}
	// Interpret the list as a discovery node array
	var nodes []*discover.Node
183
	for _, url := range nodelist {
184 185 186 187 188
		if url == "" {
			continue
		}
		node, err := discover.ParseNode(url)
		if err != nil {
189
			glog.V(logger.Error).Infof("Node URL %s: %v\n", url, err)
190 191 192 193 194 195 196
			continue
		}
		nodes = append(nodes, node)
	}
	return nodes
}

197 198 199 200 201 202
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
203
	keyfile := filepath.Join(cfg.DataDir, "nodekey")
204 205 206 207 208 209 210 211
	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)
	}
F
Felix Lange 已提交
212
	if err := crypto.SaveECDSA(keyfile, key); err != nil {
O
obscuren 已提交
213
		glog.V(logger.Error).Infoln("could not persist nodekey: ", err)
214 215 216 217
	}
	return key, nil
}

218 219 220 221
type Ethereum struct {
	// Channel for shutting down the ethereum
	shutdownChan chan bool

222
	// DB interfaces
223 224
	chainDb ethdb.Database // Block chain database
	dappDb  ethdb.Database // Dapp database
225

O
obscuren 已提交
226
	//*** SERVICES ***
227
	// State manager for processing new blocks and managing the over all states
228 229
	blockProcessor  *core.BlockProcessor
	txPool          *core.TxPool
230
	blockchain      *core.BlockChain
231 232 233 234
	accountManager  *accounts.Manager
	whisper         *whisper.Whisper
	pow             *ethash.Ethash
	protocolManager *ProtocolManager
235 236
	SolcPath        string
	solc            *compiler.Solidity
237

Z
zsfelfoldi 已提交
238 239 240 241 242 243 244
	GpoMinGasPrice          *big.Int
	GpoMaxGasPrice          *big.Int
	GpoFullBlockRatio       int
	GpobaseStepDown         int
	GpobaseStepUp           int
	GpobaseCorrectionFactor int

245 246 247
	net      *p2p.Server
	eventMux *event.TypeMux
	miner    *miner.Miner
248

249
	// logger logger.LogSystem
250

251
	Mining        bool
252
	MinerThreads  int
253
	NatSpec       bool
254
	DataDir       string
255
	AutoDAG       bool
Z
zelig 已提交
256
	PowTest       bool
257
	autodagquit   chan bool
Z
zelig 已提交
258
	etherbase     common.Address
259 260 261
	clientVersion string
	netVersionId  int
	shhVersionId  int
262 263
}

O
obscuren 已提交
264
func New(config *Config) (*Ethereum, error) {
265
	// Bootstrap database
266
	logger.New(config.DataDir, config.LogFile, config.Verbosity)
267 268 269
	if len(config.LogJSON) > 0 {
		logger.NewJSONsystem(config.DataDir, config.LogJSON)
	}
F
Felix Lange 已提交
270

271
	// 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)
272
	const dbCount = 3
273
	ethdb.OpenFileLimit = 128 / (dbCount + 1)
274

275 276
	newdb := config.NewDB
	if newdb == nil {
277
		newdb = func(path string) (ethdb.Database, error) { return ethdb.NewLDBDatabase(path, config.DatabaseCache) }
278
	}
279

280
	// Open the chain database and perform any upgrades needed
281
	chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata"))
O
obscuren 已提交
282
	if err != nil {
283
		return nil, fmt.Errorf("blockchain db err: %v", err)
O
obscuren 已提交
284
	}
285 286
	if db, ok := chainDb.(*ethdb.LDBDatabase); ok {
		db.Meter("eth/db/chaindata/")
287
	}
288 289 290 291
	if err := upgradeChainDatabase(chainDb); err != nil {
		return nil, err
	}

292
	dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
293
	if err != nil {
294
		return nil, fmt.Errorf("dapp db err: %v", err)
295
	}
296 297
	if db, ok := dappDb.(*ethdb.LDBDatabase); ok {
		db.Meter("eth/db/dapp/")
298
	}
299

300
	nodeDb := filepath.Join(config.DataDir, "nodes")
301
	glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId)
O
obscuren 已提交
302

303 304 305 306
	if len(config.GenesisFile) > 0 {
		fr, err := os.Open(config.GenesisFile)
		if err != nil {
			return nil, err
307
		}
308

309
		block, err := core.WriteGenesisBlock(chainDb, fr)
310 311 312 313 314 315
		if err != nil {
			return nil, err
		}
		glog.V(logger.Info).Infof("Successfully wrote genesis block. New genesis hash = %x\n", block.Hash())
	}

316 317 318 319 320 321
	// different modes
	switch {
	case config.Olympic:
		glog.V(logger.Error).Infoln("Starting Olympic network")
		fallthrough
	case config.DevMode:
322 323 324 325 326 327 328
		_, err := core.WriteOlympicGenesisBlock(chainDb, 42)
		if err != nil {
			return nil, err
		}
	case config.TestNet:
		state.StartingNonce = 1048576 // (2**20)
		_, err := core.WriteTestNetGenesisBlock(chainDb, 0x6d6f7264656e)
329 330 331
		if err != nil {
			return nil, err
		}
332 333 334 335
	}
	// This is for testing only.
	if config.GenesisBlock != nil {
		core.WriteTd(chainDb, config.GenesisBlock.Hash(), config.GenesisBlock.Difficulty())
336
		core.WriteBlock(chainDb, config.GenesisBlock)
337 338
		core.WriteCanonicalHash(chainDb, config.GenesisBlock.Hash(), config.GenesisBlock.NumberU64())
		core.WriteHeadBlockHash(chainDb, config.GenesisBlock.Hash())
339
	}
340

341
	if !config.SkipBcVersionCheck {
342
		b, _ := chainDb.Get([]byte("BlockchainVersion"))
343 344 345 346
		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)
		}
347
		saveBlockchainVersion(chainDb, config.BlockChainVersion)
348 349 350
	}
	glog.V(logger.Info).Infof("Blockchain DB Version: %d", config.BlockChainVersion)

351
	eth := &Ethereum{
Z
zsfelfoldi 已提交
352
		shutdownChan:            make(chan bool),
353 354
		chainDb:                 chainDb,
		dappDb:                  dappDb,
Z
zsfelfoldi 已提交
355 356 357
		eventMux:                &event.TypeMux{},
		accountManager:          config.AccountManager,
		DataDir:                 config.DataDir,
358
		etherbase:               config.Etherbase,
Z
zsfelfoldi 已提交
359 360 361 362 363 364
		clientVersion:           config.Name, // TODO should separate from Name
		netVersionId:            config.NetworkId,
		NatSpec:                 config.NatSpec,
		MinerThreads:            config.MinerThreads,
		SolcPath:                config.SolcPath,
		AutoDAG:                 config.AutoDAG,
Z
zelig 已提交
365
		PowTest:                 config.PowTest,
Z
zsfelfoldi 已提交
366 367 368 369 370 371
		GpoMinGasPrice:          config.GpoMinGasPrice,
		GpoMaxGasPrice:          config.GpoMaxGasPrice,
		GpoFullBlockRatio:       config.GpoFullBlockRatio,
		GpobaseStepDown:         config.GpobaseStepDown,
		GpobaseStepUp:           config.GpobaseStepUp,
		GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
372 373
	}

Z
zelig 已提交
374 375 376 377 378 379 380 381 382
	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()
	}
383
	//genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
384
	eth.blockchain, err = core.NewBlockChain(chainDb, eth.pow, eth.EventMux())
O
obscuren 已提交
385
	if err != nil {
386 387 388 389
		if err == core.ErrNoGenesis {
			return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`)
		}

O
obscuren 已提交
390 391
		return nil, err
	}
392 393
	newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
	eth.txPool = newPool
394

395 396 397
	eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux())
	eth.blockchain.SetProcessor(eth.blockProcessor)
	eth.protocolManager = NewProtocolManager(config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb)
398

399
	eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
400
	eth.miner.SetGasPrice(config.GasPrice)
401
	eth.miner.SetExtra(config.ExtraData)
402

403 404 405 406
	if config.Shh {
		eth.whisper = whisper.New()
		eth.shhVersionId = int(eth.whisper.Version())
	}
407

408 409 410 411
	netprv, err := config.nodeKey()
	if err != nil {
		return nil, err
	}
412
	protocols := append([]p2p.Protocol{}, eth.protocolManager.SubProtocols...)
413
	if config.Shh {
F
Felix Lange 已提交
414
		protocols = append(protocols, eth.whisper.Protocol())
415
	}
O
obscuren 已提交
416
	eth.net = &p2p.Server{
417 418 419 420
		PrivateKey:      netprv,
		Name:            config.Name,
		MaxPeers:        config.MaxPeers,
		MaxPendingPeers: config.MaxPendingPeers,
421
		Discovery:       config.Discovery,
422 423 424 425 426 427 428
		Protocols:       protocols,
		NAT:             config.NAT,
		NoDial:          !config.Dial,
		BootstrapNodes:  config.parseBootNodes(),
		StaticNodes:     config.parseNodes(staticNodes),
		TrustedNodes:    config.parseNodes(trustedNodes),
		NodeDatabase:    nodeDb,
O
Merge  
obscuren 已提交
429 430 431
	}
	if len(config.Port) > 0 {
		eth.net.ListenAddr = ":" + config.Port
432 433
	}

434 435
	vm.Debug = config.VmDebug

436 437 438
	return eth, nil
}

Z
eth:  
zelig 已提交
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
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(),
F
Felix Lange 已提交
458 459
		DiscPort:   int(node.UDP),
		TCPPort:    int(node.TCP),
Z
eth:  
zelig 已提交
460
		ListenAddr: s.net.ListenAddr,
461
		Td:         s.BlockChain().Td().String(),
Z
eth:  
zelig 已提交
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
	}
}

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
}

F
Felix Lange 已提交
497
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
498
	s.blockchain.ResetWithGenesisBlock(gb)
F
Felix Lange 已提交
499 500
}

501
func (s *Ethereum) StartMining(threads int) error {
Z
zelig 已提交
502
	eb, err := s.Etherbase()
503
	if err != nil {
Z
zelig 已提交
504
		err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)
O
obscuren 已提交
505
		glog.V(logger.Error).Infoln(err)
Z
zelig 已提交
506
		return err
507
	}
Z
zelig 已提交
508

509
	go s.miner.Start(eb, threads)
510 511 512
	return nil
}

Z
zelig 已提交
513 514 515
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
	eb = s.etherbase
	if (eb == common.Address{}) {
516 517 518 519 520
		addr, e := s.AccountManager().AddressByIndex(0)
		if e != nil {
			err = fmt.Errorf("etherbase address must be explicitly specified")
		}
		eb = common.HexToAddress(addr)
Z
zelig 已提交
521
	}
522
	return
Z
zelig 已提交
523 524
}

525 526 527
// set in js console via admin interface or wrapper from cli flags
func (self *Ethereum) SetEtherbase(etherbase common.Address) {
	self.etherbase = etherbase
J
Jeffrey Wilcke 已提交
528
	self.miner.SetEtherbase(etherbase)
529 530
}

531 532 533
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 }
534

535
// func (s *Ethereum) Logger() logger.LogSystem             { return s.logger }
536 537
func (s *Ethereum) Name() string                         { return s.net.Name }
func (s *Ethereum) AccountManager() *accounts.Manager    { return s.accountManager }
538
func (s *Ethereum) BlockChain() *core.BlockChain         { return s.blockchain }
539 540 541 542
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 }
543 544
func (s *Ethereum) ChainDb() ethdb.Database              { return s.chainDb }
func (s *Ethereum) DappDb() ethdb.Database               { return s.dappDb }
545 546 547 548
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 }
549
func (s *Ethereum) ClientVersion() string                { return s.clientVersion }
550
func (s *Ethereum) EthVersion() int                      { return int(s.protocolManager.SubProtocols[0].Version) }
551 552
func (s *Ethereum) NetVersion() int                      { return s.netVersionId }
func (s *Ethereum) ShhVersion() int                      { return s.shhVersionId }
553
func (s *Ethereum) Downloader() *downloader.Downloader   { return s.protocolManager.downloader }
O
obscuren 已提交
554

555
// Start the ethereum
556
func (s *Ethereum) Start() error {
557
	jsonlogger.LogJson(&logger.LogStarting{
O
merge  
obscuren 已提交
558
		ClientString:    s.net.Name,
559
		ProtocolVersion: s.EthVersion(),
T
Taylor Gerring 已提交
560
	})
561 562 563
	err := s.net.Start()
	if err != nil {
		return err
564
	}
565

566 567 568 569
	if s.AutoDAG {
		s.StartAutoDAG()
	}

570
	s.protocolManager.Start()
O
Merge  
obscuren 已提交
571 572 573 574

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

O
obscuren 已提交
576
	glog.V(logger.Info).Infoln("Server started")
577 578 579
	return nil
}

580
func (s *Ethereum) StartForTest() {
581
	jsonlogger.LogJson(&logger.LogStarting{
582
		ClientString:    s.net.Name,
583
		ProtocolVersion: s.EthVersion(),
584 585 586
	})
}

587 588 589 590
// 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 {
591
	n, err := discover.ParseNode(nodeURL)
592
	if err != nil {
593
		return fmt.Errorf("invalid node URL: %v", err)
594
	}
595
	self.net.AddPeer(n)
596 597 598 599
	return nil
}

func (s *Ethereum) Stop() {
F
Felix Lange 已提交
600
	s.net.Stop()
601
	s.blockchain.Stop()
602
	s.protocolManager.Stop()
603 604
	s.txPool.Stop()
	s.eventMux.Stop()
O
Merge  
obscuren 已提交
605 606 607
	if s.whisper != nil {
		s.whisper.Stop()
	}
608
	s.StopAutoDAG()
609

F
Felix Lange 已提交
610 611
	s.chainDb.Close()
	s.dappDb.Close()
612 613 614 615 616 617 618 619
	close(s.shutdownChan)
}

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

620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
// 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)
642
				currentBlock := self.BlockChain().CurrentBlock().NumberU64()
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 675 676 677 678 679 680 681 682
				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
			}
		}
	}()
}

// 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)
}

683 684 685 686 687 688 689
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
}
690

691 692 693 694 695 696
// 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()
}
697

698 699 700 701 702 703 704
// 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
}
705

706
func saveBlockchainVersion(db ethdb.Database, bcVersion int) {
707 708 709 710 711 712 713
	d, _ := db.Get([]byte("BlockchainVersion"))
	blockchainVersion := common.NewValue(d).Uint()

	if blockchainVersion == 0 {
		db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes())
	}
}
714

715 716
// upgradeChainDatabase ensures that the chain database stores block split into
// separate header and body entries.
717
func upgradeChainDatabase(db ethdb.Database) error {
718 719 720
	// Short circuit if the head block is stored already as separate header and body
	data, err := db.Get([]byte("LastBlock"))
	if err != nil {
721 722
		return nil
	}
723
	head := common.BytesToHash(data)
724

725 726
	if block := core.GetBlockByHashOld(db, head); block == nil {
		return nil
727
	}
728 729
	// At least some of the database is still the old format, upgrade (skip the head block!)
	glog.V(logger.Info).Info("Old database detected, upgrading...")
730

731 732 733 734 735 736 737 738 739 740 741 742 743
	if db, ok := db.(*ethdb.LDBDatabase); ok {
		blockPrefix := []byte("block-hash-")
		for it := db.NewIterator(); it.Next(); {
			// Skip anything other than a combined block
			if !bytes.HasPrefix(it.Key(), blockPrefix) {
				continue
			}
			// Skip the head block (merge last to signal upgrade completion)
			if bytes.HasSuffix(it.Key(), head.Bytes()) {
				continue
			}
			// Load the block, split and serialize (order!)
			block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix)))
744

745 746 747 748
			if err := core.WriteTd(db, block.Hash(), block.DeprecatedTd()); err != nil {
				return err
			}
			if err := core.WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
749 750 751 752 753 754 755 756
				return err
			}
			if err := core.WriteHeader(db, block.Header()); err != nil {
				return err
			}
			if err := db.Delete(it.Key()); err != nil {
				return err
			}
757
		}
758 759
		// Lastly, upgrade the head block, disabling the upgrade mechanism
		current := core.GetBlockByHashOld(db, head)
760

761 762 763 764
		if err := core.WriteTd(db, current.Hash(), current.DeprecatedTd()); err != nil {
			return err
		}
		if err := core.WriteBody(db, current.Hash(), &types.Body{current.Transactions(), current.Uncles()}); err != nil {
765
			return err
766
		}
767 768
		if err := core.WriteHeader(db, current.Header()); err != nil {
			return err
769 770 771
		}
	}
	return nil
772
}