diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e92f58bdcc..f455b31843 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -1,13 +1,12 @@ package baseapp import ( - "bytes" "fmt" - "os" "runtime/debug" "github.com/golang/protobuf/proto" "github.com/pkg/errors" + abci "github.com/tendermint/abci/types" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -21,76 +20,39 @@ var mainHeaderKey = []byte("header") // The ABCI application type BaseApp struct { - logger log.Logger - - // Application name from abci.Info - name string - - // Common DB backend - db dbm.DB - - // Main (uncached) state - cms sdk.CommitMultiStore - - // unmarshal []byte into sdk.Tx - txDecoder sdk.TxDecoder - - // unmarshal rawjsonbytes to initialize the application - // TODO unexpose and call from InitChain - InitStater sdk.InitStater - - // ante handler for fee and auth - defaultAnteHandler sdk.AnteHandler - - // handle any kind of message - router Router + logger log.Logger + name string // application name from abci.Info + db dbm.DB // common DB backend + cms sdk.CommitMultiStore // Main (uncached) state + txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx + initChainer sdk.InitChainer // + anteHandler sdk.AnteHandler // ante handler for fee and auth + router Router // handle any kind of message //-------------------- // Volatile + // .msCheck and .header are set on initialization. + // .msDeliver is only set (and reset) in BeginBlock. + // .header and .valUpdates are also reset in BeginBlock. + // .msCheck is only reset in Commit. - // CheckTx state, a cache-wrap of `.cms` - msCheck sdk.CacheMultiStore - - // DeliverTx state, a cache-wrap of `.cms` - msDeliver sdk.CacheMultiStore - - // current block header - header *abci.Header - - // cached validator changes from DeliverTx - valUpdates []abci.Validator + header abci.Header // current block header + msCheck sdk.CacheMultiStore // CheckTx state, a cache-wrap of `.cms` + msDeliver sdk.CacheMultiStore // DeliverTx state, a cache-wrap of `.cms` + valUpdates []abci.Validator // cached validator changes from DeliverTx } var _ abci.Application = &BaseApp{} // Create and name new BaseApp -func NewBaseApp(name string) *BaseApp { - var baseapp = &BaseApp{ - logger: makeDefaultLogger(), +func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp { + return &BaseApp{ + logger: logger, name: name, - db: nil, - cms: nil, + db: db, + cms: store.NewCommitMultiStore(db), router: NewRouter(), } - baseapp.initDB() - baseapp.initMultiStore() - return baseapp -} - -// Create the underlying leveldb datastore which will -// persist the Merkle tree inner & leaf nodes. -func (app *BaseApp) initDB() { - db, err := dbm.NewGoLevelDB(app.name, "data") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - app.db = db -} - -func (app *BaseApp) initMultiStore() { - cms := store.NewCommitMultiStore(app.db) - app.cms = cms } // BaseApp Name @@ -98,71 +60,79 @@ func (app *BaseApp) Name() string { return app.name } +// Mount a store to the provided key in the BaseApp multistore +func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) { + for _, key := range keys { + app.MountStore(key, sdk.StoreTypeIAVL) + } +} + // Mount a store to the provided key in the BaseApp multistore func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { app.cms.MountStoreWithDB(key, typ, app.db) } -// nolint +// nolint - Set functions func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) { app.txDecoder = txDecoder } -func (app *BaseApp) SetInitStater(initStater sdk.InitStater) { - app.InitStater = initStater +func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) { + app.initChainer = initChainer } -func (app *BaseApp) SetDefaultAnteHandler(ah sdk.AnteHandler) { - app.defaultAnteHandler = ah -} -func (app *BaseApp) Router() Router { - return app.router +func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { + // deducts fee from payer, verifies signatures and nonces, sets Signers to ctx. + app.anteHandler = ah } +// nolint - Get functions +func (app *BaseApp) Router() Router { return app.router } + /* TODO consider: func (app *BaseApp) SetBeginBlocker(...) {} func (app *BaseApp) SetEndBlocker(...) {} */ -// TODO add description +// load latest application version func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error { app.cms.LoadLatestVersion() return app.initFromStore(mainKey) } -// Load application version +// load application version func (app *BaseApp) LoadVersion(version int64, mainKey sdk.StoreKey) error { app.cms.LoadVersion(version) return app.initFromStore(mainKey) } -// The last CommitID of the multistore. +// the last CommitID of the multistore func (app *BaseApp) LastCommitID() sdk.CommitID { return app.cms.LastCommitID() } -// The last commited block height. +// the last commited block height func (app *BaseApp) LastBlockHeight() int64 { return app.cms.LastCommitID().Version } -// Initializes the remaining logic from app.cms. +// initializes the remaining logic from app.cms func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { var lastCommitID = app.cms.LastCommitID() var main = app.cms.GetKVStore(mainKey) - var header *abci.Header + var header abci.Header - // Main store should exist. + // main store should exist. if main == nil { return errors.New("BaseApp expects MultiStore with 'main' KVStore") } - // If we've committed before, we expect main://. + // if we've committed before, we expect main:// if !lastCommitID.IsZero() { headerBytes := main.Get(mainHeaderKey) if len(headerBytes) == 0 { errStr := fmt.Sprintf("Version > 0 but missing key %s", mainHeaderKey) return errors.New(errStr) } - err := proto.Unmarshal(headerBytes, header) + err := proto.Unmarshal(headerBytes, &header) if err != nil { return errors.Wrap(err, "Failed to parse Header") } @@ -173,16 +143,27 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { } } - // Set BaseApp state. + // set BaseApp state app.header = header - app.msCheck = nil + app.msCheck = app.cms.CacheMultiStore() app.msDeliver = nil app.valUpdates = nil return nil } +// NewContext returns a new Context suitable for AnteHandler and Handler processing. +// NOTE: header is empty for checkTx +// NOTE: txBytes may be nil, for instance in tests (using app.Check or app.Deliver directly). +func (app *BaseApp) NewContext(isCheckTx bool, txBytes []byte) sdk.Context { + store := app.getMultiStore(isCheckTx) + // XXX CheckTx can't safely get the header + header := abci.Header{} + return sdk.NewContext(store, header, isCheckTx, txBytes) +} + //---------------------------------------- +// ABCI // Implements ABCI func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { @@ -203,9 +184,27 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp } // Implements ABCI +// InitChain runs the initialization logic directly on the CommitMultiStore and commits it. func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { - // TODO: Use req.Validators - // TODO: Use req.AppStateJSON (?) + if app.initChainer == nil { + // TODO: should we have some default handling of validators? + return + } + + // make a context for the initialization. + // NOTE: we're writing to the cms directly, without a CacheWrap + ctx := sdk.NewContext(app.cms, abci.Header{}, false, nil) + + err := app.initChainer(ctx, req) + if err != nil { + // TODO: something better https://github.com/cosmos/cosmos-sdk/issues/468 + cmn.Exit(fmt.Sprintf("error initializing application genesis state: %v", err)) + } + + // XXX this commits everything and bumps the version. + // https://github.com/cosmos/cosmos-sdk/issues/442#issuecomment-366470148 + app.cms.Commit() + return } @@ -223,9 +222,8 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { // Implements ABCI func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { // NOTE: For consistency we should unset these upon EndBlock. - app.header = &req.Header + app.header = req.Header app.msDeliver = app.cms.CacheMultiStore() - app.msCheck = app.cms.CacheMultiStore() app.valUpdates = nil return } @@ -253,7 +251,6 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { }, Tags: result.Tags, } - } // Implements ABCI @@ -288,9 +285,16 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { } } -// txBytes may be nil in some cases, for example, when tx is -// coming from TestApp. Also, in the future we may support -// "internal" transactions. +// Mostly for testing +func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { + return app.runTx(true, nil, tx) +} +func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { + return app.runTx(false, nil, tx) +} + +// txBytes may be nil in some cases, eg. in tests. +// Also, in the future we may support "internal" transactions. func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // Handle any panics. @@ -319,7 +323,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // TODO: override default ante handler w/ custom ante handler. // Run the ante handler. - newCtx, result, abort := app.defaultAnteHandler(ctx, tx) + newCtx, result, abort := app.anteHandler(ctx, tx) if isCheckTx || abort { return result } @@ -328,7 +332,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk } // CacheWrap app.msDeliver in case it fails. - msCache := app.getMultiStore(isCheckTx).CacheMultiStore() + msCache := app.getMultiStore(false).CacheMultiStore() ctx = ctx.WithMultiStore(msCache) // Match and run route. @@ -348,9 +352,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { res.ValidatorUpdates = app.valUpdates app.valUpdates = nil - app.header = nil app.msDeliver = nil - app.msCheck = nil return } @@ -367,7 +369,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { } //---------------------------------------- -// Misc. +// Helpers func (app *BaseApp) getMultiStore(isCheckTx bool) sdk.MultiStore { if isCheckTx { @@ -375,20 +377,3 @@ func (app *BaseApp) getMultiStore(isCheckTx bool) sdk.MultiStore { } return app.msDeliver } - -// 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 -} - -// Make a simple default logger -// TODO: Make log capturable for each transaction, and return it in -// ResponseDeliverTx.Log and ResponseCheckTx.Log. -func makeDefaultLogger() log.Logger { - return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") -} diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 7ceaacd203..fb136904cc 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "os" "testing" "github.com/stretchr/testify/assert" @@ -12,10 +13,83 @@ import ( "github.com/tendermint/go-crypto" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" sdk "github.com/cosmos/cosmos-sdk/types" ) +func defaultLogger() log.Logger { + return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") +} + +func newBaseApp(name string) *BaseApp { + logger := defaultLogger() + db := dbm.NewMemDB() + return NewBaseApp(name, logger, db) +} + +func TestMountStores(t *testing.T) { + name := t.Name() + app := newBaseApp(name) + assert.Equal(t, name, app.Name()) + + // make some cap keys + capKey1 := sdk.NewKVStoreKey("key1") + capKey2 := sdk.NewKVStoreKey("key2") + + // no stores are mounted + assert.Panics(t, func() { app.LoadLatestVersion(capKey1) }) + + app.MountStoresIAVL(capKey1, capKey2) + + // both stores are mounted + err := app.LoadLatestVersion(capKey1) + assert.Nil(t, err) + err = app.LoadLatestVersion(capKey2) + assert.Nil(t, err) +} + +func TestLoadVersion(t *testing.T) { + // TODO +} + +func TestInitChainer(t *testing.T) { + app := newBaseApp(t.Name()) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + key, value := []byte("hello"), []byte("goodbye") + + // initChainer sets a value in the store + var initChainer sdk.InitChainer = func(ctx sdk.Context, req abci.RequestInitChain) sdk.Error { + store := ctx.KVStore(capKey) + store.Set(key, value) + return nil + } + + query := abci.RequestQuery{ + Path: "/main/key", + Data: key, + } + + // initChainer is nil - nothing happens + app.InitChain(abci.RequestInitChain{}) + res := app.Query(query) + assert.Equal(t, 0, len(res.Value)) + + // set initChainer and try again - should see the value + app.SetInitChainer(initChainer) + app.InitChain(abci.RequestInitChain{}) + res = app.Query(query) + assert.Equal(t, value, res.Value) +} + +//---------------------- + // A mock transaction to update a validator's voting power. type testUpdatePowerTx struct { Addr []byte @@ -33,10 +107,10 @@ func (tx testUpdatePowerTx) GetSigners() []crypto.Address { return ni func (tx testUpdatePowerTx) GetFeePayer() crypto.Address { return nil } func (tx testUpdatePowerTx) GetSignatures() []sdk.StdSignature { return nil } -func TestBasic(t *testing.T) { +func TestExecution(t *testing.T) { // Create app. - app := NewBaseApp(t.Name()) + app := newBaseApp(t.Name()) storeKeys := createMounts(app.cms) app.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) { var ttx testUpdatePowerTx @@ -44,7 +118,7 @@ func TestBasic(t *testing.T) { return ttx, nil }) - app.SetDefaultAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return }) + app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return }) app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { // TODO return sdk.Result{} diff --git a/baseapp/context.go b/baseapp/context.go deleted file mode 100644 index e5a60f9674..0000000000 --- a/baseapp/context.go +++ /dev/null @@ -1,33 +0,0 @@ -package baseapp - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// NewContext returns a new Context suitable for AnteHandler (and indirectly Handler) processing. -// NOTE: txBytes may be nil to support TestApp.RunCheckTx -// and TestApp.RunDeliverTx. -func (app *BaseApp) NewContext(isCheckTx bool, txBytes []byte) sdk.Context { - var store sdk.MultiStore - if isCheckTx { - store = app.msCheck - } else { - store = app.msDeliver - } - - if store == nil { - panic("BaseApp.NewContext() requires BeginBlock(): missing store") - } - if app.header == nil { - panic("BaseApp.NewContext() requires BeginBlock(): missing header") - } - - // Initialize arguments to Handler. - var ctx = sdk.NewContext( - store, - *app.header, - isCheckTx, - txBytes, - ) - return ctx -} diff --git a/baseapp/genesis.go b/baseapp/genesis.go deleted file mode 100644 index b2dad3e334..0000000000 --- a/baseapp/genesis.go +++ /dev/null @@ -1,32 +0,0 @@ -package baseapp - -import ( - "encoding/json" - "io/ioutil" -) - -// TODO: remove from here and pass the AppState -// through InitChain - -// GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. -type GenesisDoc struct { - AppState json.RawMessage `json:"app_state,omitempty"` -} - -// GenesisDocFromFile reads JSON data from a file and unmarshalls it into a GenesisDoc. -func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) { - if genDocFile == "" { - var g GenesisDoc - return &g, nil - } - jsonBlob, err := ioutil.ReadFile(genDocFile) - if err != nil { - return nil, err - } - genDoc := GenesisDoc{} - err = json.Unmarshal(jsonBlob, &genDoc) - if err != nil { - return nil, err - } - return &genDoc, nil -} diff --git a/baseapp/helpers.go b/baseapp/helpers.go new file mode 100644 index 0000000000..43bd654d67 --- /dev/null +++ b/baseapp/helpers.go @@ -0,0 +1,24 @@ +package baseapp + +import ( + "github.com/tendermint/abci/server" + abci "github.com/tendermint/abci/types" + cmn "github.com/tendermint/tmlibs/common" +) + +// RunForever - BasecoinApp execution and cleanup +func RunForever(app abci.Application) { + + // Start the ABCI server + srv, err := server.NewServer("0.0.0.0:46658", "socket", app) + if err != nil { + cmn.Exit(err.Error()) + } + srv.Start() + + // Wait forever + cmn.TrapSignal(func() { + // Cleanup + srv.Stop() + }) +} diff --git a/baseapp/router.go b/baseapp/router.go index 2f27d7a544..b6b56792bd 100644 --- a/baseapp/router.go +++ b/baseapp/router.go @@ -6,12 +6,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Router - TODO add description +// Router provides handlers for each transaction type. type Router interface { AddRoute(r string, h sdk.Handler) Route(path string) (h sdk.Handler) } +// map a transaction type to a handler type route struct { r string h sdk.Handler diff --git a/baseapp/testapp.go b/baseapp/testapp.go deleted file mode 100644 index 389593d65a..0000000000 --- a/baseapp/testapp.go +++ /dev/null @@ -1,116 +0,0 @@ -package baseapp - -import ( - abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// TestApp wraps BaseApp with helper methods, -// and exposes more functionality than otherwise needed. -type TestApp struct { - *BaseApp - - // These get set as we receive them. - *abci.ResponseBeginBlock - *abci.ResponseEndBlock -} - -func NewTestApp(bapp *BaseApp) *TestApp { - app := &TestApp{ - BaseApp: bapp, - } - return app -} - -// execute BaseApp BeginBlock -func (tapp *TestApp) RunBeginBlock() { - if tapp.header != nil { - panic("TestApp.header not nil, BeginBlock already run, or EndBlock not yet run.") - } - cms := tapp.CommitMultiStore() - lastCommit := cms.LastCommitID() - header := abci.Header{ - ChainID: "chain_" + tapp.BaseApp.name, - Height: lastCommit.Version + 1, - Time: -1, // TODO - NumTxs: -1, // TODO - LastCommitHash: lastCommit.Hash, - DataHash: nil, // TODO - ValidatorsHash: nil, // TODO - AppHash: nil, // TODO - } - res := tapp.BeginBlock(abci.RequestBeginBlock{ - Hash: nil, // TODO - Header: header, - AbsentValidators: nil, // TODO - ByzantineValidators: nil, // TODO - }) - tapp.ResponseBeginBlock = &res - return -} - -func (tapp *TestApp) ensureBeginBlock() { - if tapp.header == nil { - panic("TestApp.header was nil, call TestApp.RunBeginBlock()") - } -} - -// run tx through CheckTx of TestApp -func (tapp *TestApp) RunCheckTx(tx sdk.Tx) sdk.Result { - tapp.ensureBeginBlock() - return tapp.BaseApp.runTx(true, nil, tx) -} - -// run tx through DeliverTx of TestApp -func (tapp *TestApp) RunDeliverTx(tx sdk.Tx) sdk.Result { - tapp.ensureBeginBlock() - return tapp.BaseApp.runTx(false, nil, tx) -} - -// run tx through CheckTx of TestApp -// NOTE: Skips authentication by wrapping msg in testTx{}. -func (tapp *TestApp) RunCheckMsg(msg sdk.Msg) sdk.Result { - var tx = testTx{msg} - return tapp.RunCheckTx(tx) -} - -// run tx through DeliverTx of TestApp -// NOTE: Skips authentication by wrapping msg in testTx{}. -func (tapp *TestApp) RunDeliverMsg(msg sdk.Msg) sdk.Result { - var tx = testTx{msg} - return tapp.RunDeliverTx(tx) -} - -// return the commited multistore -func (tapp *TestApp) CommitMultiStore() sdk.CommitMultiStore { - return tapp.BaseApp.cms -} - -// return a cache-wrap CheckTx state of multistore -func (tapp *TestApp) MultiStoreCheck() sdk.MultiStore { - return tapp.BaseApp.msCheck -} - -// return a cache-wrap DeliverTx state of multistore -func (tapp *TestApp) MultiStoreDeliver() sdk.MultiStore { - return tapp.BaseApp.msDeliver -} - -//---------------------------------------- -// testTx - -type testTx struct { - sdk.Msg -} - -// nolint -func (tx testTx) GetMsg() sdk.Msg { return tx.Msg } -func (tx testTx) GetSigners() []crypto.Address { return nil } -func (tx testTx) GetFeePayer() crypto.Address { return nil } -func (tx testTx) GetSignatures() []sdk.StdSignature { return nil } -func IsTestAppTx(tx sdk.Tx) bool { - _, ok := tx.(testTx) - return ok -} diff --git a/examples/basecoin/Makefile b/examples/basecoin/Makefile index aa45d0d839..c617692a5c 100644 --- a/examples/basecoin/Makefile +++ b/examples/basecoin/Makefile @@ -1,41 +1,19 @@ PACKAGES=$(shell go list ./... | grep -v '/vendor/') BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/basecoin/version.GitCommit=`git rev-parse --short HEAD`" -all: check_tools get_vendor_deps build test - -######################################## -### Build +all: get_vendor_deps build test build: go build $(BUILD_FLAGS) -o build/basecoin ./cmd/... - -######################################## -### Tools & dependencies - -check_tools: - cd tools && $(MAKE) check - -get_tools: - cd tools && $(MAKE) - get_vendor_deps: @rm -rf vendor/ - @echo "--> Running glide install" @glide install - -######################################## -### Testing - test: @go test $(PACKAGES) benchmark: @go test -bench=. $(PACKAGES) - -# To avoid unintended conflicts with file names, always add to .PHONY -# unless there is a reason not to. -# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -.PHONY: build check_tools get_tools get_vendor_deps test benchmark +.PHONY: build get_vendor_deps test benchmark diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 4f26d49445..b1f66009e4 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -1,95 +1,114 @@ package app import ( - "fmt" - "os" + "encoding/json" + + abci "github.com/tendermint/abci/types" + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-wire" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/tendermint/abci/server" - abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-wire" - cmn "github.com/tendermint/tmlibs/common" + "github.com/cosmos/cosmos-sdk/examples/basecoin/types" + "github.com/cosmos/cosmos-sdk/examples/basecoin/x/sketchy" ) -const appName = "BasecoinApp" +const ( + appName = "BasecoinApp" +) -// BasecoinApp - extended ABCI application +// Extended ABCI application type BasecoinApp struct { *bam.BaseApp - router bam.Router - cdc *wire.Codec - multiStore sdk.CommitMultiStore //TODO distinguish this store from *bam.BaseApp.cms <- is this one master?? confused + cdc *wire.Codec - // The key to access the substores. + // keys to access the substores capKeyMainStore *sdk.KVStoreKey capKeyIBCStore *sdk.KVStoreKey - // Object mappers: + // Manage getting and setting accounts accountMapper sdk.AccountMapper } -// NewBasecoinApp - create new BasecoinApp -// TODO: This should take in more configuration options. -// TODO: This should be moved into baseapp to isolate complexity -func NewBasecoinApp(genesisPath string) *BasecoinApp { - - // Create and configure app. - var app = &BasecoinApp{} - - // TODO open up out of functions, or introduce clarity, - // interdependancies are a nightmare to debug - app.initCapKeys() // ./init_capkeys.go - app.initBaseApp() // ./init_baseapp.go - app.initStores() // ./init_stores.go - app.initBaseAppInitStater() - app.initHandlers() // ./init_handlers.go - - genesisiDoc, err := bam.GenesisDocFromFile(genesisPath) - if err != nil { - panic(fmt.Errorf("error loading genesis state: %v", err)) +func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { + // create your application object + var app = &BasecoinApp{ + BaseApp: bam.NewBaseApp(appName, logger, db), + cdc: MakeTxCodec(), + capKeyMainStore: sdk.NewKVStoreKey("main"), + capKeyIBCStore: sdk.NewKVStoreKey("ibc"), } - // set up the cache store for ctx, get ctx - // TODO: can InitChain handle this too ? - app.BaseApp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{}}) - ctx := app.BaseApp.NewContext(false, nil) // context for DeliverTx + // define the accountMapper + app.accountMapper = auth.NewAccountMapperSealed( + app.capKeyMainStore, // target store + &types.AppAccount{}, // prototype + ) - // TODO: combine with InitChain and let tendermint invoke it. - err = app.BaseApp.InitStater(ctx, genesisiDoc.AppState) + // add handlers + coinKeeper := bank.NewCoinKeeper(app.accountMapper) + app.Router().AddRoute("bank", bank.NewHandler(coinKeeper)) + app.Router().AddRoute("sketchy", sketchy.NewHandler()) + + // initialize BaseApp + app.SetTxDecoder() + app.SetInitChainer() + app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore) + app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper)) + err := app.LoadLatestVersion(app.capKeyMainStore) if err != nil { - panic(fmt.Errorf("error initializing application genesis state: %v", err)) + cmn.Exit(err.Error()) } - app.loadStores() - return app } -// RunForever - BasecoinApp execution and cleanup -func (app *BasecoinApp) RunForever() { +// custom tx codec +func MakeTxCodec() *wire.Codec { + cdc := wire.NewCodec() + crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types. + bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types. + return cdc +} - // Start the ABCI server - srv, err := server.NewServer("0.0.0.0:46658", "socket", app) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - srv.Start() - - // Wait forever - cmn.TrapSignal(func() { - // Cleanup - srv.Stop() +// custom logic for transaction decoding +func (app *BasecoinApp) SetTxDecoder() { + app.BaseApp.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) { + var tx = sdk.StdTx{} + // StdTx.Msg is an interface whose concrete + // types are registered in app/msgs.go. + err := app.cdc.UnmarshalBinary(txBytes, &tx) + if err != nil { + return nil, sdk.ErrTxParse("").TraceCause(err, "") + } + return tx, nil }) - } -// Load the stores -func (app *BasecoinApp) loadStores() { - if err := app.LoadLatestVersion(app.capKeyMainStore); err != nil { - fmt.Println(err) - os.Exit(1) - } +// custom logic for basecoin initialization +func (app *BasecoinApp) SetInitChainer() { + app.BaseApp.SetInitChainer(func(ctx sdk.Context, req abci.RequestInitChain) sdk.Error { + stateJSON := req.AppStateBytes + + genesisState := new(types.GenesisState) + err := json.Unmarshal(stateJSON, genesisState) + if err != nil { + return sdk.ErrGenesisParse("").TraceCause(err, "") + } + + for _, gacc := range genesisState.Accounts { + acc, err := gacc.ToAppAccount() + if err != nil { + return sdk.ErrGenesisParse("").TraceCause(err, "") + } + app.accountMapper.SetAccount(ctx, acc) + } + return nil + }) } diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index ce071eeeda..5ef1195c8f 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -2,6 +2,7 @@ package app import ( "encoding/json" + "os" "testing" "github.com/stretchr/testify/assert" @@ -12,12 +13,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" + abci "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" ) +func newBasecoinApp() *BasecoinApp { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") + db := dbm.NewMemDB() + return NewBasecoinApp(logger, db) +} + func TestSendMsg(t *testing.T) { - tba := newTestBasecoinApp() - tba.RunBeginBlock() + bapp := newBasecoinApp() // Construct a SendMsg. var msg = bank.SendMsg{ @@ -36,15 +45,25 @@ func TestSendMsg(t *testing.T) { }, } - // Run a Check on SendMsg. - res := tba.RunCheckMsg(msg) - assert.Equal(t, sdk.CodeOK, res.Code, res.Log) + priv := crypto.GenPrivKeyEd25519() + sig := priv.Sign(msg.GetSignBytes()) + tx := sdk.NewStdTx(msg, []sdk.StdSignature{{ + PubKey: priv.PubKey(), + Signature: sig, + }}) - // Run a Deliver on SendMsg. - res = tba.RunDeliverMsg(msg) + // Run a Check + res := bapp.Check(tx) assert.Equal(t, sdk.CodeUnrecognizedAddress, res.Code, res.Log) - // TODO seperate this test, need a closer on db? keep getting resource unavailable + // Simulate a Block + bapp.BeginBlock(abci.RequestBeginBlock{}) + res = bapp.Deliver(tx) + assert.Equal(t, sdk.CodeUnrecognizedAddress, res.Code, res.Log) +} + +func TestGenesis(t *testing.T) { + bapp := newBasecoinApp() // construct some genesis bytes to reflect basecoin/types/AppAccount pk := crypto.GenPrivKeyEd25519().PubKey() @@ -57,18 +76,19 @@ func TestSendMsg(t *testing.T) { } acc := &types.AppAccount{baseAcc, "foobart"} - genesisState := GenesisState{ - Accounts: []*GenesisAccount{ - NewGenesisAccount(acc), + genesisState := types.GenesisState{ + Accounts: []*types.GenesisAccount{ + types.NewGenesisAccount(acc), }, } - bytes, err := json.MarshalIndent(genesisState, "", "\t") + stateBytes, err := json.MarshalIndent(genesisState, "", "\t") - app := tba.BasecoinApp - ctx := app.BaseApp.NewContext(false, nil) // context for DeliverTx - err = app.BaseApp.InitStater(ctx, bytes) - require.Nil(t, err) + vals := []abci.Validator{} + bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) - res1 := app.accountMapper.GetAccount(ctx, baseAcc.Address) + // a checkTx context + ctx := bapp.BaseApp.NewContext(true, nil) + + res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address) assert.Equal(t, acc, res1) } diff --git a/examples/basecoin/app/init_baseapp.go b/examples/basecoin/app/init_baseapp.go deleted file mode 100644 index c1fcb94c6b..0000000000 --- a/examples/basecoin/app/init_baseapp.go +++ /dev/null @@ -1,93 +0,0 @@ -package app - -import ( - "encoding/json" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - crypto "github.com/tendermint/go-crypto" -) - -// initCapKeys, initBaseApp, initStores, initHandlers. -func (app *BasecoinApp) initBaseApp() { - bapp := baseapp.NewBaseApp(appName) - app.BaseApp = bapp - app.router = bapp.Router() - app.initBaseAppTxDecoder() - app.initBaseAppInitStater() -} - -func (app *BasecoinApp) initBaseAppTxDecoder() { - cdc := makeTxCodec() - app.BaseApp.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) { - var tx = sdk.StdTx{} - // StdTx.Msg is an interface whose concrete - // types are registered in app/msgs.go. - err := cdc.UnmarshalBinary(txBytes, &tx) - if err != nil { - return nil, sdk.ErrTxParse("").TraceCause(err, "") - } - return tx, nil - }) -} - -// define the custom logic for basecoin initialization -func (app *BasecoinApp) initBaseAppInitStater() { - accountMapper := app.accountMapper - - app.BaseApp.SetInitStater(func(ctx sdk.Context, state json.RawMessage) sdk.Error { - if state == nil { - return nil - } - - genesisState := new(GenesisState) - err := json.Unmarshal(state, genesisState) - if err != nil { - return sdk.ErrGenesisParse("").TraceCause(err, "") - } - - for _, gacc := range genesisState.Accounts { - acc, err := gacc.toAppAccount() - if err != nil { - return sdk.ErrGenesisParse("").TraceCause(err, "") - } - accountMapper.SetAccount(ctx, acc) - } - return nil - }) -} - -//----------------------------------------------------- - -type GenesisState struct { - Accounts []*GenesisAccount `accounts` -} - -// GenesisAccount doesn't need pubkey or sequence -type GenesisAccount struct { - Name string `json:"name"` - Address crypto.Address `json:"address"` - Coins sdk.Coins `json:"coins"` -} - -func NewGenesisAccount(aa *types.AppAccount) *GenesisAccount { - return &GenesisAccount{ - Name: aa.Name, - Address: aa.Address, - Coins: aa.Coins, - } -} - -// convert GenesisAccount to AppAccount -func (ga *GenesisAccount) toAppAccount() (acc *types.AppAccount, err error) { - baseAcc := auth.BaseAccount{ - Address: ga.Address, - Coins: ga.Coins, - } - return &types.AppAccount{ - BaseAccount: baseAcc, - Name: ga.Name, - }, nil -} diff --git a/examples/basecoin/app/init_capkeys.go b/examples/basecoin/app/init_capkeys.go deleted file mode 100644 index 7327c61843..0000000000 --- a/examples/basecoin/app/init_capkeys.go +++ /dev/null @@ -1,16 +0,0 @@ -package app - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// initCapKeys, initBaseApp, initStores, initHandlers. -func (app *BasecoinApp) initCapKeys() { - - // All top-level capabilities keys - // should be constructed here. - // For more information, see http://www.erights.org/elib/capability/ode/ode.pdf. - app.capKeyMainStore = sdk.NewKVStoreKey("main") - app.capKeyIBCStore = sdk.NewKVStoreKey("ibc") - -} diff --git a/examples/basecoin/app/init_handlers.go b/examples/basecoin/app/init_handlers.go deleted file mode 100644 index 62831514b1..0000000000 --- a/examples/basecoin/app/init_handlers.go +++ /dev/null @@ -1,30 +0,0 @@ -package app - -import ( - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/sketchy" -) - -// initCapKeys, initBaseApp, initStores, initHandlers. -func (app *BasecoinApp) initHandlers() { - app.initDefaultAnteHandler() - app.initRouterHandlers() -} - -func (app *BasecoinApp) initDefaultAnteHandler() { - - // Deducts fee from payer. - // Verifies signatures and nonces. - // Sets Signers to ctx. - app.BaseApp.SetDefaultAnteHandler( - auth.NewAnteHandler(app.accountMapper)) -} - -func (app *BasecoinApp) initRouterHandlers() { - - // All handlers must be added here. - // The order matters. - app.router.AddRoute("bank", bank.NewHandler(bank.NewCoinKeeper(app.accountMapper))) - app.router.AddRoute("sketchy", sketchy.NewHandler()) -} diff --git a/examples/basecoin/app/init_stores.go b/examples/basecoin/app/init_stores.go deleted file mode 100644 index e819ecd2c5..0000000000 --- a/examples/basecoin/app/init_stores.go +++ /dev/null @@ -1,38 +0,0 @@ -package app - -import ( - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" -) - -// initCapKeys, initBaseApp, initStores, initHandlers. -func (app *BasecoinApp) initStores() { - app.mountStores() - app.initAccountMapper() -} - -// Initialize root stores. -func (app *BasecoinApp) mountStores() { - - // Create MultiStore mounts. - app.BaseApp.MountStore(app.capKeyMainStore, sdk.StoreTypeIAVL) - app.BaseApp.MountStore(app.capKeyIBCStore, sdk.StoreTypeIAVL) -} - -// Initialize the AccountMapper. -func (app *BasecoinApp) initAccountMapper() { - - var accountMapper = auth.NewAccountMapper( - app.capKeyMainStore, // target store - &types.AppAccount{}, // prototype - ) - - // Register all interfaces and concrete types that - // implement those interfaces, here. - cdc := accountMapper.WireCodec() - auth.RegisterWireBaseAccount(cdc) - - // Make accountMapper's WireCodec() inaccessible. - app.accountMapper = accountMapper.Seal() -} diff --git a/examples/basecoin/app/msgs.go b/examples/basecoin/app/msgs.go deleted file mode 100644 index a8ec883bdf..0000000000 --- a/examples/basecoin/app/msgs.go +++ /dev/null @@ -1,22 +0,0 @@ -package app - -import ( - "github.com/cosmos/cosmos-sdk/x/bank" - crypto "github.com/tendermint/go-crypto" - wire "github.com/tendermint/go-wire" -) - -// Wire requires registration of interfaces & concrete types. All -// interfaces to be encoded/decoded in a Msg must be registered -// here, along with all the concrete types that implement them. -func makeTxCodec() (cdc *wire.Codec) { - cdc = wire.NewCodec() - - // Register crypto.[PubKey,PrivKey,Signature] types. - crypto.RegisterWire(cdc) - - // Register bank.[SendMsg,IssueMsg] types. - bank.RegisterWire(cdc) - - return -} diff --git a/examples/basecoin/app/testapp.go b/examples/basecoin/app/testapp.go deleted file mode 100644 index 8603911d51..0000000000 --- a/examples/basecoin/app/testapp.go +++ /dev/null @@ -1,19 +0,0 @@ -package app - -import ( - bam "github.com/cosmos/cosmos-sdk/baseapp" -) - -type testBasecoinApp struct { - *BasecoinApp - *bam.TestApp -} - -func newTestBasecoinApp() *testBasecoinApp { - app := NewBasecoinApp("") - tba := &testBasecoinApp{ - BasecoinApp: app, - } - tba.TestApp = bam.NewTestApp(app.BaseApp) - return tba -} diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 623bb35e45..5ea25c42ff 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -1,10 +1,27 @@ package main -import "github.com/cosmos/cosmos-sdk/examples/basecoin/app" +import ( + "fmt" + "os" + + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" +) func main() { // TODO CREATE CLI - bapp := app.NewBasecoinApp("") - bapp.RunForever() + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") + + db, err := dbm.NewGoLevelDB("basecoind", "data") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + bapp := app.NewBasecoinApp(logger, db) + baseapp.RunForever(bapp) } diff --git a/examples/basecoin/tools/.gitignore b/examples/basecoin/tools/.gitignore deleted file mode 100644 index 36f971e324..0000000000 --- a/examples/basecoin/tools/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bin/* diff --git a/examples/basecoin/tools/Makefile b/examples/basecoin/tools/Makefile deleted file mode 100644 index e945888ac4..0000000000 --- a/examples/basecoin/tools/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -all: install_glide check get_vendor_deps install - - -######################################## -### Glide - -GLIDE = github.com/tendermint/glide -GLIDE_CHECK := $(shell command -v glide 2> /dev/null) - -check: -ifndef GLIDE_CHECK - @echo "No glide in path. Install with 'make install_glide'." -else - @echo "Found glide in path." -endif - -install_glide: -ifdef GLIDE_CHECK - @echo "Glide is already installed. Run 'make update_glide' to update." -else - @echo "$(ansi_grn)Installing glide$(ansi_end)" - go get -v $(GLIDE) -endif - -update_glide: - @echo "$(ansi_grn)Updating glide$(ansi_end)" - go get -u -v $(GLIDE) - - -######################################## -### Install tools - - -get_vendor_deps: check - @rm -rf vendor/ - @echo "--> Running glide install" - @glide install - -install: get_vendor_deps - @echo "$(ansi_grn)Installing tools$(ansi_end)" - @echo "$(ansi_yel)Install go-vendorinstall$(ansi_end)" - go build -o bin/go-vendorinstall go-vendorinstall/*.go - - @echo "$(ansi_yel)Install gometalinter.v2$(ansi_end)" - GOBIN=$(CURDIR)/bin ./bin/go-vendorinstall gopkg.in/alecthomas/gometalinter.v2 - - @echo "$(ansi_grn)Done installing tools$(ansi_end)" - - -######################################## -# ANSI colors - -ansi_red=\033[0;31m -ansi_grn=\033[0;32m -ansi_yel=\033[0;33m -ansi_end=\033[0m - - -# To avoid unintended conflicts with file names, always add to .PHONY -# unless there is a reason not to. -# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -.PHONY: check install_glide update_glide get_vendor_deps install diff --git a/examples/basecoin/tools/glide.lock b/examples/basecoin/tools/glide.lock deleted file mode 100644 index db718da50d..0000000000 --- a/examples/basecoin/tools/glide.lock +++ /dev/null @@ -1,18 +0,0 @@ -hash: a163b1c4806024cfc9062db75a0abed285ec40461243e59af0e147db2c4bf0ce -updated: 2018-01-15T19:02:49.834182027-08:00 -imports: -- name: github.com/inconshreveable/mousetrap - version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 -- name: github.com/rigelrozanski/common - version: f691f115798593d783b9999b1263c2f4ffecc439 -- name: github.com/rigelrozanski/shelldown - version: 2e18b6eb9bf428aa524e71433296e0b7c73ae0a3 - subpackages: - - cmd/shelldown -- name: github.com/spf13/cobra - version: 7b2c5ac9fc04fc5efafb60700713d4fa609b777b -- name: github.com/spf13/pflag - version: 97afa5e7ca8a08a383cb259e06636b5e2cc7897f -- name: gopkg.in/alecthomas/gometalinter.v2 - version: 88d47c66988c5a5cb3945925da47c883800a94df -testImports: [] diff --git a/examples/basecoin/tools/glide.yaml b/examples/basecoin/tools/glide.yaml deleted file mode 100644 index 3a3e85d6a7..0000000000 --- a/examples/basecoin/tools/glide.yaml +++ /dev/null @@ -1,6 +0,0 @@ -package: github.com/cosmos/cosmos-sdk/tools -import: -- package: github.com/rigelrozanski/shelldown - subpackages: - - cmd/shelldown -- package: gopkg.in/alecthomas/gometalinter.v2 diff --git a/examples/basecoin/tools/go-vendorinstall/main.go b/examples/basecoin/tools/go-vendorinstall/main.go deleted file mode 100644 index c42e678f2f..0000000000 --- a/examples/basecoin/tools/go-vendorinstall/main.go +++ /dev/null @@ -1,129 +0,0 @@ -// https://raw.githubusercontent.com/roboll/go-vendorinstall/a3e9f0a5d5861b3bb16b93200b2c359c9846b3c5/main.go - -package main - -import ( - "errors" - "flag" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strings" -) - -var ( - source = flag.String("source", "vendor", "source directory") - target = flag.String("target", "", "target directory (defaults to $GOBIN, if not set $GOPATH/bin)") - commands = flag.String("commands", "", "comma separated list of commands to execute after go install in temporary environment") - quiet = flag.Bool("quiet", false, "disable output") -) - -func main() { - flag.Parse() - - packages := flag.Args() - if len(packages) < 1 { - fail(errors.New("no packages: specify a package")) - } - - gopath, err := ioutil.TempDir("", "go-vendorinstall-gopath") - if err != nil { - fail(err) - } - print(fmt.Sprintf("gopath: %s", gopath)) - defer func() { - if err := os.RemoveAll(gopath); err != nil { - fail(err) - } - }() - - if len(*target) == 0 { - if gobin := os.Getenv("GOBIN"); len(gobin) > 0 { - target = &gobin - } else { - bin := fmt.Sprintf("%s/bin", os.Getenv("GOPATH")) - target = &bin - } - } - - gobin, err := filepath.Abs(*target) - if err != nil { - fail(err) - } - print(fmt.Sprintf("gobin: %s", gobin)) - - if err := link(gopath, *source); err != nil { - fail(err) - } - - oldpath := os.Getenv("PATH") - path := fmt.Sprintf("%s%s%s", gobin, string(os.PathListSeparator), os.Getenv("PATH")) - os.Setenv("PATH", fmt.Sprintf("%s%s%s", gobin, string(os.PathListSeparator), os.Getenv("PATH"))) - defer os.Setenv("PATH", oldpath) - - env := []string{fmt.Sprintf("PATH=%s", path), fmt.Sprintf("GOPATH=%s", gopath), fmt.Sprintf("GOBIN=%s", gobin)} - args := append([]string{"install"}, packages...) - if out, err := doexec("go", gopath, args, env); err != nil { - print(string(out)) - fail(err) - } - - if len(*commands) > 0 { - for _, cmd := range strings.Split(*commands, ",") { - split := strings.Split(cmd, " ") - if out, err := doexec(split[0], gopath, split[1:], env); err != nil { - print(string(out)) - fail(err) - } - } - } -} - -func print(msg string) { - if !*quiet { - fmt.Println(msg) - } -} - -func fail(err error) { - fmt.Printf("error: %s", err.Error()) - os.Exit(1) -} - -func link(gopath, source string) error { - srcdir, err := filepath.Abs(source) - if err != nil { - return err - } - - linkto := filepath.Join(gopath, "src") - if err := os.MkdirAll(linkto, 0777); err != nil { - return err - } - - files, err := ioutil.ReadDir(srcdir) - if err != nil { - return err - } - - for _, file := range files { - real := filepath.Join(srcdir, file.Name()) - link := filepath.Join(linkto, file.Name()) - if err := os.Symlink(real, link); err != nil { - return err - } - } - - return nil -} - -func doexec(bin, dir string, args []string, env []string) ([]byte, error) { - print(fmt.Sprintf("%s %s", bin, strings.Join(args, " "))) - cmd := exec.Command(bin, args...) - cmd.Env = env - cmd.Dir = dir - - return cmd.CombinedOutput() -} diff --git a/examples/basecoin/tools/main.go b/examples/basecoin/tools/main.go deleted file mode 100644 index 7fd61d589d..0000000000 --- a/examples/basecoin/tools/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - // Include dependencies here so glide picks them up - // and installs sub-dependencies. - - // TODO: Ideally this gets auto-imported on glide update. - // Any way to make that happen? - _ "github.com/rigelrozanski/common" -) - -func main() {} diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index 28a1f41552..3177f99122 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -3,25 +3,55 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + crypto "github.com/tendermint/go-crypto" ) var _ sdk.Account = (*AppAccount)(nil) +// Custom extensions for this application. This is just an example of +// extending auth.BaseAccount with custom fields. +// +// This is compatible with the stock auth.AccountStore, since +// auth.AccountStore uses the flexible go-wire library. type AppAccount struct { auth.BaseAccount - - // Custom extensions for this application. This is just an example of - // extending auth.BaseAccount with custom fields. - // - // This is compatible with the stock auth.AccountStore, since - // auth.AccountStore uses the flexible go-wire library. Name string } -func (acc AppAccount) GetName() string { - return acc.Name +// nolint +func (acc AppAccount) GetName() string { return acc.Name } +func (acc *AppAccount) SetName(name string) { acc.Name = name } + +//___________________________________________________________________________________ + +// State to Unmarshal +type GenesisState struct { + Accounts []*GenesisAccount `json:"accounts"` } -func (acc *AppAccount) SetName(name string) { - acc.Name = name +// GenesisAccount doesn't need pubkey or sequence +type GenesisAccount struct { + Name string `json:"name"` + Address crypto.Address `json:"address"` + Coins sdk.Coins `json:"coins"` +} + +func NewGenesisAccount(aa *AppAccount) *GenesisAccount { + return &GenesisAccount{ + Name: aa.Name, + Address: aa.Address, + Coins: aa.Coins, + } +} + +// convert GenesisAccount to AppAccount +func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) { + baseAcc := auth.BaseAccount{ + Address: ga.Address, + Coins: ga.Coins, + } + return &AppAccount{ + BaseAccount: baseAcc, + Name: ga.Name, + }, nil } diff --git a/x/sketchy/handler.go b/examples/basecoin/x/sketchy/handler.go similarity index 100% rename from x/sketchy/handler.go rename to examples/basecoin/x/sketchy/handler.go diff --git a/examples/dummy/main.go b/examples/dummy/main.go index 6457fb28c9..b09662f1db 100644 --- a/examples/dummy/main.go +++ b/examples/dummy/main.go @@ -6,6 +6,8 @@ import ( "github.com/tendermint/abci/server" cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -13,11 +15,19 @@ import ( func main() { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") + + db, err := dbm.NewGoLevelDB("basecoind", "data") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + // Capabilities key to access the main KVStore. var capKeyMainStore = sdk.NewKVStoreKey("main") // Create BaseApp. - var baseApp = bam.NewBaseApp("dummy") + var baseApp = bam.NewBaseApp("dummy", logger, db) // Set mounts for BaseApp's MultiStore. baseApp.MountStore(capKeyMainStore, sdk.StoreTypeIAVL) diff --git a/glide.lock b/glide.lock index bb013ee718..341a27020d 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 2b4ad3bf1489a7cb5e62c6cb4c1fa976d4ae21993743e4968418c4e81925fb99 -updated: 2018-02-13T08:33:22.402132782-05:00 +hash: 4d523b19f5b2f54caca37e196582c85d459fdfa5d5383d7e97ee81639b46129d +updated: 2018-02-16T19:52:42.711474619-05:00 imports: - name: github.com/btcsuite/btcd version: 50de9da05b50eb15658bb350f6ea24368a111ab7 @@ -82,7 +82,7 @@ imports: - name: github.com/spf13/viper version: 25b30aa063fc18e48662b86996252eabdcf2f0c7 - name: github.com/syndtr/goleveldb - version: 211f780988068502fe874c44dae530528ebd840f + version: 34011bf325bce385408353a30b101fe5e923eb6e subpackages: - leveldb - leveldb/cache @@ -97,7 +97,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: bf70f5e273bd7dd6e22e64186cd1ccc4e3a03df1 + version: a6be687088a7158e9e431931c3769d6706f90c4c subpackages: - server - types @@ -113,7 +113,7 @@ imports: - name: github.com/tendermint/iavl version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb - name: github.com/tendermint/tmlibs - version: c858b3ba78316fdd9096a11409a7e7a493e7d974 + version: a0f652dc2e131be86fc8d9e4e2beec9831a8a6ec subpackages: - cli - common diff --git a/glide.yaml b/glide.yaml index 276513b2ce..b2d6a94773 100644 --- a/glide.yaml +++ b/glide.yaml @@ -8,7 +8,7 @@ import: version: ^0.8.0 - package: github.com/rigelrozanski/common - package: github.com/tendermint/abci - version: develop + version: feature/init-chain-app-state subpackages: - server - types diff --git a/store/iavlstore.go b/store/iavlstore.go index 52deef367a..e63585a16b 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -19,7 +19,6 @@ const ( func LoadIAVLStore(db dbm.DB, id CommitID) (CommitStore, error) { tree := iavl.NewVersionedTree(db, defaultIAVLCacheSize) - fmt.Println("LoadIAVLStore Version ", id.Version) err := tree.LoadVersion(id.Version) if err != nil { return nil, err diff --git a/types/abci.go b/types/abci.go new file mode 100644 index 0000000000..58f40dd673 --- /dev/null +++ b/types/abci.go @@ -0,0 +1,6 @@ +package types + +import abci "github.com/tendermint/abci/types" + +// initialize application state at genesis +type InitChainer func(ctx Context, req abci.RequestInitChain) Error diff --git a/types/genesis.go b/types/genesis.go deleted file mode 100644 index 643235e3e9..0000000000 --- a/types/genesis.go +++ /dev/null @@ -1,6 +0,0 @@ -package types - -import "encoding/json" - -// function variable used to initialize application state at genesis -type InitStater func(ctx Context, state json.RawMessage) Error diff --git a/types/store.go b/types/store.go index 872c380901..b552f8cb02 100644 --- a/types/store.go +++ b/types/store.go @@ -61,7 +61,8 @@ type CommitMultiStore interface { Committer MultiStore - // Mount a store of type. + // Mount a store of type using the given db. + // If db == nil, the new store will use the CommitMultiStore db. MountStoreWithDB(key StoreKey, typ StoreType, db dbm.DB) // Panics on a nil key. diff --git a/types/tx_msg.go b/types/tx_msg.go index ebed8a25a6..8763075b8b 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -49,15 +49,23 @@ type Tx interface { var _ Tx = (*StdTx)(nil) -// standard transaction form +// StdTx is a standard way to wrap a Msg with Signatures. +// NOTE: the first signature is the FeePayer (Signatures must not be nil). type StdTx struct { Msg Signatures []StdSignature } +func NewStdTx(msg Msg, sigs []StdSignature) StdTx { + return StdTx{ + Msg: msg, + Signatures: sigs, + } +} + //nolint func (tx StdTx) GetMsg() Msg { return tx.Msg } -func (tx StdTx) GetFeePayer() crypto.Address { return tx.Signatures[0].PubKey.Address() } +func (tx StdTx) GetFeePayer() crypto.Address { return tx.Signatures[0].PubKey.Address() } // XXX but PubKey is optional! func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures } //------------------------------------- diff --git a/x/auth/ante.go b/x/auth/ante.go index a91fa55c28..28785c8096 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -1,7 +1,6 @@ package auth import ( - bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -25,19 +24,15 @@ func NewAnteHandler(accountMapper sdk.AccountMapper) sdk.AnteHandler { // TODO: accountMapper.SetAccount(ctx, payerAddr) } else { // TODO: Ensure that some other spam prevention is used. - // NOTE: bam.TestApp.RunDeliverMsg/RunCheckMsg will - // create a Tx with no payer. } var sigs = tx.GetSignatures() // Assert that there are signatures. - if !bam.IsTestAppTx(tx) { - if len(sigs) == 0 { - return ctx, - sdk.ErrUnauthorized("no signers").Result(), - true - } + if len(sigs) == 0 { + return ctx, + sdk.ErrUnauthorized("no signers").Result(), + true } // Ensure that sigs are correct. @@ -46,49 +41,47 @@ func NewAnteHandler(accountMapper sdk.AccountMapper) sdk.AnteHandler { var signerAccs = make([]sdk.Account, len(signerAddrs)) // Assert that number of signatures is correct. - if !bam.IsTestAppTx(tx) { - if len(sigs) != len(signerAddrs) { + if len(sigs) != len(signerAddrs) { + return ctx, + sdk.ErrUnauthorized("wrong number of signers").Result(), + true + } + + // Check each nonce and sig. + // TODO Refactor out. + for i, sig := range sigs { + + var signerAcc = accountMapper.GetAccount(ctx, signerAddrs[i]) + signerAccs[i] = signerAcc + + // If no pubkey, set pubkey. + if signerAcc.GetPubKey() == nil { + err := signerAcc.SetPubKey(sig.PubKey) + if err != nil { + return ctx, + sdk.ErrInternal("setting PubKey on signer").Result(), + true + } + } + + // Check and increment sequence number. + seq := signerAcc.GetSequence() + if seq != sig.Sequence { return ctx, - sdk.ErrUnauthorized("wrong number of signers").Result(), + sdk.ErrInvalidSequence("").Result(), + true + } + signerAcc.SetSequence(seq + 1) + + // Check sig. + if !sig.PubKey.VerifyBytes(msg.GetSignBytes(), sig.Signature) { + return ctx, + sdk.ErrUnauthorized("").Result(), true } - // Check each nonce and sig. - // TODO Refactor out. - for i, sig := range sigs { - - var signerAcc = accountMapper.GetAccount(ctx, signerAddrs[i]) - signerAccs[i] = signerAcc - - // If no pubkey, set pubkey. - if signerAcc.GetPubKey() == nil { - err := signerAcc.SetPubKey(sig.PubKey) - if err != nil { - return ctx, - sdk.ErrInternal("setting PubKey on signer").Result(), - true - } - } - - // Check and increment sequence number. - seq := signerAcc.GetSequence() - if seq != sig.Sequence { - return ctx, - sdk.ErrInvalidSequence("").Result(), - true - } - signerAcc.SetSequence(seq + 1) - - // Check sig. - if !sig.PubKey.VerifyBytes(msg.GetSignBytes(), sig.Signature) { - return ctx, - sdk.ErrUnauthorized("").Result(), - true - } - - // Save the account. - accountMapper.SetAccount(ctx, signerAcc) - } + // Save the account. + accountMapper.SetAccount(ctx, signerAcc) } ctx = WithSigners(ctx, signerAccs) diff --git a/x/auth/mapper.go b/x/auth/mapper.go index cccc774a3b..6cd51552c7 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -36,6 +36,20 @@ func NewAccountMapper(key sdk.StoreKey, proto sdk.Account) accountMapper { } } +// Create and return a sealed account mapper +func NewAccountMapperSealed(key sdk.StoreKey, proto sdk.Account) sealedAccountMapper { + cdc := wire.NewCodec() + am := accountMapper{ + key: key, + proto: proto, + cdc: cdc, + } + RegisterWireBaseAccount(cdc) + + // make accountMapper's WireCodec() inaccessible, return + return am.Seal() +} + // Returns the go-wire codec. You may need to register interfaces // and concrete types here, if your app's sdk.Account // implementation includes interface fields.