diff --git a/cmd/serve.go b/cmd/serve.go index 9e379a14e923228ee49bd2d481c5f50dcdcfe981..0f224aa637b1358aa0af4abcf5b6f847bb234eda 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -98,8 +98,19 @@ func Serve(cmd *cobra.Command, args []string) { } log.Debug(fmt.Sprintf("Data directory is set at %s", do.DataDir)) - config.LoadConsensusModuleConfig(do) + consensusConfig, err := config.LoadConsensusModuleConfig(do) + if err != nil { + log.Fatalf("Failed to load consensus module configuration: %s.", err) + os.Exit(1) + } + + managerConfig, err := config.LoadApplicationManagerModuleConfig(do) + if err != nil { + log.Fatalf("Failed to load application manager module configuration: %s.", err) + os.Exit(1) + } + fmt.Printf("Consensus %s, App %s", consensusConfig.Version, managerConfig.Version) } //------------------------------------------------------------------------------ diff --git a/config/config.go b/config/config.go index 9d92b4ccac306fdc831029649dee72bc0f8bee17..099a591971f12c08fe4ace5f7c6c915d7c8e1b7a 100644 --- a/config/config.go +++ b/config/config.go @@ -28,6 +28,7 @@ import ( consensus "github.com/eris-ltd/eris-db/consensus" definitions "github.com/eris-ltd/eris-db/definitions" + manager "github.com/eris-ltd/eris-db/manager" util "github.com/eris-ltd/eris-db/util" version "github.com/eris-ltd/eris-db/version" ) @@ -46,6 +47,12 @@ func LoadConsensusModuleConfig(do *definitions.Do) (ModuleConfig, error) { return loadModuleConfig(do, "consensus") } +// LoadApplicationManagerModuleConfig wraps specifically for the application +// manager +func LoadApplicationManagerModuleConfig(do *definitions.Do) (ModuleConfig, error) { + return loadModuleConfig(do, "manager") +} + // Generic Module loader for configuration information func loadModuleConfig(do *definitions.Do, module string) (ModuleConfig, error) { moduleName := do.Config.GetString("chain." + module + ".name") @@ -70,7 +77,14 @@ func loadModuleConfig(do *definitions.Do, module string) (ModuleConfig, error) { fmt.Errorf("Failed to create module data directory %s.", dataDir) } // load configuration subtree for module - config := do.Config.Sub(moduleName) + // TODO: [ben] Viper internally panics if `moduleName` contains an unallowed + // character (eg, a dash). Either this needs to be wrapped in a go-routine + // and recovered from or a PR to viper is needed to address this bug. + subConfig := do.Config.Sub(moduleName) + if subConfig == nil { + return ModuleConfig{}, + fmt.Errorf("Failed to read configuration section for %s.", moduleName) + } return ModuleConfig { Module : module, @@ -78,7 +92,7 @@ func loadModuleConfig(do *definitions.Do, module string) (ModuleConfig, error) { Version : minorVersionString, WorkDir : workDir, DataDir : dataDir, - Config : config, + Config : subConfig, }, nil } @@ -89,6 +103,8 @@ func assertValidModule(module, name, minorVersionString string) bool { switch module { case "consensus" : return consensus.AssertValidConsensusModule(name, minorVersionString) + case "manager" : + return manager.AssertValidApplicationManagerModule(name, minorVersionString) } return false } diff --git a/consensus/config.go b/consensus/config.go index 674c20dca082a1bffcf6a3ed9cf7c01fec955adf..06b5a6afd45316df0dd9bce7802aa84a2abf6748 100644 --- a/consensus/config.go +++ b/consensus/config.go @@ -38,6 +38,9 @@ func AssertValidConsensusModule(name, minorVersionString string) bool { return minorVersionString == tmsp.GetTmspVersion().GetMinorVersionString() case "tendermint" : return minorVersionString == tendermint.GetTendermintVersion().GetMinorVersionString() + case "bigchaindb" : + // TODO: [ben] implement BigchainDB as consensus engine + return false } return false } diff --git a/manager/config.go b/manager/config.go new file mode 100644 index 0000000000000000000000000000000000000000..9c1632815a42744972ade390d4e85922bb215027 --- /dev/null +++ b/manager/config.go @@ -0,0 +1,38 @@ +// Copyright 2015, 2016 Eris Industries (UK) Ltd. +// This file is part of Eris-RT + +// Eris-RT is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Eris-RT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Eris-RT. If not, see <http://www.gnu.org/licenses/>. + +// version provides the current Eris-DB version and a VersionIdentifier +// for the modules to identify their version with. + +package manager + +import ( + erismint "github.com/eris-ltd/eris-db/manager/eris-mint" +) + +//------------------------------------------------------------------------------ +// Helper functions + +func AssertValidApplicationManagerModule(name, minorVersionString string) bool { + switch name { + case "erismint" : + return minorVersionString == erismint.GetErisMintVersion().GetMinorVersionString() + case "geth" : + // TODO: [ben] implement Geth 1.4 as an application manager + return false + } + return false +} diff --git a/manager/eris-mint/eris-mint.go b/manager/eris-mint/eris-mint.go index 9f25c3dd7ca8486f801403b47c5078ff2c606847..8eb514b44bc863b09cc8e5d6f94ecc2a1d9061d9 100644 --- a/manager/eris-mint/eris-mint.go +++ b/manager/eris-mint/eris-mint.go @@ -14,23 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Eris-RT. If not, see <http://www.gnu.org/licenses/>. -package eris-mint +package erismint import ( - "bytes" - "encoding/hex" - "fmt" - "sync" + "bytes" + "encoding/hex" + "fmt" + "sync" - sm "github.com/eris-ltd/eris-db/manager/eris-mint/state" - types "github.com/eris-ltd/eris-db/txs" + events "github.com/tendermint/go-events" + client "github.com/tendermint/go-rpc/client" + wire "github.com/tendermint/go-wire" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + tmsp "github.com/tendermint/tmsp/types" - "github.com/tendermint/go-events" - client "github.com/tendermint/go-rpc/client" - "github.com/tendermint/go-wire" - ctypes "github.com/tendermint/tendermint/rpc/core/types" + log "github.com/eris-ltd/eris-logger" - tmsp "github.com/tendermint/tmsp/types" + sm "github.com/eris-ltd/eris-db/manager/eris-mint/state" + types "github.com/eris-ltd/eris-db/txs" ) //-------------------------------------------------------------------------------- @@ -39,159 +40,163 @@ import ( // one for mempool, one for consensus. type ErisMint struct { - mtx sync.Mutex + mtx sync.Mutex - state *sm.State - cache *sm.BlockCache - checkCache *sm.BlockCache // for CheckTx (eg. so we get nonces right) + state *sm.State + cache *sm.BlockCache + checkCache *sm.BlockCache // for CheckTx (eg. so we get nonces right) - evc *events.EventCache - evsw *events.EventSwitch + evc *events.EventCache + evsw *events.EventSwitch - // client to the tendermint core rpc - client *client.ClientURI - host string // tendermint core endpoint + // client to the tendermint core rpc + client *client.ClientURI + host string // tendermint core endpoint - nTxs int // count txs in a block + nTxs int // count txs in a block } func (app *ErisMint) GetState() *sm.State { - app.mtx.Lock() - defer app.mtx.Unlock() - return app.state.Copy() + app.mtx.Lock() + defer app.mtx.Unlock() + return app.state.Copy() } // TODO: this is used for call/callcode and to get nonces during mempool. // the former should work on last committed state only and the later should // be handled by the client, or a separate wallet-like nonce tracker thats not part of the app func (app *ErisMint) GetCheckCache() *sm.BlockCache { - return app.checkCache + return app.checkCache } func (app *ErisMint) SetHostAddress(host string) { - app.host = host - app.client = client.NewClientURI(host) //fmt.Sprintf("http://%s", host)) + app.host = host + app.client = client.NewClientURI(host) //fmt.Sprintf("http://%s", host)) } // Broadcast a tx to the tendermint core // NOTE: this assumes we know the address of core func (app *ErisMint) BroadcastTx(tx types.Tx) error { - buf := new(bytes.Buffer) - var n int - var err error - wire.WriteBinary(struct{ types.Tx }{tx}, buf, &n, &err) - if err != nil { - return err - } - - params := map[string]interface{}{ - "tx": hex.EncodeToString(buf.Bytes()), - } - - var result ctypes.TMResult - _, err = app.client.Call("broadcast_tx_sync", params, &result) - return err + buf := new(bytes.Buffer) + var n int + var err error + wire.WriteBinary(struct{ types.Tx }{tx}, buf, &n, &err) + if err != nil { + return err + } + + params := map[string]interface{}{ + "tx": hex.EncodeToString(buf.Bytes()), + } + + var result ctypes.TMResult + _, err = app.client.Call("broadcast_tx_sync", params, &result) + return err } func NewErisMint(s *sm.State, evsw *events.EventSwitch) *ErisMint { - return &ErisMint{ - state: s, - cache: sm.NewBlockCache(s), - checkCache: sm.NewBlockCache(s), - evc: events.NewEventCache(evsw), - evsw: evsw, - } + return &ErisMint{ + state: s, + cache: sm.NewBlockCache(s), + checkCache: sm.NewBlockCache(s), + evc: events.NewEventCache(evsw), + evsw: evsw, + } } // Implements tmsp.Application func (app *ErisMint) Info() (info string) { - return "ErisDB" + return "ErisDB" } // Implements tmsp.Application func (app *ErisMint) SetOption(key string, value string) (log string) { - return "" + return "" } // Implements tmsp.Application func (app *ErisMint) AppendTx(txBytes []byte) (res tmsp.Result) { - app.nTxs += 1 - - // XXX: if we had tx ids we could cache the decoded txs on CheckTx - var n int - var err error - tx := new(types.Tx) - buf := bytes.NewBuffer(txBytes) - wire.ReadBinaryPtr(tx, buf, len(txBytes), &n, &err) - if err != nil { - return tmsp.NewError(tmsp.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) - } - - log.Info("AppendTx", "tx", *tx) - - err = sm.ExecTx(app.cache, *tx, true, app.evc) - if err != nil { - return tmsp.NewError(tmsp.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) - } - // TODO: need to return receipt so rpc.ResultBroadcastTx.Data (or Log) is the receipt - return tmsp.NewResultOK(nil, "Success") + app.nTxs += 1 + + // XXX: if we had tx ids we could cache the decoded txs on CheckTx + var n int + var err error + tx := new(types.Tx) + buf := bytes.NewBuffer(txBytes) + wire.ReadBinaryPtr(tx, buf, len(txBytes), &n, &err) + if err != nil { + return tmsp.NewError(tmsp.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) + } + + log.Info("AppendTx", "tx", *tx) + + err = sm.ExecTx(app.cache, *tx, true, app.evc) + if err != nil { + return tmsp.NewError(tmsp.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) + } + // TODO: need to return receipt so rpc.ResultBroadcastTx.Data (or Log) is the receipt + return tmsp.NewResultOK(nil, "Success") } // Implements tmsp.Application func (app *ErisMint) CheckTx(txBytes []byte) (res tmsp.Result) { - var n int - var err error - tx := new(types.Tx) - buf := bytes.NewBuffer(txBytes) - wire.ReadBinaryPtr(tx, buf, len(txBytes), &n, &err) - if err != nil { - return tmsp.NewError(tmsp.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) - } - - log.Info("CheckTx", "tx", *tx) - - // TODO: make errors tmsp aware - err = sm.ExecTx(app.checkCache, *tx, false, nil) - if err != nil { - return tmsp.NewError(tmsp.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) - } - - // TODO: need to return receipt so rpc.ResultBroadcastTx.Data (or Log) is the receipt - return tmsp.NewResultOK(nil, "Success") + var n int + var err error + tx := new(types.Tx) + buf := bytes.NewBuffer(txBytes) + wire.ReadBinaryPtr(tx, buf, len(txBytes), &n, &err) + if err != nil { + return tmsp.NewError(tmsp.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) + } + + log.Info("CheckTx", "tx", *tx) + + // TODO: make errors tmsp aware + err = sm.ExecTx(app.checkCache, *tx, false, nil) + if err != nil { + return tmsp.NewError(tmsp.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) + } + + // TODO: need to return receipt so rpc.ResultBroadcastTx.Data (or Log) is the receipt + return tmsp.NewResultOK(nil, "Success") } // Implements tmsp.Application // Commit the state (called at end of block) // NOTE: CheckTx/AppendTx must not run concurrently with Commit - -// the mempool should run during AppendTxs, but lock for Commit and Update +// the mempool should run during AppendTxs, but lock for Commit and Update func (app *ErisMint) Commit() (res tmsp.Result) { - app.mtx.Lock() // the lock protects app.state - defer app.mtx.Unlock() - - app.state.LastBlockHeight += 1 - log.Info("Commit", "block", app.state.LastBlockHeight) - - // sync the AppendTx cache - app.cache.Sync() - - // if there were any txs in the block, - // reset the check cache to the new height - if app.nTxs > 0 { - log.Info("Reset checkCache", "txs", app.nTxs) - app.checkCache = sm.NewBlockCache(app.state) - } - app.nTxs = 0 - - // save state to disk - app.state.Save() - - // flush events to listeners (XXX: note issue with blocking) - app.evc.Flush() - - return tmsp.NewResultOK(app.state.Hash(), "Success") + app.mtx.Lock() // the lock protects app.state + defer app.mtx.Unlock() + + app.state.LastBlockHeight += 1 + log.WithFields(log.Fields{ + "blockheight" : app.state.LastBlockHeight, + }).Info("Commit block") + + // sync the AppendTx cache + app.cache.Sync() + + // if there were any txs in the block, + // reset the check cache to the new height + if app.nTxs > 0 { + log.WithFields(log.Fields{ + "txs" : app.nTxs, + }).Info("Reset checkCache") + app.checkCache = sm.NewBlockCache(app.state) + } + app.nTxs = 0 + + // save state to disk + app.state.Save() + + // flush events to listeners (XXX: note issue with blocking) + app.evc.Flush() + + return tmsp.NewResultOK(app.state.Hash(), "Success") } func (app *ErisMint) Query(query []byte) (res tmsp.Result) { - return tmsp.NewResultOK(nil, "Success") + return tmsp.NewResultOK(nil, "Success") } diff --git a/manager/eris-mint/version.go b/manager/eris-mint/version.go new file mode 100644 index 0000000000000000000000000000000000000000..bfc3b18c18c8335f686e0a3ffa56436c80594969 --- /dev/null +++ b/manager/eris-mint/version.go @@ -0,0 +1,37 @@ +// Copyright 2015, 2016 Eris Industries (UK) Ltd. +// This file is part of Eris-RT + +// Eris-RT is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Eris-RT is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Eris-RT. If not, see <http://www.gnu.org/licenses/>. + +package erismint + +import ( + version "github.com/eris-ltd/eris-db/version" +) + +const ( + // Client identifier to advertise over the network + erisMintClientIdentifier = "erismint" + // Major version component of the current release + erisMintVersionMajor = 0 + // Minor version component of the current release + erisMintVersionMinor = 12 + // Patch version component of the current release + erisMintVersionPatch = 0 +) + +func GetErisMintVersion() *version.VersionIdentifier { + return version.New(erisMintClientIdentifier, erisMintVersionMajor, + erisMintVersionMinor, erisMintVersionPatch) +} diff --git a/server_config.toml b/server_config.toml index 676801b56f1ef0a89e316a80a082e332d5176588..4aa9cbc81cc425e2183d5bc50f51545b893fdc79 100644 --- a/server_config.toml +++ b/server_config.toml @@ -49,7 +49,16 @@ minor_version = 12 ## ############################################################################### - [chain.application_manager] + [chain.manager] + # application manager name defines the module to use for handling + # the transactions. Supported names are `erismint` + name = "erismint" + # version is the major and minor semantic version; + # the version will be asserted on + major_version = 0 + minor_version = 12 + # relative path to application manager root folder + relative_root = "erismint" [servers] @@ -73,3 +82,12 @@ minor_version = 12 [tmsp] # listener address for accepting tendermint socket protocol connections listener = "tcp://0.0.0.0:46658" + +################################################################################ +## +## Eris-Mint +## +## +################################################################################ + +[erismint]