Skip to content
Snippets Groups Projects
serve.go 6.37 KiB
Newer Older
Androlo's avatar
Androlo committed
// The erisdb package contains tendermint-specific services that goes with the
// server.
Androlo's avatar
Androlo committed
package erisdb

import (
Ethan Buchman's avatar
Ethan Buchman committed
	"bytes"
	"fmt"
	"io/ioutil"
Ethan Buchman's avatar
Ethan Buchman committed
	"path"
	"strings"
	"sync"
Ethan Buchman's avatar
Ethan Buchman committed

Ethan Buchman's avatar
Ethan Buchman committed
	. "github.com/tendermint/go-common"
	cfg "github.com/tendermint/go-config"
	dbm "github.com/tendermint/go-db"
	"github.com/tendermint/go-events"
	"github.com/tendermint/go-p2p"
	"github.com/tendermint/go-wire"
	"github.com/tendermint/log15"
Ethan Buchman's avatar
Ethan Buchman committed
	"github.com/tendermint/tendermint/node"
	"github.com/tendermint/tendermint/proxy"
	"github.com/tendermint/tendermint/types"
	tmspcli "github.com/tendermint/tmsp/client"
	tmsp "github.com/tendermint/tmsp/server"
Ethan Buchman's avatar
Ethan Buchman committed

	tmcfg "github.com/eris-ltd/eris-db/config/tendermint"
Androlo's avatar
Androlo committed
	ep "github.com/eris-ltd/eris-db/erisdb/pipe"
	"github.com/eris-ltd/eris-db/server"
	sm "github.com/eris-ltd/eris-db/state"
	stypes "github.com/eris-ltd/eris-db/state/types"
Ethan Buchman's avatar
Ethan Buchman committed
	edbapp "github.com/eris-ltd/eris-db/tmsp"
Androlo's avatar
Androlo committed
)

Androlo's avatar
Androlo committed
const ERISDB_VERSION = "0.11.5"
androlo's avatar
androlo committed
const TENDERMINT_VERSION = "0.5.0"
Androlo's avatar
Androlo committed

Androlo's avatar
Androlo committed
var log = log15.New("module", "eris/erisdb_server")
var tmConfig cfg.Config

Ethan Buchman's avatar
Ethan Buchman committed
// This function returns a properly configured ErisDb server process,
// with a tmsp listener for talking to tendermint core.
Ethan Buchman's avatar
Ethan Buchman committed
// To start listening for incoming HTTP requests on the Rest server, call 'Start()' on the process.
Ethan Buchman's avatar
Ethan Buchman committed
// Make sure to register any start event listeners first
func ServeErisDB(workDir string, inProc bool) (*server.ServeProcess, error) {
Androlo's avatar
Androlo committed
	log.Info("ErisDB Serve initializing.")
	errEns := EnsureDir(workDir, 0777)
Androlo's avatar
Androlo committed

	if errEns != nil {
		return nil, errEns
	}
Androlo's avatar
Androlo committed

Ethan Buchman's avatar
Ethan Buchman committed
	// there are two types of config we need to load,
	// one for the erisdb server and one for tendermint.
	// even if consensus isn't in process, the tendermint libs (eg. db)
	// expect tendermint/go-config to be setup.
	// Regardless, both configs are expected in the same file (root/config.toml)
	// Some of this stuff is implicit and maybe a little confusing,
	// but cfg mgmt across projects probably often is!
Androlo's avatar
Androlo committed

Ethan Buchman's avatar
Ethan Buchman committed
	// Get an erisdb configuration
	var sConf *server.ServerConfig
	sConfPath := path.Join(workDir, "config.toml")
Androlo's avatar
Androlo committed
	if !FileExists(sConfPath) {
Androlo's avatar
Androlo committed
		log.Info("No server configuration, using default.")
		log.Info("Writing to: " + sConfPath)
Androlo's avatar
Androlo committed
		sConf = server.DefaultServerConfig()
Androlo's avatar
Androlo committed
		errW := server.WriteServerConfig(sConfPath, sConf)
		if errW != nil {
			panic(errW)
		}
Androlo's avatar
Androlo committed
	} else {
		var errRSC error
		sConf, errRSC = server.ReadServerConfig(sConfPath)
		if errRSC != nil {
			log.Error("Server config file error.", "error", errRSC.Error())
		}
	}
Androlo's avatar
Androlo committed

Androlo's avatar
Androlo committed
	// Get tendermint configuration
	tmConfig = tmcfg.GetConfig(workDir)
Ethan Buchman's avatar
Ethan Buchman committed
	// tmConfig.Set("tm.version", TENDERMINT_VERSION) // ?
	cfg.ApplyConfig(tmConfig) // Notify in proc tendermint of new config
Androlo's avatar
Androlo committed

Ethan Buchman's avatar
Ethan Buchman committed
	// Load the application state
	// The app state used to be managed by tendermint node,
	// but is now managed by ErisDB.
	// The tendermint core only stores the blockchain (history of txs)
Ethan Buchman's avatar
Ethan Buchman committed
	stateDB := dbm.GetDB("app_state", config.GetString("erisdb.db_backend"), config.GetString("erisdb.db_dir"))
Ethan Buchman's avatar
Ethan Buchman committed
	state := sm.LoadState(stateDB)
	var genDoc *stypes.GenesisDoc
	if state == nil {
Ethan Buchman's avatar
Ethan Buchman committed
		genDoc, state = sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file"))
Ethan Buchman's avatar
Ethan Buchman committed
		state.Save()
		buf, n, err := new(bytes.Buffer), new(int), new(error)
Ethan Buchman's avatar
Ethan Buchman committed
		wire.WriteJSON(genDoc, buf, n, err)
		stateDB.Set(stypes.GenDocKey, buf.Bytes())
		if *err != nil {
			Exit(Fmt("Unable to write gendoc to db: %v", err))
		}
	} else {
		genDocBytes := stateDB.Get(stypes.GenDocKey)
		err := new(error)
		wire.ReadJSONPtr(&genDoc, genDocBytes, err)
		if *err != nil {
			Exit(Fmt("Unable to read gendoc from db: %v", err))
		}
	}
	// add the chainid to the global config
Ethan Buchman's avatar
Ethan Buchman committed
	// for now it's same for both of them
	config.Set("tm.chain_id", state.ChainID)
	config.Set("erisdb.chain_id", state.ChainID)
Ethan Buchman's avatar
Ethan Buchman committed

Ethan Buchman's avatar
Ethan Buchman committed
	// *****************************
	// erisdb-tmsp app

	// start the event switch for state related events
	// (transactions to/from acconts, etc)
Ethan Buchman's avatar
Ethan Buchman committed
	evsw := events.NewEventSwitch()
	evsw.Start()
Ethan Buchman's avatar
Ethan Buchman committed
	// create the app
Ethan Buchman's avatar
Ethan Buchman committed
	app := edbapp.NewErisDBApp(state, evsw)

Ethan Buchman's avatar
Ethan Buchman committed
	// so we know where to find the consensus host (for eg. blockchain/consensus rpcs)
	app.SetHostAddress(sConf.Consensus.TendermintHost)
	if inProc {
		fmt.Println("Starting tm node in proc")
		startTMNode(app)
	} else {
		fmt.Println("Starting tmsp listener")
		// Start the tmsp listener for state update commands
		go func() {
			// TODO config
			_, err := tmsp.NewServer(sConf.Consensus.TMSPListener, app)
			if err != nil {
				// TODO: play nice
				Exit(err.Error())
			}
		}()
	}
Ethan Buchman's avatar
Ethan Buchman committed

Ethan Buchman's avatar
Ethan Buchman committed
	// *****************************
	// Boot the erisdb restful API servers

Ethan Buchman's avatar
Ethan Buchman committed
	// Load supporting objects.
	pipe := ep.NewPipe(app, evsw)
Androlo's avatar
Androlo committed
	codec := &TCodec{}
Androlo's avatar
Androlo committed
	evtSubs := NewEventSubscriptions(pipe.Events())
Androlo's avatar
Androlo committed
	// The services.
	tmwss := NewErisDbWsService(codec, pipe)
	tmjs := NewErisDbJsonService(codec, pipe, evtSubs)
	// The servers.
	jsonServer := NewJsonRpcServer(tmjs)
	restServer := NewRestServer(codec, pipe, evtSubs)
Androlo's avatar
Androlo committed
	wsServer := server.NewWebSocketServer(sConf.WebSocket.MaxWebSocketSessions, tmwss)
Androlo's avatar
Androlo committed
	// Create a server process.
	proc := server.NewServeProcess(sConf, jsonServer, restServer, wsServer)
Androlo's avatar
Androlo committed

Androlo's avatar
Androlo committed
	return proc, nil
}

Ethan Buchman's avatar
Ethan Buchman committed
// start an inproc tendermint node
func startTMNode(app *edbapp.ErisDBApp) {
	// get the genesis
Ethan Buchman's avatar
Ethan Buchman committed
	genDocFile := config.GetString("tm.genesis_file")
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
	}
	genDoc := types.GenesisDocFromJSON(jsonBlob)
	if genDoc.ChainID == "" {
		PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile))
	}
Ethan Buchman's avatar
Ethan Buchman committed
	config.Set("tm.chain_id", genDoc.ChainID)
	config.Set("tm.genesis_doc", genDoc)

	// Get PrivValidator
	privValidatorFile := config.GetString("priv_validator_file")
	privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
	nd := node.NewNode(privValidator, func(addr string, hash []byte) proxy.AppConn {
		// TODO: Check the hash
		return tmspcli.NewLocalClient(new(sync.Mutex), app)
	})

	l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp"))
	nd.AddListener(l)
	if err := nd.Start(); err != nil {
		Exit(Fmt("Failed to start node: %v", err))
	}

	log.Notice("Started node", "nodeInfo", nd.NodeInfo())

	// If seedNode is provided by config, dial out.
	if config.GetString("seeds") != "" {
		seeds := strings.Split(config.GetString("seeds"), ",")
		nd.DialSeeds(seeds)
	}

	// Run the RPC server.
	if config.GetString("rpc_laddr") != "" {
		_, err := nd.StartRPC()
		if err != nil {
			PanicCrisis(err)
		}
	}
}