diff --git a/app/app.go b/app/app.go index 300e1b9b6469..3f190463b89d 100644 --- a/app/app.go +++ b/app/app.go @@ -261,6 +261,8 @@ func (app *App) runTx(isCheckTx bool, txBytes []byte) (result sdk.Result) { } } + // TODO: override default ante handler w/ custom ante handler. + // Run the ante handler. ctx, result, abort := app.defaultAnteHandler(ctx, tx) if isCheckTx || abort { diff --git a/examples/basecoin/app/basecoin_app.go b/examples/basecoin/app/basecoin_app.go new file mode 100644 index 000000000000..a56e184662e1 --- /dev/null +++ b/examples/basecoin/app/basecoin_app.go @@ -0,0 +1,97 @@ +package app + +import ( + "fmt" + "os" + + apm "github.com/cosmos/cosmos-sdk/app" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/abci/server" + "github.com/tendermint/go-wire" + cmn "github.com/tendermint/tmlibs/common" +) + +const appName = "BasecoinApp" + +type BasecoinApp struct { + *apm.App + cdc *wire.Codec + multiStore sdk.CommitMultiStore + + // The key to access the substores. + mainStoreKey *sdk.KVStoreKey + ibcStoreKey *sdk.KVStoreKey + + // Additional stores: + accStore sdk.AccountStore +} + +// TODO: This should take in more configuration options. +func NewBasecoinApp() *BasecoinApp { + + // Create and configure app. + var app = &BasecoinApp{} + app.initKeys() + app.initMultiStore() + app.initAppStore() + app.initSDKApp() + app.initCodec() + app.initTxDecoder() + app.initAnteHandler() + app.initRoutes() + + // TODO: Load genesis + // TODO: InitChain with validators + // TODO: Set the genesis accounts + app.loadStores() + + return app +} + +func (app *BasecoinApp) RunForever() { + + // 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() + }) + +} + +func (app *BasecoinApp) initKeys() { + app.mainStoreKey = sdk.NewKVStoreKey("main") + app.ibcStoreKey = sdk.NewKVStoreKey("ibc") +} + +// depends on initMultiStore() +func (app *BasecoinApp) initSDKApp() { + app.App = apm.NewApp(appName, app.multiStore) +} + +func (app *BasecoinApp) initCodec() { + app.cdc = wire.NewCodec() +} + +// depends on initSDKApp() +func (app *BasecoinApp) initTxDecoder() { + app.App.SetTxDecoder(app.decodeTx) +} + +// initAnteHandler defined in app/routes.go +// initRoutes defined in app/routes.go + +// Load the stores. +func (app *BasecoinApp) loadStores() { + if err := app.LoadLatestVersion(app.mainStoreKey); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/examples/basecoin/app/msgs.go b/examples/basecoin/app/msgs.go new file mode 100644 index 000000000000..43fd04539df0 --- /dev/null +++ b/examples/basecoin/app/msgs.go @@ -0,0 +1,28 @@ +package app + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank" +) + +// Set via `app.App.SetTxDecoder(app.decodeTx)` +func (app *BasecoinApp) decodeTx(txBytes []byte) (sdk.Tx, error) { + var tx = sdk.StdTx{} + err := app.cdc.UnmarshalBinary(txBytes, &tx) + return tx, err +} + +// Wire requires registration of interfaces & concrete types. +func (app *BasecoinApp) registerMsgs() { + cdc := app.cdc + + // Register the Msg interface. + cdc.RegisterInterface((*sdk.Msg)(nil), nil) + cdc.RegisterConcrete(bank.SendMsg{}, "cosmos-sdk/SendMsg", nil) // XXX refactor out + cdc.RegisterConcrete(bank.IssueMsg{}, "cosmos-sdk/IssueMsg", nil) // XXX refactor out to bank/msgs.go + // more msgs here... + + // All interfaces to be encoded/decoded in a Msg must be + // registered here, along with all the concrete types that + // implement them. +} diff --git a/examples/basecoin/app/routes.go b/examples/basecoin/app/routes.go new file mode 100644 index 000000000000..b045daef5f03 --- /dev/null +++ b/examples/basecoin/app/routes.go @@ -0,0 +1,22 @@ +package app + +import ( + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" +) + +// Handle charging tx fees and checking signatures. +func (app *BasecoinApp) initAnteHandler() { + var authAnteHandler = auth.NewAnteHandler(app.accStore) + app.App.SetDefaultAnteHandler(authAnteHandler) +} + +// Constructs router to route handling of msgs. +func (app *BasecoinApp) initRoutes() { + var router = app.App.Router() + // var multiStore = app.multiStore + var accStore = app.accStore + + router.AddRoute("bank", bank.NewHandler(accStore)) + // more routes here... (order matters) +} diff --git a/examples/basecoin/app/stores.go b/examples/basecoin/app/stores.go new file mode 100644 index 000000000000..acd35d69f812 --- /dev/null +++ b/examples/basecoin/app/stores.go @@ -0,0 +1,45 @@ +package app + +import ( + "fmt" + "os" + + "github.com/cosmos/cosmos-sdk/examples/basecoin/types" + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/x/auth" + dbm "github.com/tendermint/tmlibs/db" +) + +// depends on initKeys() +func (app *BasecoinApp) initMultiStore() { + + // Create the underlying leveldb datastore which will + // persist the Merkle tree inner & leaf nodes. + db, err := dbm.NewGoLevelDB("basecoin", "basecoin-data") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Create CommitStoreLoader. + cacheSize := 10000 + numHistory := int64(100) + mainLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory) + ibcLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory) + + // Create MultiStore + multiStore := store.NewCommitMultiStore(db) + multiStore.SetSubstoreLoader(app.mainStoreKey, mainLoader) + multiStore.SetSubstoreLoader(app.ibcStoreKey, ibcLoader) + + // Finally, + app.multiStore = multiStore +} + +// depends on initKeys() +func (app *BasecoinApp) initAppStore() { + app.accStore = auth.NewAccountStore( + app.mainStoreKey, + types.AppAccountCodec{}, + ) +} diff --git a/examples/basecoin/main.go b/examples/basecoin/main.go deleted file mode 100644 index 5c7c219e4483..000000000000 --- a/examples/basecoin/main.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/cosmos/cosmos-sdk/app" - "github.com/cosmos/cosmos-sdk/store" - 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" - "github.com/tendermint/go-wire" - cmn "github.com/tendermint/tmlibs/common" - dbm "github.com/tendermint/tmlibs/db" - - bcm "github.com/cosmos/cosmos-sdk/examples/basecoin/types" -) - -func main() { - - // Create the underlying leveldb datastore which will - // persist the Merkle tree inner & leaf nodes. - db, err := dbm.NewGoLevelDB("basecoin", "basecoin-data") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Create CommitStoreLoader. - cacheSize := 10000 - numHistory := int64(100) - mainLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory) - ibcLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory) - - // The key to access the main KVStore. - var mainStoreKey = sdk.NewKVStoreKey("main") - var ibcStoreKey = sdk.NewKVStoreKey("ibc") - - // Create MultiStore - multiStore := store.NewCommitMultiStore(db) - multiStore.SetSubstoreLoader(mainStoreKey, mainLoader) - multiStore.SetSubstoreLoader(ibcStoreKey, ibcLoader) - - // Create the Application. - app := app.NewApp("basecoin", multiStore) - - // Set Tx decoder - app.SetTxDecoder(decodeTx) - - var accStore = auth.NewAccountStore(mainStoreKey, bcm.AppAccountCodec{}) - var authAnteHandler = auth.NewAnteHandler(accStore) - - // Handle charging fees and checking signatures. - app.SetDefaultAnteHandler(authAnteHandler) - - // Add routes to App. - app.Router().AddRoute("bank", bank.NewHandler(accStore)) - - // TODO: load genesis - // TODO: InitChain with validators - // accounts := auth.NewAccountStore(multiStore.GetKVStore("main")) - // TODO: set the genesis accounts - - // Load the stores. - if err := app.LoadLatestVersion(mainStoreKey); err != nil { - fmt.Println(err) - os.Exit(1) - } - - // 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() - }) - return -} - -//---------------------------------------- -// Misc. - -func registerMsgs() { - wire.RegisterInterface((*sdk.Msg)(nil), nil) - wire.RegisterConcrete(&bank.SendMsg{}, "com.cosmos.basecoin.send_msg", nil) -} - -func decodeTx(txBytes []byte) (sdk.Tx, error) { - var tx = sdk.StdTx{} - err := wire.UnmarshalBinary(txBytes, &tx) - return tx, err -} diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 944cea4a0054..0fb8993f0124 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -39,6 +39,9 @@ func NewCommitMultiStore(db dbm.DB) *rootMultiStore { // Implements CommitMultiStore. func (rs *rootMultiStore) SetSubstoreLoader(key SubstoreKey, loader CommitStoreLoader) { + if key == nil { + panic("SetSubstoreLoader() key cannot be nil") + } if _, ok := rs.storeLoaders[key]; ok { panic(fmt.Sprintf("rootMultiStore duplicate substore key", key)) } diff --git a/types/store.go b/types/store.go index 8e6d9bf4665a..a17f703b6c3d 100644 --- a/types/store.go +++ b/types/store.go @@ -54,9 +54,11 @@ type CommitMultiStore interface { MultiStore // Add a substore loader. + // Panics on a nil key. SetSubstoreLoader(key SubstoreKey, loader CommitStoreLoader) // Gets the substore, which is a CommitSubstore. + // Panics on a nil key. GetSubstore(key SubstoreKey) CommitStore // Load the latest persisted version. diff --git a/x/auth/ante.go b/x/auth/ante.go index 9a9ff92ddbe4..312f20470820 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -55,7 +55,7 @@ func NewAnteHandler(store sdk.AccountStore) sdk.AnteHandler { } } - // Check and incremenet sequence number. + // Check and increment sequence number. seq := signerAcc.GetSequence() if seq != sig.Sequence { return ctx, sdk.Result{ diff --git a/x/bank/handler.go b/x/bank/handler.go index 825dd8570313..63198cb6e10e 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -2,38 +2,56 @@ package bank import ( sdk "github.com/cosmos/cosmos-sdk/types" + "reflect" ) +// Handle all "bank" type messages. func NewHandler(accStore sdk.AccountStore) sdk.Handler { return func(ctx sdk.Context, tx sdk.Tx) sdk.Result { cs := CoinStore{accStore} - - sendTx, ok := tx.(sdk.Msg).(SendMsg) - if !ok { - panic("tx is not SendTx") // ? + msg := tx.(sdk.Msg) + switch msg := msg.(type) { + case SendMsg: + return handleSendMsg(ctx, cs, msg) + case IssueMsg: + return handleIssueMsg(ctx, cs, msg) + default: + return sdk.Result{ + Code: 1, // TODO + Log: "Unrecognized bank Tx type: " + reflect.TypeOf(tx).Name(), + } } + } + +} - // NOTE: totalIn == totalOut should already have been checked +// Handle SendMsg. +func handleSendMsg(ctx sdk.Context, cs CoinStore, msg SendMsg) sdk.Result { + // NOTE: totalIn == totalOut should already have been checked - for _, in := range sendTx.Inputs { - _, err := cs.SubtractCoins(ctx, in.Address, in.Coins) - if err != nil { - return sdk.Result{ - Code: 1, // TODO - } + for _, in := range msg.Inputs { + _, err := cs.SubtractCoins(ctx, in.Address, in.Coins) + if err != nil { + return sdk.Result{ + Code: 1, // TODO } } + } - for _, out := range sendTx.Outputs { - _, err := cs.AddCoins(ctx, out.Address, out.Coins) - if err != nil { - return sdk.Result{ - Code: 1, // TODO - } + for _, out := range msg.Outputs { + _, err := cs.AddCoins(ctx, out.Address, out.Coins) + if err != nil { + return sdk.Result{ + Code: 1, // TODO } } - - return sdk.Result{} // TODO } + + return sdk.Result{} // TODO +} + +// Handle IssueMsg. +func handleIssueMsg(ctx sdk.Context, cs CoinStore, msg IssueMsg) sdk.Result { + panic("not implemented yet") } diff --git a/x/bank/tx.go b/x/bank/tx.go index 150b9b9bf1aa..eed7f57e705c 100644 --- a/x/bank/tx.go +++ b/x/bank/tx.go @@ -81,6 +81,60 @@ func (msg SendMsg) GetSigners() []crypto.Address { return addrs } +//---------------------------------------- +// IssueMsg + +// IssueMsg - high level transaction of the coin module +type IssueMsg struct { + Banker crypto.Address `json:"banker"` + Outputs []Output `json:"outputs"` +} + +// NewIssueMsg - construct arbitrary multi-in, multi-out send msg. +func NewIssueMsg(banker crypto.Address, out []Output) IssueMsg { + return IssueMsg{Banker: banker, Outputs: out} +} + +// Implements Msg. +func (msg IssueMsg) Type() string { return "bank" } // TODO: "bank/send" + +// Implements Msg. +func (msg IssueMsg) ValidateBasic() error { + // XXX + if len(msg.Outputs) == 0 { + return ErrNoOutputs() + } + for _, out := range msg.Outputs { + if err := out.ValidateBasic(); err != nil { + return err + } + } + return nil +} + +func (msg IssueMsg) String() string { + return fmt.Sprintf("IssueMsg{%v#%v}", msg.Banker, msg.Outputs) +} + +// Implements Msg. +func (msg IssueMsg) Get(key interface{}) (value interface{}) { + return nil +} + +// Implements Msg. +func (msg IssueMsg) GetSignBytes() []byte { + b, err := json.Marshal(msg) // XXX: ensure some canonical form + if err != nil { + panic(err) + } + return b +} + +// Implements Msg. +func (msg IssueMsg) GetSigners() []crypto.Address { + return []crypto.Address{msg.Banker} +} + //---------------------------------------- // Input