Merge pull request #266 from cosmos/feature/iavl-app
Refactor out BaseApp, StoreApp (per spec #1 and #2)
This commit is contained in:
commit
de87acef0d
240
app/app.go
240
app/app.go
|
@ -1,240 +0,0 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/stack"
|
||||
sm "github.com/cosmos/cosmos-sdk/state"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
)
|
||||
|
||||
//nolint
|
||||
const (
|
||||
ModuleNameBase = "base"
|
||||
ChainKey = "chain_id"
|
||||
)
|
||||
|
||||
// Basecoin - The ABCI application
|
||||
type Basecoin struct {
|
||||
info *sm.ChainState
|
||||
state *Store
|
||||
|
||||
handler sdk.Handler
|
||||
tick Ticker
|
||||
|
||||
pending []*abci.Validator
|
||||
height uint64
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// Ticker - tick function
|
||||
type Ticker func(sm.SimpleDB) ([]*abci.Validator, error)
|
||||
|
||||
var _ abci.Application = &Basecoin{}
|
||||
|
||||
// NewBasecoin - create a new instance of the basecoin application
|
||||
func NewBasecoin(handler sdk.Handler, store *Store, logger log.Logger) *Basecoin {
|
||||
return &Basecoin{
|
||||
handler: handler,
|
||||
info: sm.NewChainState(),
|
||||
state: store,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBasecoinTick - create a new instance of the basecoin application with tick functionality
|
||||
func NewBasecoinTick(handler sdk.Handler, store *Store, logger log.Logger, tick Ticker) *Basecoin {
|
||||
return &Basecoin{
|
||||
handler: handler,
|
||||
info: sm.NewChainState(),
|
||||
state: store,
|
||||
logger: logger,
|
||||
tick: tick,
|
||||
}
|
||||
}
|
||||
|
||||
// GetChainID returns the currently stored chain
|
||||
func (app *Basecoin) GetChainID() string {
|
||||
return app.info.GetChainID(app.state.Committed())
|
||||
}
|
||||
|
||||
// GetState is back... please kill me
|
||||
func (app *Basecoin) GetState() sm.SimpleDB {
|
||||
return app.state.Append()
|
||||
}
|
||||
|
||||
// Info - ABCI
|
||||
func (app *Basecoin) Info(req abci.RequestInfo) abci.ResponseInfo {
|
||||
resp := app.state.Info()
|
||||
app.logger.Debug("Info",
|
||||
"height", resp.LastBlockHeight,
|
||||
"hash", fmt.Sprintf("%X", resp.LastBlockAppHash))
|
||||
app.height = resp.LastBlockHeight
|
||||
return abci.ResponseInfo{
|
||||
Data: fmt.Sprintf("Basecoin v%v", version.Version),
|
||||
LastBlockHeight: resp.LastBlockHeight,
|
||||
LastBlockAppHash: resp.LastBlockAppHash,
|
||||
}
|
||||
}
|
||||
|
||||
// InitState - used to setup state (was SetOption)
|
||||
// to be used by InitChain later
|
||||
func (app *Basecoin) InitState(key string, value string) string {
|
||||
module, key := splitKey(key)
|
||||
state := app.state.Append()
|
||||
|
||||
if module == ModuleNameBase {
|
||||
if key == ChainKey {
|
||||
app.info.SetChainID(state, value)
|
||||
return "Success"
|
||||
}
|
||||
return fmt.Sprintf("Error: unknown base option: %s", key)
|
||||
}
|
||||
|
||||
log, err := app.handler.InitState(app.logger, state, module, key, value)
|
||||
if err == nil {
|
||||
return log
|
||||
}
|
||||
return "Error: " + err.Error()
|
||||
}
|
||||
|
||||
// SetOption - ABCI
|
||||
func (app *Basecoin) SetOption(key string, value string) string {
|
||||
return "Not Implemented"
|
||||
}
|
||||
|
||||
// DeliverTx - ABCI
|
||||
func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
||||
tx, err := sdk.LoadTx(txBytes)
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
|
||||
ctx := stack.NewContext(
|
||||
app.GetChainID(),
|
||||
app.height,
|
||||
app.logger.With("call", "delivertx"),
|
||||
)
|
||||
res, err := app.handler.DeliverTx(ctx, app.state.Append(), tx)
|
||||
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
app.addValChange(res.Diff)
|
||||
return sdk.ToABCI(res)
|
||||
}
|
||||
|
||||
// CheckTx - ABCI
|
||||
func (app *Basecoin) CheckTx(txBytes []byte) abci.Result {
|
||||
tx, err := sdk.LoadTx(txBytes)
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
|
||||
ctx := stack.NewContext(
|
||||
app.GetChainID(),
|
||||
app.height,
|
||||
app.logger.With("call", "checktx"),
|
||||
)
|
||||
res, err := app.handler.CheckTx(ctx, app.state.Check(), tx)
|
||||
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
return sdk.ToABCI(res)
|
||||
}
|
||||
|
||||
// Query - ABCI
|
||||
func (app *Basecoin) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
|
||||
if len(reqQuery.Data) == 0 {
|
||||
resQuery.Log = "Query cannot be zero length"
|
||||
resQuery.Code = abci.CodeType_EncodingError
|
||||
return
|
||||
}
|
||||
|
||||
return app.state.Query(reqQuery)
|
||||
}
|
||||
|
||||
// Commit - ABCI
|
||||
func (app *Basecoin) Commit() (res abci.Result) {
|
||||
// Commit state
|
||||
res = app.state.Commit()
|
||||
if res.IsErr() {
|
||||
cmn.PanicSanity("Error getting hash: " + res.Error())
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// InitChain - ABCI
|
||||
func (app *Basecoin) InitChain(req abci.RequestInitChain) {
|
||||
// for _, plugin := range app.plugins.GetList() {
|
||||
// plugin.InitChain(app.state, validators)
|
||||
// }
|
||||
}
|
||||
|
||||
// BeginBlock - ABCI
|
||||
func (app *Basecoin) BeginBlock(req abci.RequestBeginBlock) {
|
||||
app.height++
|
||||
|
||||
// for _, plugin := range app.plugins.GetList() {
|
||||
// plugin.BeginBlock(app.state, hash, header)
|
||||
// }
|
||||
|
||||
if app.tick != nil {
|
||||
diff, err := app.tick(app.state.Append())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.addValChange(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// EndBlock - ABCI
|
||||
// Returns a list of all validator changes made in this block
|
||||
func (app *Basecoin) EndBlock(height uint64) (res abci.ResponseEndBlock) {
|
||||
// TODO: cleanup in case a validator exists multiple times in the list
|
||||
res.Diffs = app.pending
|
||||
app.pending = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (app *Basecoin) addValChange(diffs []*abci.Validator) {
|
||||
for _, d := range diffs {
|
||||
idx := pubKeyIndex(d, app.pending)
|
||||
if idx >= 0 {
|
||||
app.pending[idx] = d
|
||||
} else {
|
||||
app.pending = append(app.pending, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return index of list with validator of same PubKey, or -1 if no match
|
||||
func pubKeyIndex(val *abci.Validator, list []*abci.Validator) int {
|
||||
for i, v := range list {
|
||||
if bytes.Equal(val.PubKey, v.PubKey) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
//TODO move split key to tmlibs?
|
||||
|
||||
// Splits the string at the first '/'.
|
||||
// if there are none, assign default module ("base").
|
||||
func splitKey(key string) (string, string) {
|
||||
if strings.Contains(key, "/") {
|
||||
keyParts := strings.SplitN(key, "/", 2)
|
||||
return keyParts[0], keyParts[1]
|
||||
}
|
||||
return ModuleNameBase, key
|
||||
}
|
|
@ -55,7 +55,7 @@ func DefaultHandler(feeDenom string) sdk.Handler {
|
|||
type appTest struct {
|
||||
t *testing.T
|
||||
chainID string
|
||||
app *Basecoin
|
||||
app *BaseApp
|
||||
acctIn *coin.AccountWithKey
|
||||
acctOut *coin.AccountWithKey
|
||||
}
|
||||
|
@ -100,8 +100,8 @@ func (at *appTest) feeTx(coins coin.Coins, toll coin.Coin, sequence uint32) sdk.
|
|||
|
||||
// set the account on the app through InitState
|
||||
func (at *appTest) initAccount(acct *coin.AccountWithKey) {
|
||||
res := at.app.InitState("coin/account", acct.MakeOption())
|
||||
require.EqualValues(at.t, res, "Success")
|
||||
err := at.app.InitState("coin", "account", acct.MakeOption())
|
||||
require.Nil(at.t, err, "%+v", err)
|
||||
}
|
||||
|
||||
// reset the in and out accs to be one account each with 7mycoin
|
||||
|
@ -112,17 +112,13 @@ func (at *appTest) reset() {
|
|||
// Note: switch logger if you want to get more info
|
||||
logger := log.TestingLogger()
|
||||
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
|
||||
store, err := NewStore("", 0, logger.With("module", "store"))
|
||||
|
||||
store, err := NewStoreApp("app-test", "", 0, logger)
|
||||
require.Nil(at.t, err, "%+v", err)
|
||||
at.app = NewBaseApp(store, DefaultHandler("mycoin"), nil)
|
||||
|
||||
at.app = NewBasecoin(
|
||||
DefaultHandler("mycoin"),
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
|
||||
res := at.app.InitState("base/chain_id", at.chainID)
|
||||
require.EqualValues(at.t, res, "Success")
|
||||
err = at.app.InitState("base", "chain_id", at.chainID)
|
||||
require.Nil(at.t, err, "%+v", err)
|
||||
|
||||
at.initAccount(at.acctIn)
|
||||
at.initAccount(at.acctOut)
|
||||
|
@ -146,9 +142,9 @@ func getAddr(addr []byte, state state.SimpleDB) (coin.Coins, error) {
|
|||
func (at *appTest) exec(t *testing.T, tx sdk.Tx, checkTx bool) (res abci.Result, diffIn, diffOut coin.Coins) {
|
||||
require := require.New(t)
|
||||
|
||||
initBalIn, err := getBalance(at.acctIn.Actor(), at.app.GetState())
|
||||
initBalIn, err := getBalance(at.acctIn.Actor(), at.app.Append())
|
||||
require.Nil(err, "%+v", err)
|
||||
initBalOut, err := getBalance(at.acctOut.Actor(), at.app.GetState())
|
||||
initBalOut, err := getBalance(at.acctOut.Actor(), at.app.Append())
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
txBytes := wire.BinaryBytes(tx)
|
||||
|
@ -158,9 +154,9 @@ func (at *appTest) exec(t *testing.T, tx sdk.Tx, checkTx bool) (res abci.Result,
|
|||
res = at.app.DeliverTx(txBytes)
|
||||
}
|
||||
|
||||
endBalIn, err := getBalance(at.acctIn.Actor(), at.app.GetState())
|
||||
endBalIn, err := getBalance(at.acctIn.Actor(), at.app.Append())
|
||||
require.Nil(err, "%+v", err)
|
||||
endBalOut, err := getBalance(at.acctOut.Actor(), at.app.GetState())
|
||||
endBalOut, err := getBalance(at.acctOut.Actor(), at.app.Append())
|
||||
require.Nil(err, "%+v", err)
|
||||
return res, endBalIn.Minus(initBalIn), endBalOut.Minus(initBalOut)
|
||||
}
|
||||
|
@ -172,29 +168,24 @@ func TestInitState(t *testing.T) {
|
|||
require := require.New(t)
|
||||
|
||||
logger := log.TestingLogger()
|
||||
store, err := NewStore("", 0, logger.With("module", "store"))
|
||||
store, err := NewStoreApp("app-test", "", 0, logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
app := NewBasecoin(
|
||||
DefaultHandler("atom"),
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
app := NewBaseApp(store, DefaultHandler("atom"), nil)
|
||||
|
||||
//testing ChainID
|
||||
chainID := "testChain"
|
||||
res := app.InitState("base/chain_id", chainID)
|
||||
err = app.InitState("base", "chain_id", chainID)
|
||||
require.Nil(err, "%+v", err)
|
||||
assert.EqualValues(app.GetChainID(), chainID)
|
||||
assert.EqualValues(res, "Success")
|
||||
|
||||
// make a nice account...
|
||||
bal := coin.Coins{{"atom", 77}, {"eth", 12}}
|
||||
acct := coin.NewAccountWithKey(bal)
|
||||
res = app.InitState("coin/account", acct.MakeOption())
|
||||
require.EqualValues(res, "Success")
|
||||
err = app.InitState("coin", "account", acct.MakeOption())
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// make sure it is set correctly, with some balance
|
||||
coins, err := getBalance(acct.Actor(), app.GetState())
|
||||
coins, err := getBalance(acct.Actor(), app.Append())
|
||||
require.Nil(err)
|
||||
assert.Equal(bal, coins)
|
||||
|
||||
|
@ -218,23 +209,22 @@ func TestInitState(t *testing.T) {
|
|||
}
|
||||
]
|
||||
}`
|
||||
res = app.InitState("coin/account", unsortAcc)
|
||||
require.EqualValues(res, "Success")
|
||||
err = app.InitState("coin", "account", unsortAcc)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
coins, err = getAddr(unsortAddr, app.GetState())
|
||||
coins, err = getAddr(unsortAddr, app.Append())
|
||||
require.Nil(err)
|
||||
assert.True(coins.IsValid())
|
||||
assert.Equal(unsortCoins, coins)
|
||||
|
||||
res = app.InitState("base/dslfkgjdas", "")
|
||||
assert.NotEqual(res, "Success")
|
||||
err = app.InitState("base", "dslfkgjdas", "")
|
||||
require.NotNil(err)
|
||||
|
||||
res = app.InitState("dslfkgjdas", "")
|
||||
assert.NotEqual(res, "Success")
|
||||
|
||||
res = app.InitState("dslfkgjdas/szfdjzs", "")
|
||||
assert.NotEqual(res, "Success")
|
||||
err = app.InitState("", "dslfkgjdas", "")
|
||||
require.NotNil(err)
|
||||
|
||||
err = app.InitState("dslfkgjdas", "szfdjzs", "")
|
||||
require.NotNil(err)
|
||||
}
|
||||
|
||||
// Test CheckTx and DeliverTx with insufficient and sufficient balance
|
||||
|
@ -301,19 +291,3 @@ func TestQuery(t *testing.T) {
|
|||
})
|
||||
assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit")
|
||||
}
|
||||
|
||||
func TestSplitKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
prefix, suffix := splitKey("foo/bar")
|
||||
assert.EqualValues("foo", prefix)
|
||||
assert.EqualValues("bar", suffix)
|
||||
|
||||
prefix, suffix = splitKey("foobar")
|
||||
assert.EqualValues("base", prefix)
|
||||
assert.EqualValues("foobar", suffix)
|
||||
|
||||
prefix, suffix = splitKey("some/complex/issue")
|
||||
assert.EqualValues("some", prefix)
|
||||
assert.EqualValues("complex/issue", suffix)
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/stack"
|
||||
)
|
||||
|
||||
// BaseApp - The ABCI application
|
||||
type BaseApp struct {
|
||||
*StoreApp
|
||||
handler sdk.Handler
|
||||
clock sdk.Ticker
|
||||
}
|
||||
|
||||
var _ abci.Application = &BaseApp{}
|
||||
|
||||
// NewBaseApp extends a StoreApp with a handler and a ticker,
|
||||
// which it binds to the proper abci calls
|
||||
func NewBaseApp(store *StoreApp, handler sdk.Handler, clock sdk.Ticker) *BaseApp {
|
||||
return &BaseApp{
|
||||
StoreApp: store,
|
||||
handler: handler,
|
||||
clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
// DeliverTx - ABCI - dispatches to the handler
|
||||
func (app *BaseApp) DeliverTx(txBytes []byte) abci.Result {
|
||||
tx, err := sdk.LoadTx(txBytes)
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
|
||||
ctx := stack.NewContext(
|
||||
app.GetChainID(),
|
||||
app.WorkingHeight(),
|
||||
app.Logger().With("call", "delivertx"),
|
||||
)
|
||||
res, err := app.handler.DeliverTx(ctx, app.Append(), tx)
|
||||
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
app.AddValChange(res.Diff)
|
||||
return sdk.ToABCI(res)
|
||||
}
|
||||
|
||||
// CheckTx - ABCI - dispatches to the handler
|
||||
func (app *BaseApp) CheckTx(txBytes []byte) abci.Result {
|
||||
tx, err := sdk.LoadTx(txBytes)
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
|
||||
ctx := stack.NewContext(
|
||||
app.GetChainID(),
|
||||
app.WorkingHeight(),
|
||||
app.Logger().With("call", "checktx"),
|
||||
)
|
||||
res, err := app.handler.CheckTx(ctx, app.Check(), tx)
|
||||
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
return sdk.ToABCI(res)
|
||||
}
|
||||
|
||||
// BeginBlock - ABCI - triggers Tick actions
|
||||
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) {
|
||||
// execute tick if present
|
||||
if app.clock != nil {
|
||||
ctx := stack.NewContext(
|
||||
app.GetChainID(),
|
||||
app.WorkingHeight(),
|
||||
app.Logger().With("call", "tick"),
|
||||
)
|
||||
|
||||
diff, err := app.clock.Tick(ctx, app.Append())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.AddValChange(diff)
|
||||
}
|
||||
}
|
||||
|
||||
// InitState - used to setup state (was SetOption)
|
||||
// to be used by InitChain later
|
||||
//
|
||||
// TODO: rethink this a bit more....
|
||||
func (app *BaseApp) InitState(module, key, value string) error {
|
||||
state := app.Append()
|
||||
logger := app.Logger().With("module", module, "key", key)
|
||||
|
||||
if module == sdk.ModuleNameBase {
|
||||
if key == sdk.ChainKey {
|
||||
app.info.SetChainID(state, value)
|
||||
return nil
|
||||
}
|
||||
logger.Error("Invalid genesis option")
|
||||
return fmt.Errorf("Unknown base option: %s", key)
|
||||
}
|
||||
|
||||
log, err := app.handler.InitState(logger, state, module, key, value)
|
||||
if err != nil {
|
||||
logger.Error("Invalid genesis option", "err", err)
|
||||
} else {
|
||||
logger.Info(log)
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Package app contains data structures that provide basic
|
||||
data storage functionality and act as a bridge between the abci
|
||||
interface and the internal sdk representations.
|
||||
|
||||
StoreApp handles creating a datastore or loading an existing one
|
||||
from disk, provides helpers to use in the transaction workflow
|
||||
(check/deliver/commit), and provides bindings to the ABCI interface
|
||||
for functionality such as handshaking with tendermint on restart,
|
||||
querying the data store, and handling begin/end block and commit messages.
|
||||
It does not handle CheckTx or DeliverTx, or have any logic for modifying
|
||||
the state, and is quite generic if you don't wish to use the standard Handlers.
|
||||
|
||||
BaseApp embeds StoreApp and extends it for the standard sdk usecase, where
|
||||
we dispatch all CheckTx/DeliverTx messages to a handler (which may contain
|
||||
decorators and a router to multiple modules), and supports a Ticker which
|
||||
is called every BeginBlock.
|
||||
*/
|
||||
package app
|
|
@ -1,99 +0,0 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
// LoadGenesis - Load the genesis file into memory
|
||||
func (app *Basecoin) LoadGenesis(path string) error {
|
||||
genDoc, err := loadGenesis(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set chain_id
|
||||
app.InitState("base/chain_id", genDoc.ChainID)
|
||||
|
||||
// set accounts
|
||||
for _, acct := range genDoc.AppOptions.Accounts {
|
||||
_ = app.InitState("coin/account", string(acct))
|
||||
}
|
||||
|
||||
// set plugin options
|
||||
for _, kv := range genDoc.AppOptions.pluginOptions {
|
||||
_ = app.InitState(kv.Key, kv.Value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type keyValue struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// FullGenesisDoc - includes tendermint (in the json, we ignore here)
|
||||
type FullGenesisDoc struct {
|
||||
ChainID string `json:"chain_id"`
|
||||
AppOptions *GenesisDoc `json:"app_options"`
|
||||
}
|
||||
|
||||
// GenesisDoc - All genesis values
|
||||
type GenesisDoc struct {
|
||||
Accounts []json.RawMessage `json:"accounts"`
|
||||
PluginOptions []json.RawMessage `json:"plugin_options"`
|
||||
|
||||
pluginOptions []keyValue // unmarshaled rawmessages
|
||||
}
|
||||
|
||||
func loadGenesis(filePath string) (*FullGenesisDoc, error) {
|
||||
bytes, err := cmn.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "loading genesis file")
|
||||
}
|
||||
|
||||
// the basecoin genesis go-wire/data :)
|
||||
genDoc := new(FullGenesisDoc)
|
||||
err = json.Unmarshal(bytes, genDoc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshaling genesis file")
|
||||
}
|
||||
|
||||
if genDoc.AppOptions == nil {
|
||||
genDoc.AppOptions = new(GenesisDoc)
|
||||
}
|
||||
|
||||
pluginOpts, err := parseGenesisList(genDoc.AppOptions.PluginOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
genDoc.AppOptions.pluginOptions = pluginOpts
|
||||
return genDoc, nil
|
||||
}
|
||||
|
||||
func parseGenesisList(kvzIn []json.RawMessage) (kvz []keyValue, err error) {
|
||||
if len(kvzIn)%2 != 0 {
|
||||
return nil, errors.New("genesis cannot have an odd number of items. Format = [key1, value1, key2, value2, ...]")
|
||||
}
|
||||
|
||||
for i := 0; i < len(kvzIn); i += 2 {
|
||||
kv := keyValue{}
|
||||
rawK := []byte(kvzIn[i])
|
||||
err := json.Unmarshal(rawK, &(kv.Key))
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Non-string key: %s", string(rawK))
|
||||
}
|
||||
// convert value to string if possible (otherwise raw json)
|
||||
rawV := kvzIn[i+1]
|
||||
err = json.Unmarshal(rawV, &(kv.Value))
|
||||
if err != nil {
|
||||
kv.Value = string(rawV)
|
||||
}
|
||||
kvz = append(kvz, kv)
|
||||
}
|
||||
return kvz, nil
|
||||
}
|
|
@ -2,59 +2,44 @@ package app
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/genesis"
|
||||
"github.com/cosmos/cosmos-sdk/modules/coin"
|
||||
)
|
||||
|
||||
const genesisFilepath = "./testdata/genesis.json"
|
||||
const genesisAcctFilepath = "./testdata/genesis2.json"
|
||||
|
||||
// 2b is just like 2, but add carl who has inconsistent
|
||||
// pubkey and address
|
||||
const genesisBadAcctFilepath = "./testdata/genesis2b.json"
|
||||
|
||||
func TestLoadGenesisDoNotFailIfAppOptionsAreMissing(t *testing.T) {
|
||||
logger := log.TestingLogger()
|
||||
store, err := NewStore("", 0, logger)
|
||||
store, err := MockStoreApp("genesis", logger)
|
||||
require.Nil(t, err, "%+v", err)
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||
err = app.LoadGenesis("./testdata/genesis3.json")
|
||||
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
|
||||
|
||||
err = genesis.Load(app, "./testdata/genesis3.json")
|
||||
require.Nil(t, err, "%+v", err)
|
||||
}
|
||||
|
||||
func TestLoadGenesis(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
func TestLoadGenesisFailsWithUnknownOptions(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
logger := log.TestingLogger()
|
||||
store, err := NewStore("", 0, logger)
|
||||
store, err := MockStoreApp("genesis", logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||
err = app.LoadGenesis(genesisFilepath)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// check the chain id
|
||||
assert.Equal("foo_bar_chain", app.GetChainID())
|
||||
|
||||
// and check the account info - previously calculated values
|
||||
addr, _ := hex.DecodeString("eb98e0688217cfdeb70eddf4b33cdcc37fc53197")
|
||||
|
||||
coins, err := getAddr(addr, app.GetState())
|
||||
require.Nil(err)
|
||||
assert.True(coins.IsPositive())
|
||||
|
||||
// make sure balance is proper
|
||||
assert.Equal(2, len(coins))
|
||||
assert.True(coins.IsValid())
|
||||
// note, that we now sort them to be valid
|
||||
assert.EqualValues(654321, coins[0].Amount)
|
||||
assert.EqualValues("ETH", coins[0].Denom)
|
||||
assert.EqualValues(12345, coins[1].Amount)
|
||||
assert.EqualValues("blank", coins[1].Denom)
|
||||
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
|
||||
err = genesis.Load(app, genesisFilepath)
|
||||
require.NotNil(err, "%+v", err)
|
||||
}
|
||||
|
||||
// Fix for issue #89, change the parse format for accounts in genesis.json
|
||||
|
@ -62,11 +47,11 @@ func TestLoadGenesisAccountAddress(t *testing.T) {
|
|||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
logger := log.TestingLogger()
|
||||
store, err := NewStore("", 0, logger)
|
||||
store, err := MockStoreApp("genesis", logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
|
||||
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||
err = app.LoadGenesis(genesisAcctFilepath)
|
||||
err = genesis.Load(app, genesisAcctFilepath)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// check the chain id
|
||||
|
@ -83,9 +68,6 @@ func TestLoadGenesisAccountAddress(t *testing.T) {
|
|||
{"62035D628DE7543332544AA60D90D3693B6AD51B", true, true, coin.Coins{{"one", 111}}},
|
||||
// this comes from an address, should be stored proper (bob)
|
||||
{"C471FB670E44D219EE6DF2FC284BE38793ACBCE1", true, false, coin.Coins{{"two", 222}}},
|
||||
// this one had a mismatched address and pubkey, should not store under either (carl)
|
||||
{"1234ABCDD18E8EFE3FFC4B0506BF9BF8E5B0D9E9", false, false, nil}, // this is given addr
|
||||
{"700BEC5ED18E8EFE3FFC4B0506BF9BF8E5B0D9E9", false, false, nil}, // this is addr of the given pubkey
|
||||
// this comes from a secp256k1 public key, should be stored proper (sam)
|
||||
{"979F080B1DD046C452C2A8A250D18646C6B669D4", true, true, coin.Coins{{"four", 444}}},
|
||||
}
|
||||
|
@ -93,7 +75,7 @@ func TestLoadGenesisAccountAddress(t *testing.T) {
|
|||
for i, tc := range cases {
|
||||
addr, err := hex.DecodeString(tc.addr)
|
||||
require.Nil(err, tc.addr)
|
||||
coins, err := getAddr(addr, app.GetState())
|
||||
coins, err := getAddr(addr, app.Append())
|
||||
require.Nil(err, "%+v", err)
|
||||
if !tc.exists {
|
||||
assert.True(coins.IsZero(), "%d", i)
|
||||
|
@ -105,23 +87,15 @@ func TestLoadGenesisAccountAddress(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseGenesisList(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
// When you define an account in genesis with address
|
||||
// and pubkey that don't match
|
||||
func TestLoadGenesisAccountInconsistentAddress(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
bytes, err := cmn.ReadFile(genesisFilepath)
|
||||
require.Nil(err, "loading genesis file %+v", err)
|
||||
|
||||
// the basecoin genesis go-wire/data :)
|
||||
genDoc := new(FullGenesisDoc)
|
||||
err = json.Unmarshal(bytes, genDoc)
|
||||
require.Nil(err, "unmarshaling genesis file %+v", err)
|
||||
|
||||
pluginOpts, err := parseGenesisList(genDoc.AppOptions.PluginOptions)
|
||||
logger := log.TestingLogger()
|
||||
store, err := MockStoreApp("genesis", logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
genDoc.AppOptions.pluginOptions = pluginOpts
|
||||
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[0].Key, "plugin1/key1")
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[1].Key, "plugin1/key2")
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[0].Value, "value1")
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[1].Value, "value2")
|
||||
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
|
||||
err = genesis.Load(app, genesisBadAcctFilepath)
|
||||
require.NotNil(err)
|
||||
}
|
||||
|
|
289
app/store.go
289
app/store.go
|
@ -1,132 +1,140 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/iavl"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/state"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
sm "github.com/cosmos/cosmos-sdk/state"
|
||||
)
|
||||
|
||||
// Store contains the merkle tree, and all info to handle abci requests
|
||||
type Store struct {
|
||||
state.State
|
||||
// StoreApp contains a data store and all info needed
|
||||
// to perform queries and handshakes.
|
||||
//
|
||||
// It should be embeded in another struct for CheckTx,
|
||||
// DeliverTx and initializing state from the genesis.
|
||||
type StoreApp struct {
|
||||
// Name is what is returned from info
|
||||
Name string
|
||||
|
||||
// this is the database state
|
||||
info *sm.ChainState
|
||||
state *sm.State
|
||||
|
||||
// cached validator changes from DeliverTx
|
||||
pending []*abci.Validator
|
||||
|
||||
// height is last committed block, DeliverTx is the next one
|
||||
height uint64
|
||||
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// MockStore returns an in-memory store only intended for testing
|
||||
func MockStore() *Store {
|
||||
res, err := NewStore("", 0, log.NewNopLogger())
|
||||
// NewStoreApp creates a data store to handle queries
|
||||
func NewStoreApp(appName, dbName string, cacheSize int, logger log.Logger) (*StoreApp, error) {
|
||||
state, err := loadState(dbName, cacheSize)
|
||||
if err != nil {
|
||||
// should never happen, abort test if it does
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
return res
|
||||
app := &StoreApp{
|
||||
Name: appName,
|
||||
state: state,
|
||||
height: state.LatestHeight(),
|
||||
info: sm.NewChainState(),
|
||||
logger: logger.With("module", "app"),
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// NewStore initializes an in-memory iavl.VersionedTree, or attempts to load a
|
||||
// persistant tree from disk
|
||||
func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) {
|
||||
// memory backed case, just for testing
|
||||
if dbName == "" {
|
||||
tree := iavl.NewVersionedTree(
|
||||
0,
|
||||
dbm.NewMemDB(),
|
||||
)
|
||||
store := &Store{
|
||||
State: state.NewState(tree),
|
||||
logger: logger,
|
||||
}
|
||||
return store, nil
|
||||
}
|
||||
// MockStoreApp returns a Store app with no persistence
|
||||
func MockStoreApp(appName string, logger log.Logger) (*StoreApp, error) {
|
||||
return NewStoreApp(appName, "", 0, logger)
|
||||
}
|
||||
|
||||
// Expand the path fully
|
||||
dbPath, err := filepath.Abs(dbName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Invalid Database Name")
|
||||
}
|
||||
// GetChainID returns the currently stored chain
|
||||
func (app *StoreApp) GetChainID() string {
|
||||
return app.info.GetChainID(app.state.Committed())
|
||||
}
|
||||
|
||||
// Some external calls accidently add a ".db", which is now removed
|
||||
dbPath = strings.TrimSuffix(dbPath, path.Ext(dbPath))
|
||||
|
||||
// Split the database name into it's components (dir, name)
|
||||
dir := path.Dir(dbPath)
|
||||
name := path.Base(dbPath)
|
||||
|
||||
// Make sure the path exists
|
||||
empty, _ := cmn.IsDirEmpty(dbPath + ".db")
|
||||
|
||||
// Open database called "dir/name.db", if it doesn't exist it will be created
|
||||
db := dbm.NewDB(name, dbm.LevelDBBackendStr, dir)
|
||||
tree := iavl.NewVersionedTree(cacheSize, db)
|
||||
|
||||
if empty {
|
||||
logger.Info("no existing db, creating new db")
|
||||
} else {
|
||||
logger.Info("loading existing db")
|
||||
if err = tree.Load(); err != nil {
|
||||
return nil, errors.Wrap(err, "Loading tree")
|
||||
}
|
||||
}
|
||||
|
||||
res := &Store{
|
||||
State: state.NewState(tree),
|
||||
logger: logger,
|
||||
}
|
||||
res.height = res.State.LatestHeight()
|
||||
return res, nil
|
||||
// Logger returns the application base logger
|
||||
func (app *StoreApp) Logger() log.Logger {
|
||||
return app.logger
|
||||
}
|
||||
|
||||
// Hash gets the last hash stored in the database
|
||||
func (s *Store) Hash() []byte {
|
||||
return s.State.LatestHash()
|
||||
func (app *StoreApp) Hash() []byte {
|
||||
return app.state.LatestHash()
|
||||
}
|
||||
|
||||
// Info implements abci.Application. It returns the height, hash and size (in the data).
|
||||
// Committed returns the committed state,
|
||||
// also exposing historical queries
|
||||
// func (app *StoreApp) Committed() *Bonsai {
|
||||
// return app.state.committed
|
||||
// }
|
||||
|
||||
// Append returns the working state for DeliverTx
|
||||
func (app *StoreApp) Append() sm.SimpleDB {
|
||||
return app.state.Append()
|
||||
}
|
||||
|
||||
// Check returns the working state for CheckTx
|
||||
func (app *StoreApp) Check() sm.SimpleDB {
|
||||
return app.state.Check()
|
||||
}
|
||||
|
||||
// CommittedHeight gets the last block height committed
|
||||
// to the db
|
||||
func (app *StoreApp) CommittedHeight() uint64 {
|
||||
return app.height
|
||||
}
|
||||
|
||||
// WorkingHeight gets the current block we are writing
|
||||
func (app *StoreApp) WorkingHeight() uint64 {
|
||||
return app.height + 1
|
||||
}
|
||||
|
||||
// Info implements abci.Application. It returns the height and hash,
|
||||
// as well as the abci name and version.
|
||||
//
|
||||
// The height is the block that holds the transactions, not the apphash itself.
|
||||
func (s *Store) Info() abci.ResponseInfo {
|
||||
s.logger.Info("Info synced",
|
||||
"height", s.height,
|
||||
"hash", fmt.Sprintf("%X", s.Hash()))
|
||||
func (app *StoreApp) Info(req abci.RequestInfo) abci.ResponseInfo {
|
||||
hash := app.Hash()
|
||||
|
||||
app.logger.Info("Info synced",
|
||||
"height", app.CommittedHeight(),
|
||||
"hash", fmt.Sprintf("%X", hash))
|
||||
|
||||
return abci.ResponseInfo{
|
||||
Data: cmn.Fmt("size:%v", s.State.Size()),
|
||||
LastBlockHeight: s.height,
|
||||
LastBlockAppHash: s.Hash(),
|
||||
Data: app.Name,
|
||||
LastBlockHeight: app.CommittedHeight(),
|
||||
LastBlockAppHash: hash,
|
||||
}
|
||||
}
|
||||
|
||||
// Commit implements abci.Application
|
||||
func (s *Store) Commit() abci.Result {
|
||||
s.height++
|
||||
|
||||
hash, err := s.State.Commit(s.height)
|
||||
if err != nil {
|
||||
return abci.NewError(abci.CodeType_InternalError, err.Error())
|
||||
}
|
||||
s.logger.Debug("Commit synced",
|
||||
"height", s.height,
|
||||
"hash", fmt.Sprintf("%X", hash),
|
||||
)
|
||||
|
||||
if s.State.Size() == 0 {
|
||||
return abci.NewResultOK(nil, "Empty hash for empty tree")
|
||||
}
|
||||
return abci.NewResultOK(hash, "")
|
||||
// SetOption - ABCI
|
||||
func (app *StoreApp) SetOption(key string, value string) string {
|
||||
return "Not Implemented"
|
||||
}
|
||||
|
||||
// Query implements abci.Application
|
||||
func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
|
||||
// Query - ABCI
|
||||
func (app *StoreApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
|
||||
if len(reqQuery.Data) == 0 {
|
||||
resQuery.Log = "Query cannot be zero length"
|
||||
resQuery.Code = abci.CodeType_EncodingError
|
||||
return
|
||||
}
|
||||
|
||||
// set the query response height to current
|
||||
tree := s.State.Committed()
|
||||
tree := app.state.Committed()
|
||||
|
||||
height := reqQuery.Height
|
||||
if height == 0 {
|
||||
|
@ -135,10 +143,10 @@ func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery)
|
|||
// we must retrun most recent, even if apphash
|
||||
// is not yet in the blockchain
|
||||
|
||||
// if tree.Tree.VersionExists(s.height - 1) {
|
||||
// height = s.height - 1
|
||||
// if tree.Tree.VersionExists(app.height - 1) {
|
||||
// height = app.height - 1
|
||||
// } else {
|
||||
height = s.height
|
||||
height = app.CommittedHeight()
|
||||
// }
|
||||
}
|
||||
resQuery.Height = height
|
||||
|
@ -166,3 +174,98 @@ func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery)
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Commit implements abci.Application
|
||||
func (app *StoreApp) Commit() (res abci.Result) {
|
||||
app.height++
|
||||
|
||||
hash, err := app.state.Commit(app.height)
|
||||
if err != nil {
|
||||
// die if we can't commit, not to recover
|
||||
panic(err)
|
||||
}
|
||||
app.logger.Debug("Commit synced",
|
||||
"height", app.height,
|
||||
"hash", fmt.Sprintf("%X", hash),
|
||||
)
|
||||
|
||||
if app.state.Size() == 0 {
|
||||
return abci.NewResultOK(nil, "Empty hash for empty tree")
|
||||
}
|
||||
return abci.NewResultOK(hash, "")
|
||||
}
|
||||
|
||||
// InitChain - ABCI
|
||||
func (app *StoreApp) InitChain(req abci.RequestInitChain) {}
|
||||
|
||||
// BeginBlock - ABCI
|
||||
func (app *StoreApp) BeginBlock(req abci.RequestBeginBlock) {}
|
||||
|
||||
// EndBlock - ABCI
|
||||
// Returns a list of all validator changes made in this block
|
||||
func (app *StoreApp) EndBlock(height uint64) (res abci.ResponseEndBlock) {
|
||||
// TODO: cleanup in case a validator exists multiple times in the list
|
||||
res.Diffs = app.pending
|
||||
app.pending = nil
|
||||
return
|
||||
}
|
||||
|
||||
// AddValChange is meant to be called by apps on DeliverTx
|
||||
// results, this is added to the cache for the endblock
|
||||
// changeset
|
||||
func (app *StoreApp) AddValChange(diffs []*abci.Validator) {
|
||||
for _, d := range diffs {
|
||||
idx := pubKeyIndex(d, app.pending)
|
||||
if idx >= 0 {
|
||||
app.pending[idx] = d
|
||||
} else {
|
||||
app.pending = append(app.pending, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return index of list with validator of same PubKey, or -1 if no match
|
||||
func pubKeyIndex(val *abci.Validator, list []*abci.Validator) int {
|
||||
for i, v := range list {
|
||||
if bytes.Equal(val.PubKey, v.PubKey) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func loadState(dbName string, cacheSize int) (*sm.State, error) {
|
||||
// memory backed case, just for testing
|
||||
if dbName == "" {
|
||||
tree := iavl.NewVersionedTree(0, dbm.NewMemDB())
|
||||
return sm.NewState(tree), nil
|
||||
}
|
||||
|
||||
// Expand the path fully
|
||||
dbPath, err := filepath.Abs(dbName)
|
||||
if err != nil {
|
||||
return nil, errors.ErrInternal("Invalid Database Name")
|
||||
}
|
||||
|
||||
// Some external calls accidently add a ".db", which is now removed
|
||||
dbPath = strings.TrimSuffix(dbPath, path.Ext(dbPath))
|
||||
|
||||
// Split the database name into it's components (dir, name)
|
||||
dir := path.Dir(dbPath)
|
||||
name := path.Base(dbPath)
|
||||
|
||||
// Make sure the path exists
|
||||
empty, _ := cmn.IsDirEmpty(dbPath + ".db")
|
||||
|
||||
// Open database called "dir/name.db", if it doesn't exist it will be created
|
||||
db := dbm.NewDB(name, dbm.LevelDBBackendStr, dir)
|
||||
tree := iavl.NewVersionedTree(cacheSize, db)
|
||||
|
||||
if !empty {
|
||||
if err = tree.Load(); err != nil {
|
||||
return nil, errors.ErrInternal("Loading tree: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return sm.NewState(tree), nil
|
||||
}
|
||||
|
|
|
@ -22,19 +22,6 @@
|
|||
"amount": 222
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "carl",
|
||||
"address": "1234ABCDD18E8EFE3FFC4B0506BF9BF8E5B0D9E9",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "177C0AC45E86257F0708DC085D592AB22AAEECD1D26381B757F7C96135921858"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "three",
|
||||
"amount": 333
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "sam",
|
||||
"pub_key": {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"chain_id": "addr_accounts_chain",
|
||||
"app_options": {
|
||||
"accounts": [{
|
||||
"name": "alice",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "DBD9A46C45868F0A37C92B53113C09B048FBD87B5FBC2F8B199052973B8FAA36"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "one",
|
||||
"amount": 111
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "bob",
|
||||
"address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "two",
|
||||
"amount": 222
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "carl",
|
||||
"address": "1234ABCDD18E8EFE3FFC4B0506BF9BF8E5B0D9E9",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "177C0AC45E86257F0708DC085D592AB22AAEECD1D26381B757F7C96135921858"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "three",
|
||||
"amount": 333
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "sam",
|
||||
"pub_key": {
|
||||
"type": "secp256k1",
|
||||
"data": "02AA8342F63CCCCE6DDB128525BA048CE0B2993DA3B4308746E1F216361A87651E"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "four",
|
||||
"amount": 444
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
|
@ -39,9 +39,10 @@ func TestEndBlock(t *testing.T) {
|
|||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
store := MockStore()
|
||||
handler := base.ValSetHandler{}
|
||||
app := NewBasecoin(handler, store, logger)
|
||||
store, err := MockStoreApp("vals", logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
app := NewBaseApp(store, handler, nil)
|
||||
|
||||
val1 := makeVal()
|
||||
val2 := makeVal()
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/app"
|
||||
sdkapp "github.com/cosmos/cosmos-sdk/app"
|
||||
"github.com/cosmos/cosmos-sdk/modules/auth"
|
||||
"github.com/cosmos/cosmos-sdk/modules/base"
|
||||
"github.com/cosmos/cosmos-sdk/modules/coin"
|
||||
|
@ -21,7 +21,7 @@ import (
|
|||
)
|
||||
|
||||
type BenchApp struct {
|
||||
App *app.Basecoin
|
||||
App *sdkapp.BaseApp
|
||||
Accounts []*coin.AccountWithKey
|
||||
ChainID string
|
||||
}
|
||||
|
@ -53,27 +53,20 @@ func NewBenchApp(h sdk.Handler, chainID string, n int,
|
|||
// logger := log.NewFilter(log.NewTMLogger(os.Stdout), log.AllowError())
|
||||
// logger = log.NewTracingLogger(logger)
|
||||
|
||||
// TODO: disk writing
|
||||
var store *app.Store
|
||||
var err error
|
||||
|
||||
dbDir, cache := "", 0
|
||||
if persist {
|
||||
tmpDir, _ := ioutil.TempDir("", "bc-app-benchmark")
|
||||
store, err = app.NewStore(tmpDir, 500, logger)
|
||||
} else {
|
||||
store, err = app.NewStore("", 0, logger)
|
||||
dbDir, _ = ioutil.TempDir("", "bc-app-benchmark")
|
||||
cache = 500
|
||||
}
|
||||
|
||||
store, err := sdkapp.NewStoreApp("bench", dbDir, cache, logger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app := sdkapp.NewBaseApp(store, h, nil)
|
||||
|
||||
app := app.NewBasecoin(
|
||||
h,
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
res := app.InitState("base/chain_id", chainID)
|
||||
if res != "Success" {
|
||||
err = app.InitState("base", "chain_id", chainID)
|
||||
if err != nil {
|
||||
panic("cannot set chain")
|
||||
}
|
||||
|
||||
|
@ -82,8 +75,8 @@ func NewBenchApp(h sdk.Handler, chainID string, n int,
|
|||
accts := make([]*coin.AccountWithKey, n)
|
||||
for i := 0; i < n; i++ {
|
||||
accts[i] = coin.NewAccountWithKey(money)
|
||||
res := app.InitState("coin/account", accts[i].MakeOption())
|
||||
if res != "Success" {
|
||||
err = app.InitState("coin", "account", accts[i].MakeOption())
|
||||
if err != nil {
|
||||
panic("can't set account")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/app"
|
||||
sdkapp "github.com/cosmos/cosmos-sdk/app"
|
||||
"github.com/cosmos/cosmos-sdk/modules/eyes"
|
||||
)
|
||||
|
||||
|
@ -26,11 +26,12 @@ var node *nm.Node
|
|||
|
||||
func TestMain(m *testing.M) {
|
||||
logger := log.TestingLogger()
|
||||
store, err := app.NewStore("", 0, logger)
|
||||
store, err := sdkapp.MockStoreApp("query", logger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app := app.NewBasecoin(eyes.NewHandler(), store, logger)
|
||||
app := sdkapp.NewBaseApp(store, eyes.NewHandler(), nil)
|
||||
|
||||
node = rpctest.StartTendermint(app)
|
||||
|
||||
code := m.Run()
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/modules/auth"
|
||||
"github.com/cosmos/cosmos-sdk/modules/base"
|
||||
"github.com/cosmos/cosmos-sdk/modules/coin"
|
||||
"github.com/cosmos/cosmos-sdk/modules/eyes"
|
||||
"github.com/cosmos/cosmos-sdk/modules/fee"
|
||||
"github.com/cosmos/cosmos-sdk/modules/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/modules/nonce"
|
||||
|
@ -45,6 +46,8 @@ func BuildApp(feeDenom string) sdk.Handler {
|
|||
coin.NewHandler(),
|
||||
stack.WrapHandler(roles.NewHandler()),
|
||||
stack.WrapHandler(ibc.NewHandler()),
|
||||
// and just for run, add eyes as well
|
||||
stack.WrapHandler(eyes.NewHandler()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ test01initOption() {
|
|||
GENESIS_FILE=${SERVE_DIR}/genesis.json
|
||||
HEX="deadbeef1234deadbeef1234deadbeef1234aaaa"
|
||||
|
||||
${SERVER_EXE} init ${HEX} --home="$SERVE_DIR" -p=app1/key1/val1 -p='"app2/key2/{""name"": ""joe"", ""age"": ""100""}"' >/dev/null
|
||||
${SERVER_EXE} init ${HEX} --home="$SERVE_DIR" -p=eyes/key1/val1 -p='"eyes/key2/{""name"": ""joe"", ""age"": ""100""}"' >/dev/null
|
||||
if ! assertTrue "line=${LINENO}" $?; then return 1; fi
|
||||
|
||||
OPTION1KEY=$(cat ${GENESIS_FILE} | jq '.app_options.plugin_options[2]')
|
||||
|
@ -21,9 +21,9 @@ test01initOption() {
|
|||
OPTION2VAL=$(cat ${GENESIS_FILE} | jq '.app_options.plugin_options[5]')
|
||||
OPTION2VALEXPECTED=$(echo '{"name": "joe", "age": "100"}' | jq '.')
|
||||
|
||||
assertEquals "line=${LINENO}" '"app1/key1"' $OPTION1KEY
|
||||
assertEquals "line=${LINENO}" '"eyes/key1"' $OPTION1KEY
|
||||
assertEquals "line=${LINENO}" '"val1"' $OPTION1VAL
|
||||
assertEquals "line=${LINENO}" '"app2/key2"' $OPTION2KEY
|
||||
assertEquals "line=${LINENO}" '"eyes/key2"' $OPTION2KEY
|
||||
assertEquals "line=${LINENO}" "$OPTION2VALEXPECTED" "$OPTION2VAL"
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ test01GetInsecure() {
|
|||
INFO=$(${CLIENT_EXE} rpc info)
|
||||
assertTrue "line=${LINENO}, get info" "$?"
|
||||
DATA=$(echo $INFO | jq .response.data)
|
||||
assertEquals "line=${LINENO}, basecoin info" '"Basecoin v0.7.1"' "$DATA"
|
||||
assertEquals "line=${LINENO}, basecoin info" '"basecoin v0.7.1"' "$DATA"
|
||||
}
|
||||
|
||||
test02GetSecure() {
|
||||
|
|
|
@ -27,22 +27,18 @@ func TestCounterPlugin(t *testing.T) {
|
|||
logger := log.TestingLogger()
|
||||
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
|
||||
|
||||
store, err := app.NewStore("", 0, logger.With("module", "store"))
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
h := NewHandler("gold")
|
||||
bcApp := app.NewBasecoin(
|
||||
h,
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
bcApp.InitState("base/chain_id", chainID)
|
||||
store, err := app.MockStoreApp("counter", logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
bcApp := app.NewBaseApp(store, h, nil)
|
||||
err = bcApp.InitState("base", "chain_id", chainID)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// Account initialization
|
||||
bal := coin.Coins{{"", 1000}, {"gold", 1000}}
|
||||
acct := coin.NewAccountWithKey(bal)
|
||||
log := bcApp.InitState("coin/account", acct.MakeOption())
|
||||
require.Equal("Success", log)
|
||||
err = bcApp.InitState("coin", "account", acct.MakeOption())
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// Deliver a CounterTx
|
||||
DeliverCounterTx := func(valid bool, counterFee coin.Coins, sequence uint32) abci.Result {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Package genesis provides some utility functions for parsing
|
||||
a standard genesis file to initialize your abci application.
|
||||
|
||||
We wish to support using one genesis file to initialize both
|
||||
tendermint and the application, so this file format is designed
|
||||
to be embedable in the tendermint genesis.json file. We reuse
|
||||
the same chain_id field for tendermint, ignore the other fields,
|
||||
and add a special app_options field that contains information just
|
||||
for the abci app (and ignored by tendermint).
|
||||
|
||||
The use of this file format for your application is not required by
|
||||
the sdk and is only used by default in the start command, if you wish
|
||||
to write your own start command, you can use any other method to
|
||||
store and parse options for your abci application. The important part is
|
||||
that the same data is available on every node.
|
||||
|
||||
Example file format:
|
||||
|
||||
{
|
||||
"chain_id": "foo_bar_chain",
|
||||
"app_options": {
|
||||
"accounts": [{
|
||||
"address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1",
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "6880DB93598E283A67C4D88FC67A8858AA2DE70F713FE94A5109E29C137100C2"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "ETH",
|
||||
"amount": 654321
|
||||
}
|
||||
]
|
||||
}],
|
||||
"plugin_options": [
|
||||
"plugin1/key1", "value1",
|
||||
"profile/set", {"name": "john", age: 37}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Note that there are two subfields under app_options. The first one "accounts"
|
||||
is a special case for the coin module, which is assumed to be used by most
|
||||
applications. It is simply a list of accounts with an identifier and their
|
||||
initial balance. The account must be identified by EITHER an address
|
||||
(20 bytes in hex) or a pubkey (in the go-crypto json format), not both as in
|
||||
this example. "coins" defines the initial balance of the account.
|
||||
|
||||
Configuration options for every other module should be placed under
|
||||
"plugin_options" as key value pairs (there must be an even number of items).
|
||||
The first value must be "<module>/<key>" to define the option to be set.
|
||||
The second value is parsed as raw json and is the value to pass to the
|
||||
application. This may be a string, an array, a map or any other valid json
|
||||
structure that the module can parse.
|
||||
|
||||
Note that we don't use a map for plugin_options, as we will often wish
|
||||
to have many values for the same key, to run this setup many times,
|
||||
just as we support setting many accounts.
|
||||
*/
|
||||
package genesis
|
|
@ -0,0 +1,153 @@
|
|||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
// Option just holds module/key/value triples from
|
||||
// parsing the genesis file
|
||||
type Option struct {
|
||||
Module string
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// InitStater is anything that can handle app options
|
||||
// from genesis file. Setting the merkle store, config options,
|
||||
// or anything else
|
||||
type InitStater interface {
|
||||
InitState(module, key, value string) error
|
||||
}
|
||||
|
||||
// Load parses the genesis file and sets the initial
|
||||
// state based on that
|
||||
func Load(app InitStater, filePath string) error {
|
||||
opts, err := GetOptions(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// execute all the genesis init options
|
||||
// abort on any error
|
||||
for _, opt := range opts {
|
||||
err = app.InitState(opt.Module, opt.Key, opt.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOptions parses the genesis file in a format
|
||||
// that can easily be handed into InitStaters
|
||||
func GetOptions(path string) ([]Option, error) {
|
||||
genDoc, err := load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := genDoc.AppOptions
|
||||
cnt := 1 + len(opts.Accounts) + len(opts.pluginOptions)
|
||||
res := make([]Option, cnt)
|
||||
|
||||
res[0] = Option{sdk.ModuleNameBase, sdk.ChainKey, genDoc.ChainID}
|
||||
i := 1
|
||||
|
||||
// set accounts
|
||||
for _, acct := range opts.Accounts {
|
||||
res[i] = Option{"coin", "account", string(acct)}
|
||||
i++
|
||||
}
|
||||
|
||||
// set plugin options
|
||||
for _, kv := range opts.pluginOptions {
|
||||
module, key := splitKey(kv.Key)
|
||||
res[i] = Option{module, key, kv.Value}
|
||||
i++
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type keyValue struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// FullDoc - includes tendermint (in the json, we ignore here)
|
||||
type FullDoc struct {
|
||||
ChainID string `json:"chain_id"`
|
||||
AppOptions *Doc `json:"app_options"`
|
||||
}
|
||||
|
||||
// Doc - All genesis values
|
||||
type Doc struct {
|
||||
Accounts []json.RawMessage `json:"accounts"`
|
||||
PluginOptions []json.RawMessage `json:"plugin_options"`
|
||||
|
||||
pluginOptions []keyValue // unmarshaled rawmessages
|
||||
}
|
||||
|
||||
func load(filePath string) (*FullDoc, error) {
|
||||
bytes, err := cmn.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "loading genesis file")
|
||||
}
|
||||
|
||||
// the basecoin genesis go-wire/data :)
|
||||
genDoc := new(FullDoc)
|
||||
err = json.Unmarshal(bytes, genDoc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshaling genesis file")
|
||||
}
|
||||
|
||||
if genDoc.AppOptions == nil {
|
||||
genDoc.AppOptions = new(Doc)
|
||||
}
|
||||
|
||||
pluginOpts, err := parseList(genDoc.AppOptions.PluginOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
genDoc.AppOptions.pluginOptions = pluginOpts
|
||||
return genDoc, nil
|
||||
}
|
||||
|
||||
func parseList(kvzIn []json.RawMessage) (kvz []keyValue, err error) {
|
||||
if len(kvzIn)%2 != 0 {
|
||||
return nil, errors.New("genesis cannot have an odd number of items. Format = [key1, value1, key2, value2, ...]")
|
||||
}
|
||||
|
||||
for i := 0; i < len(kvzIn); i += 2 {
|
||||
kv := keyValue{}
|
||||
rawK := []byte(kvzIn[i])
|
||||
err := json.Unmarshal(rawK, &(kv.Key))
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Non-string key: %s", string(rawK))
|
||||
}
|
||||
// convert value to string if possible (otherwise raw json)
|
||||
rawV := kvzIn[i+1]
|
||||
err = json.Unmarshal(rawV, &(kv.Value))
|
||||
if err != nil {
|
||||
kv.Value = string(rawV)
|
||||
}
|
||||
kvz = append(kvz, kv)
|
||||
}
|
||||
return kvz, nil
|
||||
}
|
||||
|
||||
// Splits the string at the first '/'.
|
||||
// if there are none, assign default module ("base").
|
||||
func splitKey(key string) (string, string) {
|
||||
if strings.Contains(key, "/") {
|
||||
keyParts := strings.SplitN(key, "/", 2)
|
||||
return keyParts[0], keyParts[1]
|
||||
}
|
||||
return sdk.ModuleNameBase, key
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
const genesisFilepath = "./testdata/genesis.json"
|
||||
|
||||
func TestParseList(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
bytes, err := cmn.ReadFile(genesisFilepath)
|
||||
require.Nil(err, "loading genesis file %+v", err)
|
||||
|
||||
// the basecoin genesis go-wire/data :)
|
||||
genDoc := new(FullDoc)
|
||||
err = json.Unmarshal(bytes, genDoc)
|
||||
require.Nil(err, "unmarshaling genesis file %+v", err)
|
||||
|
||||
pluginOpts, err := parseList(genDoc.AppOptions.PluginOptions)
|
||||
require.Nil(err, "%+v", err)
|
||||
genDoc.AppOptions.pluginOptions = pluginOpts
|
||||
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[0].Key, "plugin1/key1")
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[1].Key, "plugin1/key2")
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[0].Value, "value1")
|
||||
assert.Equal(genDoc.AppOptions.pluginOptions[1].Value, "value2")
|
||||
}
|
||||
|
||||
func TestGetOptions(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
opts, err := GetOptions(genesisFilepath)
|
||||
require.Nil(err, "loading genesis file %+v", err)
|
||||
|
||||
require.Equal(4, len(opts))
|
||||
chain := opts[0]
|
||||
assert.Equal(sdk.ModuleNameBase, chain.Module)
|
||||
assert.Equal(sdk.ChainKey, chain.Key)
|
||||
assert.Equal("foo_bar_chain", chain.Value)
|
||||
|
||||
acct := opts[1]
|
||||
assert.Equal("coin", acct.Module)
|
||||
assert.Equal("account", acct.Key)
|
||||
|
||||
p1 := opts[2]
|
||||
assert.Equal("plugin1", p1.Module)
|
||||
assert.Equal("key1", p1.Key)
|
||||
assert.Equal("value1", p1.Value)
|
||||
|
||||
p2 := opts[3]
|
||||
assert.Equal("plugin1", p2.Module)
|
||||
assert.Equal("key2", p2.Key)
|
||||
assert.Equal("value2", p2.Value)
|
||||
}
|
||||
|
||||
func TestSplitKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
prefix, suffix := splitKey("foo/bar")
|
||||
assert.EqualValues("foo", prefix)
|
||||
assert.EqualValues("bar", suffix)
|
||||
|
||||
prefix, suffix = splitKey("foobar")
|
||||
assert.EqualValues("base", prefix)
|
||||
assert.EqualValues("foobar", suffix)
|
||||
|
||||
prefix, suffix = splitKey("some/complex/issue")
|
||||
assert.EqualValues("some", prefix)
|
||||
assert.EqualValues("complex/issue", suffix)
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"chain_id": "foo_bar_chain",
|
||||
"app_options": {
|
||||
"accounts": [{
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "6880db93598e283a67c4d88fc67a8858aa2de70f713fe94a5109e29c137100c2"
|
||||
},
|
||||
"coins": [
|
||||
{
|
||||
"denom": "blank",
|
||||
"amount": 12345
|
||||
},
|
||||
{
|
||||
"denom": "ETH",
|
||||
"amount": 654321
|
||||
}
|
||||
]
|
||||
}],
|
||||
"plugin_options": ["plugin1/key1", "value1", "plugin1/key2", "value2"]
|
||||
}
|
||||
}
|
19
handler.go
19
handler.go
|
@ -8,6 +8,13 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/state"
|
||||
)
|
||||
|
||||
const (
|
||||
// ModuleNameBase is the module name for internal functionality
|
||||
ModuleNameBase = "base"
|
||||
// ChainKey is the option key for setting the chain id
|
||||
ChainKey = "chain_id"
|
||||
)
|
||||
|
||||
// Handler is anything that processes a transaction
|
||||
type Handler interface {
|
||||
// Checker verifies there are valid fees and estimates work
|
||||
|
@ -25,6 +32,18 @@ type Handler interface {
|
|||
// BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header)
|
||||
}
|
||||
|
||||
// Ticker can be executed every block
|
||||
type Ticker interface {
|
||||
Tick(Context, state.SimpleDB) ([]*abci.Validator, error)
|
||||
}
|
||||
|
||||
// TickerFunc allows a function to implement the interface
|
||||
type TickerFunc func(Context, state.SimpleDB) ([]*abci.Validator, error)
|
||||
|
||||
func (t TickerFunc) Tick(ctx Context, store state.SimpleDB) ([]*abci.Validator, error) {
|
||||
return t(ctx, store)
|
||||
}
|
||||
|
||||
// Named ensures there is a name for the item
|
||||
type Named interface {
|
||||
Name() string
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package eyes
|
||||
|
||||
import (
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/errors"
|
||||
"github.com/cosmos/cosmos-sdk/state"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -19,7 +21,6 @@ const (
|
|||
|
||||
// Handler allows us to set and remove data
|
||||
type Handler struct {
|
||||
sdk.NopInitState
|
||||
sdk.NopInitValidate
|
||||
}
|
||||
|
||||
|
@ -35,6 +36,16 @@ func (Handler) Name() string {
|
|||
return Name
|
||||
}
|
||||
|
||||
// InitState - sets the genesis state
|
||||
func (h Handler) InitState(l log.Logger, store state.SimpleDB,
|
||||
module, key, value string) (log string, err error) {
|
||||
if module != Name {
|
||||
return "", errors.ErrUnknownModule(module)
|
||||
}
|
||||
store.Set([]byte(key), []byte(value))
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// CheckTx verifies if the transaction is properly formated
|
||||
func (h Handler) CheckTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx) (res sdk.CheckResult, err error) {
|
||||
err = tx.ValidateBasic()
|
||||
|
|
|
@ -21,6 +21,8 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk"
|
||||
"github.com/cosmos/cosmos-sdk/app"
|
||||
"github.com/cosmos/cosmos-sdk/genesis"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
)
|
||||
|
||||
// StartCmd - command to start running the abci app (and tendermint)!
|
||||
|
@ -31,7 +33,7 @@ var StartCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
// GetTickStartCmd - initialize a command as the start command with tick
|
||||
func GetTickStartCmd(tick app.Ticker) *cobra.Command {
|
||||
func GetTickStartCmd(tick sdk.Ticker) *cobra.Command {
|
||||
startCmd := &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start this full node",
|
||||
|
@ -70,42 +72,47 @@ func addStartFlag(startCmd *cobra.Command) {
|
|||
}
|
||||
|
||||
//returns the start command which uses the tick
|
||||
func tickStartCmd(tick app.Ticker) func(cmd *cobra.Command, args []string) error {
|
||||
func tickStartCmd(clock sdk.Ticker) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
|
||||
store, err := app.NewStore(
|
||||
cmdName := cmd.Root().Name()
|
||||
appName := fmt.Sprintf("%s v%v", cmdName, version.Version)
|
||||
storeApp, err := app.NewStoreApp(
|
||||
appName,
|
||||
path.Join(rootDir, "data", "merkleeyes.db"),
|
||||
EyesCacheSize,
|
||||
logger.With("module", "store"),
|
||||
)
|
||||
logger.With("module", "app"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create Basecoin app
|
||||
basecoinApp := app.NewBasecoinTick(Handler, store, logger.With("module", "app"), tick)
|
||||
return start(rootDir, store, basecoinApp)
|
||||
basecoinApp := app.NewBaseApp(storeApp, Handler, clock)
|
||||
return start(rootDir, basecoinApp)
|
||||
}
|
||||
}
|
||||
|
||||
func startCmd(cmd *cobra.Command, args []string) error {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
|
||||
store, err := app.NewStore(
|
||||
cmdName := cmd.Root().Name()
|
||||
appName := fmt.Sprintf("%s v%v", cmdName, version.Version)
|
||||
storeApp, err := app.NewStoreApp(
|
||||
appName,
|
||||
path.Join(rootDir, "data", "merkleeyes.db"),
|
||||
EyesCacheSize,
|
||||
logger.With("module", "store"),
|
||||
)
|
||||
logger.With("module", "app"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create Basecoin app
|
||||
basecoinApp := app.NewBasecoin(Handler, store, logger.With("module", "app"))
|
||||
return start(rootDir, store, basecoinApp)
|
||||
basecoinApp := app.NewBaseApp(storeApp, Handler, nil)
|
||||
return start(rootDir, basecoinApp)
|
||||
}
|
||||
|
||||
func start(rootDir string, store *app.Store, basecoinApp *app.Basecoin) error {
|
||||
func start(rootDir string, basecoinApp *app.BaseApp) error {
|
||||
|
||||
// if chain_id has not been set yet, load the genesis.
|
||||
// else, assume it's been loaded
|
||||
|
@ -113,7 +120,7 @@ func start(rootDir string, store *app.Store, basecoinApp *app.Basecoin) error {
|
|||
// If genesis file exists, set key-value options
|
||||
genesisFile := path.Join(rootDir, "genesis.json")
|
||||
if _, err := os.Stat(genesisFile); err == nil {
|
||||
err := basecoinApp.LoadGenesis(genesisFile)
|
||||
err = genesis.Load(basecoinApp, genesisFile)
|
||||
if err != nil {
|
||||
return errors.Errorf("Error in LoadGenesis: %v\n", err)
|
||||
}
|
||||
|
|
|
@ -8,16 +8,14 @@ type State struct {
|
|||
committed *Bonsai
|
||||
deliverTx SimpleDB
|
||||
checkTx SimpleDB
|
||||
persistent bool
|
||||
}
|
||||
|
||||
func NewState(tree *iavl.VersionedTree) State {
|
||||
func NewState(tree *iavl.VersionedTree) *State {
|
||||
base := NewBonsai(tree)
|
||||
return State{
|
||||
return &State{
|
||||
committed: base,
|
||||
deliverTx: base.Checkpoint(),
|
||||
checkTx: base.Checkpoint(),
|
||||
persistent: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,14 +43,6 @@ func (s State) LatestHash() []byte {
|
|||
return s.committed.Tree.Hash()
|
||||
}
|
||||
|
||||
// BatchSet is used for some weird magic in storing the new height
|
||||
func (s *State) BatchSet(key, value []byte) {
|
||||
if s.persistent {
|
||||
// This is in the batch with the Save, but not in the tree
|
||||
s.committed.Tree.BatchSet(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Commit save persistent nodes to the database and re-copies the trees
|
||||
func (s *State) Commit(version uint64) ([]byte, error) {
|
||||
// commit (if we didn't do hash earlier)
|
||||
|
@ -62,16 +52,12 @@ func (s *State) Commit(version uint64) ([]byte, error) {
|
|||
}
|
||||
|
||||
var hash []byte
|
||||
if s.persistent {
|
||||
if s.committed.Tree.Size() > 0 || s.committed.Tree.LatestVersion() > 0 {
|
||||
hash, err = s.committed.Tree.SaveVersion(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hash = s.committed.Tree.Hash()
|
||||
}
|
||||
|
||||
s.deliverTx = s.committed.Checkpoint()
|
||||
s.checkTx = s.committed.Checkpoint()
|
||||
|
|
Loading…
Reference in New Issue