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(&ethlogger.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  }