cosmos-sdk/docs/_attic/sdk/core/examples/app1.go

234 lines
5.7 KiB
Go
Raw Normal View History

2018-06-25 18:55:23 -07:00
package app
import (
"encoding/json"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
2018-06-25 18:55:23 -07:00
bapp "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
2018-06-25 18:55:23 -07:00
)
const (
2018-11-16 09:12:24 -08:00
app1Name = "App1"
bankCodespace = "BANK"
2018-06-25 18:55:23 -07:00
)
func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp {
2018-06-25 18:55:23 -07:00
// Create the base application object.
2018-07-18 16:24:16 -07:00
app := bapp.NewBaseApp(app1Name, logger, db, tx1Decoder)
2018-06-25 18:55:23 -07:00
// Create a key for accessing the account store.
keyAccount := sdk.NewKVStoreKey(auth.StoreKey)
2018-06-25 18:55:23 -07:00
// Register message routes.
// Note the handler gets access to the account store.
app.Router().
AddRoute("send", handleMsgSend(keyAccount))
2018-06-25 18:55:23 -07:00
// Mount stores and load the latest state.
app.MountStoresIAVL(keyAccount)
err := app.LoadLatestVersion(keyAccount)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
//------------------------------------------------------------------
// Msg
// MsgSend implements sdk.Msg
var _ sdk.Msg = MsgSend{}
// MsgSend to send coins from Input to Output
type MsgSend struct {
2018-07-06 00:06:53 -07:00
From sdk.AccAddress `json:"from"`
To sdk.AccAddress `json:"to"`
Amount sdk.Coins `json:"amount"`
2018-06-25 18:55:23 -07:00
}
// NewMsgSend
2018-07-06 00:06:53 -07:00
func NewMsgSend(from, to sdk.AccAddress, amt sdk.Coins) MsgSend {
2018-06-25 18:55:23 -07:00
return MsgSend{from, to, amt}
}
// Implements Msg.
// nolint
func (msg MsgSend) Route() string { return "send" }
func (msg MsgSend) Type() string { return "send" }
2018-06-25 18:55:23 -07:00
// Implements Msg. Ensure the addresses are good and the
// amount is positive.
func (msg MsgSend) ValidateBasic() sdk.Error {
if len(msg.From) == 0 {
return sdk.ErrInvalidAddress("From address is empty")
}
if len(msg.To) == 0 {
return sdk.ErrInvalidAddress("To address is empty")
}
if !msg.Amount.IsPositive() {
return sdk.ErrInvalidCoins("Amount is not positive")
}
return nil
}
// Implements Msg. JSON encode the message.
func (msg MsgSend) GetSignBytes() []byte {
bz, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return sdk.MustSortJSON(bz)
2018-06-25 18:55:23 -07:00
}
// Implements Msg. Return the signer.
2018-07-06 00:06:53 -07:00
func (msg MsgSend) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.From}
2018-06-25 18:55:23 -07:00
}
2018-06-27 04:51:46 -07:00
// Returns the sdk.Tags for the message
func (msg MsgSend) Tags() sdk.Tags {
return sdk.NewTags("sender", []byte(msg.From.String())).
AppendTag("receiver", []byte(msg.To.String()))
}
2018-06-25 18:55:23 -07:00
//------------------------------------------------------------------
// Handler for the message
// Handle MsgSend.
// NOTE: msg.From, msg.To, and msg.Amount were already validated
2018-06-28 16:06:10 -07:00
// in ValidateBasic().
func handleMsgSend(key *sdk.KVStoreKey) sdk.Handler {
2018-06-25 18:55:23 -07:00
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
sendMsg, ok := msg.(MsgSend)
if !ok {
// Create custom error message and return result
// Note: Using unreserved error codespace
2018-11-16 09:12:24 -08:00
return sdk.NewError(bankCodespace, 1, "MsgSend is malformed").Result()
2018-06-25 18:55:23 -07:00
}
// Load the store.
store := ctx.KVStore(key)
2018-06-27 04:51:46 -07:00
// Debit from the sender.
if res := handleFrom(store, sendMsg.From, sendMsg.Amount); !res.IsOK() {
return res
}
2018-06-27 04:51:46 -07:00
// Credit the receiver.
if res := handleTo(store, sendMsg.To, sendMsg.Amount); !res.IsOK() {
return res
}
// Return a success (Code 0).
// Add list of key-value pair descriptors ("tags").
return sdk.Result{
Tags: sendMsg.Tags(),
}
2018-06-27 04:51:46 -07:00
}
}
// Convenience Handlers
2018-07-06 00:06:53 -07:00
func handleFrom(store sdk.KVStore, from sdk.AccAddress, amt sdk.Coins) sdk.Result {
2018-06-27 04:51:46 -07:00
// Get sender account from the store.
accBytes := store.Get(from)
if accBytes == nil {
2018-06-26 11:12:44 -07:00
// Account was not added to store. Return the result of the error.
2018-11-16 09:12:24 -08:00
return sdk.NewError(bankCodespace, 101, "Account not added to store").Result()
2018-06-25 18:55:23 -07:00
}
2018-06-27 04:51:46 -07:00
// Unmarshal the JSON account bytes.
2018-06-27 04:59:20 -07:00
var acc appAccount
2018-06-27 04:51:46 -07:00
err := json.Unmarshal(accBytes, &acc)
2018-06-25 18:55:23 -07:00
if err != nil {
// InternalError
2018-06-26 11:12:44 -07:00
return sdk.ErrInternal("Error when deserializing account").Result()
2018-06-25 18:55:23 -07:00
}
2018-06-27 04:51:46 -07:00
// Deduct msg amount from sender account.
senderCoins := acc.Coins.Sub(amt)
2018-06-26 11:12:44 -07:00
// If any coin has negative amount, return insufficient coins error.
if senderCoins.IsAnyNegative() {
2018-06-26 11:12:44 -07:00
return sdk.ErrInsufficientCoins("Insufficient coins in account").Result()
}
2018-06-27 04:51:46 -07:00
// Set acc coins to new amount.
2018-06-26 11:12:44 -07:00
acc.Coins = senderCoins
2018-06-27 04:51:46 -07:00
// Encode sender account.
accBytes, err = json.Marshal(acc)
2018-06-26 11:12:44 -07:00
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
// Update store with updated sender account
2018-06-27 04:51:46 -07:00
store.Set(from, accBytes)
return sdk.Result{}
}
2018-06-26 11:12:44 -07:00
2018-07-06 00:06:53 -07:00
func handleTo(store sdk.KVStore, to sdk.AccAddress, amt sdk.Coins) sdk.Result {
2018-06-26 11:12:44 -07:00
// Add msg amount to receiver account
2018-06-27 04:51:46 -07:00
accBytes := store.Get(to)
2018-06-27 04:59:20 -07:00
var acc appAccount
2018-06-27 04:51:46 -07:00
if accBytes == nil {
2018-06-26 19:09:54 -07:00
// Receiver account does not already exist, create a new one.
2018-06-27 04:59:20 -07:00
acc = appAccount{}
2018-06-26 11:12:44 -07:00
} else {
2018-06-26 19:09:54 -07:00
// Receiver account already exists. Retrieve and decode it.
2018-06-27 04:51:46 -07:00
err := json.Unmarshal(accBytes, &acc)
2018-06-26 11:12:44 -07:00
if err != nil {
return sdk.ErrInternal("Account decoding error").Result()
}
}
// Add amount to receiver's old coins
receiverCoins := acc.Coins.Add(amt)
2018-06-26 11:12:44 -07:00
// Update receiver account
2018-06-27 04:51:46 -07:00
acc.Coins = receiverCoins
2018-06-26 11:12:44 -07:00
// Encode receiver account
2018-06-27 04:51:46 -07:00
accBytes, err := json.Marshal(acc)
2018-06-26 11:12:44 -07:00
if err != nil {
return sdk.ErrInternal("Account encoding error").Result()
}
2018-06-27 04:51:46 -07:00
// Update store with updated receiver account
store.Set(to, accBytes)
return sdk.Result{}
2018-06-25 18:55:23 -07:00
}
// Simple account struct
2018-06-27 04:59:20 -07:00
type appAccount struct {
2018-06-25 18:55:23 -07:00
Coins sdk.Coins `json:"coins"`
}
//------------------------------------------------------------------
// Tx
// Simple tx to wrap the Msg.
type app1Tx struct {
2018-06-25 18:55:23 -07:00
MsgSend
}
// This tx only has one Msg.
func (tx app1Tx) GetMsgs() []sdk.Msg {
2018-06-25 18:55:23 -07:00
return []sdk.Msg{tx.MsgSend}
}
// JSON decode MsgSend.
func tx1Decoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx app1Tx
2018-06-25 18:55:23 -07:00
err := json.Unmarshal(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode(err.Error())
}
return tx, nil
}