diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 089d1ca23..fca5e8639 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -3,183 +3,174 @@ package app import ( "encoding/json" - abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" - cmn "github.com/tendermint/tmlibs/common" - dbm "github.com/tendermint/tmlibs/db" - "github.com/tendermint/tmlibs/log" - bam "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/wire" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/ibc" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/stake" - - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" ) const ( appName = "BasecoinApp" ) -// Extended ABCI application +// BasecoinApp implements an extended ABCI application. It contains a BaseApp, +// a codec for serialization, KVStore keys for multistore state management, and +// various mappers and keepers to manage getting, setting, and serializing the +// integral app types. type BasecoinApp struct { *bam.BaseApp cdc *wire.Codec - // keys to access the substores - keyMain *sdk.KVStoreKey - keyAccount *sdk.KVStoreKey - keyIBC *sdk.KVStoreKey - keyStake *sdk.KVStoreKey - keySlashing *sdk.KVStoreKey + // keys to access the multistore + keyMain *sdk.KVStoreKey + keyAccount *sdk.KVStoreKey + keyIBC *sdk.KVStoreKey - // Manage getting and setting accounts + // manage getting and setting accounts accountMapper auth.AccountMapper feeCollectionKeeper auth.FeeCollectionKeeper coinKeeper bank.Keeper ibcMapper ibc.Mapper - stakeKeeper stake.Keeper - slashingKeeper slashing.Keeper } +// NewBasecoinApp returns a reference to a new BasecoinApp given a logger and +// database. Internally, a codec is created along with all the necessary keys. +// In addition, all necessary mappers and keepers are created, routes +// registered, and finally the stores being mounted along with any necessary +// chain initialization. func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { + // create and register app-level codec for TXs and accounts + cdc := MakeCodec() - // Create app-level codec for txs and accounts. - var cdc = MakeCodec() - - // Create your application object. + // create your application type var app = &BasecoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), - cdc: cdc, - keyMain: sdk.NewKVStoreKey("main"), - keyAccount: sdk.NewKVStoreKey("acc"), - keyIBC: sdk.NewKVStoreKey("ibc"), - keyStake: sdk.NewKVStoreKey("stake"), - keySlashing: sdk.NewKVStoreKey("slashing"), + cdc: cdc, + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + keyMain: sdk.NewKVStoreKey("main"), + keyAccount: sdk.NewKVStoreKey("acc"), + keyIBC: sdk.NewKVStoreKey("ibc"), } - // Define the accountMapper. + // define and attach the mappers and keepers app.accountMapper = auth.NewAccountMapper( cdc, app.keyAccount, // target store &types.AppAccount{}, // prototype ) - - // add accountMapper/handlers app.coinKeeper = bank.NewKeeper(app.accountMapper) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) - app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) - app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace)) // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.coinKeeper)). - AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)). - AddRoute("stake", stake.NewHandler(app.stakeKeeper)) + AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)) - // Initialize BaseApp. + // perform initialization logic app.SetInitChainer(app.initChainer) app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) - app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing) + + // mount the multistore and load the latest state + app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) } + return app } -// Custom tx codec +// MakeCodec creates a new wire codec and registers all the necessary types +// with the codec. func MakeCodec() *wire.Codec { - var cdc = wire.NewCodec() - wire.RegisterCrypto(cdc) // Register crypto. - sdk.RegisterWire(cdc) // Register Msgs + cdc := wire.NewCodec() + + wire.RegisterCrypto(cdc) + sdk.RegisterWire(cdc) bank.RegisterWire(cdc) - stake.RegisterWire(cdc) - slashing.RegisterWire(cdc) ibc.RegisterWire(cdc) - // register custom AppAccount + // register custom types cdc.RegisterInterface((*auth.Account)(nil), nil) cdc.RegisterConcrete(&types.AppAccount{}, "basecoin/Account", nil) + return cdc } -// application updates every end block -func (app *BasecoinApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper) - - return abci.ResponseBeginBlock{ - Tags: tags.ToKVPairs(), - } +// BeginBlocker reflects logic to run before any TXs application are processed +// by the application. +func (app *BasecoinApp) BeginBlocker(_ sdk.Context, _ abci.RequestBeginBlock) abci.ResponseBeginBlock { + return abci.ResponseBeginBlock{} } -// application updates every end block -// nolint: unparam -func (app *BasecoinApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper) - - return abci.ResponseEndBlock{ - ValidatorUpdates: validatorUpdates, - } +// EndBlocker reflects logic to run after all TXs are processed by the +// application. +func (app *BasecoinApp) EndBlocker(_ sdk.Context, _ abci.RequestEndBlock) abci.ResponseEndBlock { + return abci.ResponseEndBlock{} } -// Custom logic for basecoin initialization +// initChainer implements the custom application logic that the BaseApp will +// invoke upon initialization. In this case, it will take the application's +// state provided by 'req' and attempt to deserialize said state. The state +// should contain all the genesis accounts. These accounts will be added to the +// application's account mapper. func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { stateJSON := req.AppStateBytes genesisState := new(types.GenesisState) err := app.cdc.UnmarshalJSON(stateJSON, genesisState) if err != nil { - panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 - // return sdk.ErrGenesisParse("").TraceCause(err, "") + // TODO: https://github.com/cosmos/cosmos-sdk/issues/468 + panic(err) } for _, gacc := range genesisState.Accounts { acc, err := gacc.ToAppAccount() if err != nil { - panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 - // return sdk.ErrGenesisParse("").TraceCause(err, "") + // TODO: https://github.com/cosmos/cosmos-sdk/issues/468 + panic(err) } + acc.AccountNumber = app.accountMapper.GetNextAccountNumber(ctx) app.accountMapper.SetAccount(ctx, acc) } - // load the initial stake information - stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData) - return abci.ResponseInitChain{} } -// Custom logic for state export +// ExportAppStateAndValidators implements custom application logic that exposes +// various parts of the application's state and set of validators. An error is +// returned if any step getting the state or set of validators fails. func (app *BasecoinApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { ctx := app.NewContext(true, abci.Header{}) - - // iterate to get the accounts accounts := []*types.GenesisAccount{} - appendAccount := func(acc auth.Account) (stop bool) { + + appendAccountsFn := func(acc auth.Account) bool { account := &types.GenesisAccount{ Address: acc.GetAddress(), Coins: acc.GetCoins(), } + accounts = append(accounts, account) return false } - app.accountMapper.IterateAccounts(ctx, appendAccount) - genState := types.GenesisState{ - Accounts: accounts, - StakeData: stake.WriteGenesis(ctx, app.stakeKeeper), - } + app.accountMapper.IterateAccounts(ctx, appendAccountsFn) + + genState := types.GenesisState{Accounts: accounts} appState, err = wire.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err } - validators = stake.WriteValidators(ctx, app.stakeKeeper) + return appState, validators, err } diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index 499c0e408..2605dad66 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -1,88 +1,81 @@ package app import ( - "fmt" "os" "testing" - "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/stake" - gen "github.com/cosmos/cosmos-sdk/x/stake/types" - + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" ) -func setGenesis(bapp *BasecoinApp, accs ...auth.BaseAccount) error { - genaccs := make([]*types.GenesisAccount, len(accs)) - for i, acc := range accs { - genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, "foobart"}) +func setGenesis(baseApp *BasecoinApp, accounts ...*types.AppAccount) (types.GenesisState, error) { + genAccts := make([]*types.GenesisAccount, len(accounts)) + for i, appAct := range accounts { + genAccts[i] = types.NewGenesisAccount(appAct) } - genesisState := types.GenesisState{ - Accounts: genaccs, - StakeData: stake.DefaultGenesisState(), - } - - stateBytes, err := wire.MarshalJSONIndent(bapp.cdc, genesisState) + genesisState := types.GenesisState{Accounts: genAccts} + stateBytes, err := wire.MarshalJSONIndent(baseApp.cdc, genesisState) if err != nil { - return err + return types.GenesisState{}, err } - // Initialize the chain - vals := []abci.Validator{} - bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes}) - bapp.Commit() + // initialize and commit the chain + baseApp.InitChain(abci.RequestInitChain{ + Validators: []abci.Validator{}, AppStateBytes: stateBytes, + }) + baseApp.Commit() - return nil + return genesisState, nil } -//_______________________________________________________________________ - func TestGenesis(t *testing.T) { logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") db := dbm.NewMemDB() - bapp := NewBasecoinApp(logger, db) + baseApp := NewBasecoinApp(logger, db) - // Construct some genesis bytes to reflect basecoin/types/AppAccount - pk := crypto.GenPrivKeyEd25519().PubKey() - addr := pk.Address() + // construct a pubkey and an address for the test account + pubkey := crypto.GenPrivKeyEd25519().PubKey() + addr := pubkey.Address() + + // construct some test coins coins, err := sdk.ParseCoins("77foocoin,99barcoin") require.Nil(t, err) - baseAcc := auth.BaseAccount{ - Address: addr, - Coins: coins, - } - acc := &types.AppAccount{baseAcc, "foobart"} - err = setGenesis(bapp, baseAcc) + // create an auth.BaseAccount for the given test account and set it's coins + baseAcct := auth.NewBaseAccountWithAddress(addr) + err = baseAcct.SetCoins(coins) require.Nil(t, err) - // A checkTx context - ctx := bapp.BaseApp.NewContext(true, abci.Header{}) - res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address) - require.Equal(t, acc, res1) + // create a new test AppAccount with the given auth.BaseAccount + appAcct := types.NewAppAccount("foobar", baseAcct) + genState, err := setGenesis(baseApp, appAcct) + require.Nil(t, err) + + // create a context for the BaseApp + ctx := baseApp.BaseApp.NewContext(true, abci.Header{}) + res := baseApp.accountMapper.GetAccount(ctx, baseAcct.Address) + require.Equal(t, appAcct, res) // reload app and ensure the account is still there - bapp = NewBasecoinApp(logger, db) - // Initialize stake data with default genesis state - stakedata := gen.DefaultGenesisState() - genState, err := bapp.cdc.MarshalJSON(stakedata) - if err != nil { - panic(err) - } + baseApp = NewBasecoinApp(logger, db) - // InitChain with default stake data. Initializes deliverState and checkState context - bapp.InitChain(abci.RequestInitChain{AppStateBytes: []byte(fmt.Sprintf("{\"stake\": %s}", string(genState)))}) + stateBytes, err := wire.MarshalJSONIndent(baseApp.cdc, genState) + require.Nil(t, err) - ctx = bapp.BaseApp.NewContext(true, abci.Header{}) - res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address) - require.Equal(t, acc, res1) + // initialize the chain with the expected genesis state + baseApp.InitChain(abci.RequestInitChain{ + Validators: []abci.Validator{}, AppStateBytes: stateBytes, + }) + + ctx = baseApp.BaseApp.NewContext(true, abci.Header{}) + res = baseApp.accountMapper.GetAccount(ctx, baseAcct.Address) + require.Equal(t, appAcct, res) } diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 1191aab6a..fef9727a4 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -3,24 +3,20 @@ package main import ( "os" - "github.com/spf13/cobra" - - "github.com/tendermint/tmlibs/cli" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" - + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/examples/basecoin/types" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" - - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" + "github.com/spf13/cobra" + "github.com/tendermint/tmlibs/cli" ) // rootCmd is the entry point for this binary @@ -82,7 +78,7 @@ func main() { executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.basecli")) err := executor.Execute() if err != nil { - // handle with #870 + // Note: Handle with #870 panic(err) } } diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index f3f3de519..2cb37cfe1 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -4,16 +4,14 @@ import ( "encoding/json" "os" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/server" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" "github.com/tendermint/tmlibs/cli" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" - - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" - "github.com/cosmos/cosmos-sdk/server" ) func main() { @@ -33,9 +31,10 @@ func main() { // prepare and add flags rootDir := os.ExpandEnv("$HOME/.basecoind") executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir) + err := executor.Execute() if err != nil { - // handle with #870 + // Note: Handle with #870 panic(err) } } diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index 43a8e2e38..8047ed68d 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -4,18 +4,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/stake" ) var _ auth.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-amino library. +// AppAccount is a custom extension for this application. It is an example of +// extending auth.BaseAccount with custom fields. It is compatible with the +// stock auth.AccountStore, since auth.AccountStore uses the flexible go-amino +// library. type AppAccount struct { auth.BaseAccount + Name string `json:"name"` } @@ -23,36 +22,45 @@ type AppAccount struct { func (acc AppAccount) GetName() string { return acc.Name } func (acc *AppAccount) SetName(name string) { acc.Name = name } -// Get the AccountDecoder function for the custom AppAccount +// NewAppAccount returns a reference to a new AppAccount given a name and an +// auth.BaseAccount. +func NewAppAccount(name string, baseAcct auth.BaseAccount) *AppAccount { + return &AppAccount{BaseAccount: baseAcct, Name: name} +} + +// GetAccountDecoder returns the AccountDecoder function for the custom +// AppAccount. func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder { - return func(accBytes []byte) (res auth.Account, err error) { + return func(accBytes []byte) (auth.Account, error) { if len(accBytes) == 0 { return nil, sdk.ErrTxDecode("accBytes are empty") } + acct := new(AppAccount) - err = cdc.UnmarshalBinaryBare(accBytes, &acct) + err := cdc.UnmarshalBinaryBare(accBytes, &acct) if err != nil { panic(err) } + return acct, err } } -//___________________________________________________________________________________ - -// State to Unmarshal +// GenesisState reflects the genesis state of the application. type GenesisState struct { - Accounts []*GenesisAccount `json:"accounts"` - StakeData stake.GenesisState `json:"stake"` + Accounts []*GenesisAccount `json:"accounts"` } -// GenesisAccount doesn't need pubkey or sequence +// GenesisAccount reflects a genesis account the application expects in it's +// genesis state. type GenesisAccount struct { Name string `json:"name"` Address sdk.Address `json:"address"` Coins sdk.Coins `json:"coins"` } +// NewGenesisAccount returns a reference to a new GenesisAccount given an +// AppAccount. func NewGenesisAccount(aa *AppAccount) *GenesisAccount { return &GenesisAccount{ Name: aa.Name, @@ -61,14 +69,13 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount { } } -// convert GenesisAccount to AppAccount +// ToAppAccount converts a GenesisAccount to an AppAccount. func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) { - baseAcc := auth.BaseAccount{ - Address: ga.Address, - Coins: ga.Coins.Sort(), - } return &AppAccount{ - BaseAccount: baseAcc, - Name: ga.Name, + Name: ga.Name, + BaseAccount: auth.BaseAccount{ + Address: ga.Address, + Coins: ga.Coins.Sort(), + }, }, nil } diff --git a/server/init.go b/server/init.go index ed1727e35..8beeb354e 100644 --- a/server/init.go +++ b/server/init.go @@ -444,7 +444,7 @@ func SimpleAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState j "coins": [ { "denom": "mycoin", - "amount": 9007199254740992 + "amount": "9007199254740992" } ] }]