Merge pull request #1434 from cosmos/bucky-aditya/docs-core
Bucky aditya/docs core
This commit is contained in:
commit
3a96f8ffd1
|
@ -2,7 +2,6 @@ package app
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
|
@ -35,7 +34,7 @@ func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
// Register message routes.
|
||||
// Note the handler gets access to the account store.
|
||||
app.Router().
|
||||
AddRoute("bank", NewApp1Handler(keyAccount))
|
||||
AddRoute("send", handleMsgSend(keyAccount))
|
||||
|
||||
// Mount stores and load the latest state.
|
||||
app.MountStoresIAVL(keyAccount)
|
||||
|
@ -65,7 +64,7 @@ func NewMsgSend(from, to sdk.Address, amt sdk.Coins) MsgSend {
|
|||
}
|
||||
|
||||
// Implements Msg.
|
||||
func (msg MsgSend) Type() string { return "bank" }
|
||||
func (msg MsgSend) Type() string { return "send" }
|
||||
|
||||
// Implements Msg. Ensure the addresses are good and the
|
||||
// amount is positive.
|
||||
|
@ -105,41 +104,40 @@ func (msg MsgSend) Tags() sdk.Tags {
|
|||
//------------------------------------------------------------------
|
||||
// Handler for the message
|
||||
|
||||
func NewApp1Handler(keyAcc *sdk.KVStoreKey) sdk.Handler {
|
||||
// Handle MsgSend.
|
||||
// NOTE: msg.From, msg.To, and msg.Amount were already validated
|
||||
func handleMsgSend(key *sdk.KVStoreKey) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MsgSend:
|
||||
return handleMsgSend(ctx, keyAcc, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
sendMsg, ok := msg.(MsgSend)
|
||||
if !ok {
|
||||
// Create custom error message and return result
|
||||
// Note: Using unreserved error codespace
|
||||
return sdk.NewError(2, 1, "Send Message is malformed").Result()
|
||||
}
|
||||
|
||||
|
||||
// Load the store.
|
||||
store := ctx.KVStore(key)
|
||||
|
||||
// Debit from the sender.
|
||||
if res := handleFrom(store, sendMsg.From, sendMsg.Amount); !res.IsOK() {
|
||||
return res
|
||||
}
|
||||
|
||||
// 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle MsgSend.
|
||||
// NOTE: msg.From, msg.To, and msg.Amount were already validated
|
||||
func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result {
|
||||
// Load the store.
|
||||
store := ctx.KVStore(key)
|
||||
|
||||
// Debit from the sender.
|
||||
if res := handleFrom(store, msg.From, msg.Amount); !res.IsOK() {
|
||||
return res
|
||||
}
|
||||
|
||||
// Credit the receiver.
|
||||
if res := handleTo(store, msg.To, msg.Amount); !res.IsOK() {
|
||||
return res
|
||||
}
|
||||
|
||||
// Return a success (Code 0).
|
||||
// Add list of key-value pair descriptors ("tags").
|
||||
return sdk.Result{
|
||||
Tags: msg.Tags(),
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience Handlers
|
||||
func handleFrom(store sdk.KVStore, from sdk.Address, amt sdk.Coins) sdk.Result {
|
||||
// Get sender account from the store.
|
||||
accBytes := store.Get(from)
|
||||
|
@ -210,6 +208,7 @@ func handleTo(store sdk.KVStore, to sdk.Address, amt sdk.Coins) sdk.Result {
|
|||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// Simple account struct
|
||||
type appAccount struct {
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
}
|
||||
|
|
|
@ -49,7 +49,8 @@ func NewApp2(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
// Register message routes.
|
||||
// Note the handler gets access to the account store.
|
||||
app.Router().
|
||||
AddRoute("bank", NewApp2Handler(keyAccount, keyMain))
|
||||
AddRoute("send", handleMsgSend(keyAccount)).
|
||||
AddRoute("issue", handleMsgIssue(keyAccount, keyMain))
|
||||
|
||||
// Mount stores and load the latest state.
|
||||
app.MountStoresIAVL(keyAccount, keyMain)
|
||||
|
@ -71,39 +72,37 @@ type CoinMetadata struct {
|
|||
//------------------------------------------------------------------
|
||||
// Msgs
|
||||
|
||||
// Create Output struct to allow single message to issue arbitrary coins to multiple users
|
||||
type Output struct {
|
||||
Address sdk.Address
|
||||
Coins sdk.Coins
|
||||
}
|
||||
|
||||
// Single permissioned issuer can issue multiple outputs
|
||||
// Single permissioned issuer can issue Coin to Receiver
|
||||
// if he is the issuer in Coin Metadata
|
||||
// Implements sdk.Msg Interface
|
||||
type MsgIssue struct {
|
||||
Issuer sdk.Address
|
||||
Outputs []Output
|
||||
Receiver sdk.Address
|
||||
Coin sdk.Coin
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (msg MsgIssue) Type() string { return "bank" }
|
||||
// Implements Msg.
|
||||
func (msg MsgIssue) Type() string { return "issue" }
|
||||
|
||||
// Implements Msg. Ensures addresses are valid and Coin is positive
|
||||
func (msg MsgIssue) ValidateBasic() sdk.Error {
|
||||
if len(msg.Issuer) == 0 {
|
||||
return sdk.ErrInvalidAddress("Issuer address cannot be empty")
|
||||
}
|
||||
|
||||
for _, o := range msg.Outputs {
|
||||
if len(o.Address) == 0 {
|
||||
return sdk.ErrInvalidAddress("Output address cannot be empty")
|
||||
}
|
||||
// Cannot issue zero or negative coins
|
||||
if !o.Coins.IsPositive() {
|
||||
return sdk.ErrInvalidCoins("Cannot issue 0 or negative coin amounts")
|
||||
}
|
||||
if len(msg.Receiver) == 0 {
|
||||
return sdk.ErrInvalidAddress("Receiver address cannot be empty")
|
||||
}
|
||||
|
||||
// Cannot issue zero or negative coins
|
||||
if !msg.Coin.IsPositive() {
|
||||
return sdk.ErrInvalidCoins("Cannot issue 0 or negative coin amounts")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements Msg. Get canonical sign bytes for MsgIssue
|
||||
func (msg MsgIssue) GetSignBytes() []byte {
|
||||
bz, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
|
@ -117,112 +116,93 @@ func (msg MsgIssue) GetSigners() []sdk.Address {
|
|||
return []sdk.Address{msg.Issuer}
|
||||
}
|
||||
|
||||
// Returns the sdk.Tags for the message
|
||||
func (msg MsgIssue) Tags() sdk.Tags {
|
||||
return sdk.NewTags("issuer", []byte(msg.Issuer.String())).
|
||||
AppendTag("receiver", []byte(msg.Receiver.String()))
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Handler for the message
|
||||
|
||||
func NewApp2Handler(keyAcc *sdk.KVStoreKey, keyMain *sdk.KVStoreKey) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MsgSend:
|
||||
return handleMsgSend(ctx, keyAcc, msg)
|
||||
case MsgIssue:
|
||||
return handleMsgIssue(ctx, keyMain, keyAcc, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Msg Issue
|
||||
func handleMsgIssue(ctx sdk.Context, keyMain *sdk.KVStoreKey, keyAcc *sdk.KVStoreKey, msg MsgIssue) sdk.Result {
|
||||
store := ctx.KVStore(keyMain)
|
||||
accStore := ctx.KVStore(keyAcc)
|
||||
|
||||
for _, o := range msg.Outputs {
|
||||
for _, coin := range o.Coins {
|
||||
bz := store.Get([]byte(coin.Denom))
|
||||
var metadata CoinMetadata
|
||||
|
||||
if bz == nil {
|
||||
// Coin not set yet, initialize with issuer and default values
|
||||
// Coin amount can't be above default value
|
||||
if coin.Amount.GT(sdk.NewInt(1000000)) {
|
||||
return sdk.ErrInvalidCoins("Cannot issue that many new coins").Result()
|
||||
}
|
||||
metadata = CoinMetadata{
|
||||
TotalSupply: sdk.NewInt(1000000),
|
||||
CurrentSupply: sdk.NewInt(0),
|
||||
Issuer: msg.Issuer,
|
||||
Decimal: 10,
|
||||
}
|
||||
} else {
|
||||
// Decode coin metadata
|
||||
err := json.Unmarshal(bz, &metadata)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal("Decoding coin metadata failed").Result()
|
||||
}
|
||||
}
|
||||
|
||||
// Return error result if msg Issuer is not equal to coin issuer
|
||||
if !reflect.DeepEqual(metadata.Issuer, msg.Issuer) {
|
||||
return sdk.ErrUnauthorized(fmt.Sprintf("Msg issuer cannot issue these coins: %s", coin.Denom)).Result()
|
||||
}
|
||||
|
||||
// Issuer cannot issue more than remaining supply
|
||||
issuerSupply := metadata.TotalSupply.Sub(metadata.CurrentSupply)
|
||||
if coin.Amount.GT(issuerSupply) {
|
||||
return sdk.ErrInsufficientCoins(fmt.Sprintf("Issuer cannot issue that many coins. Current issuer supply: %d", issuerSupply.Int64())).Result()
|
||||
}
|
||||
|
||||
// Update coin metadata
|
||||
metadata.CurrentSupply = metadata.CurrentSupply.Add(coin.Amount)
|
||||
|
||||
val, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal("Encoding coin metadata failed").Result()
|
||||
}
|
||||
|
||||
// Update coin metadata in store
|
||||
store.Set([]byte(coin.Denom), val)
|
||||
func handleMsgIssue(keyMain *sdk.KVStoreKey, keyAcc *sdk.KVStoreKey) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
issueMsg, ok := msg.(MsgIssue)
|
||||
if !ok {
|
||||
return sdk.NewError(2, 1, "IssueMsg is malformed").Result()
|
||||
}
|
||||
|
||||
// Add coins to receiver account
|
||||
bz := accStore.Get(o.Address)
|
||||
var acc appAccount
|
||||
if bz == nil {
|
||||
// Receiver account does not already exist, create a new one.
|
||||
acc = appAccount{}
|
||||
} else {
|
||||
// Receiver account already exists. Retrieve and decode it.
|
||||
err := json.Unmarshal(bz, &acc)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal("Account decoding error").Result()
|
||||
}
|
||||
// Retrieve stores
|
||||
store := ctx.KVStore(keyMain)
|
||||
accStore := ctx.KVStore(keyAcc)
|
||||
|
||||
// Handle updating metadata
|
||||
if res := handleMetaData(store, issueMsg.Issuer, issueMsg.Coin); !res.IsOK() {
|
||||
return res
|
||||
}
|
||||
|
||||
// Add amount to receiver's old coins
|
||||
receiverCoins := acc.Coins.Plus(o.Coins)
|
||||
|
||||
// Update receiver account
|
||||
acc.Coins = receiverCoins
|
||||
|
||||
// Encode receiver account
|
||||
val, err := json.Marshal(acc)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal("Account encoding error").Result()
|
||||
// Issue coins to receiver using previously defined handleTo function
|
||||
if res := handleTo(accStore, issueMsg.Receiver, []sdk.Coin{issueMsg.Coin}); !res.IsOK() {
|
||||
return res
|
||||
}
|
||||
|
||||
// set account with new issued coins in store
|
||||
store.Set(o.Address, val)
|
||||
return sdk.Result{
|
||||
// Return result with Issue msg tags
|
||||
Tags: issueMsg.Tags(),
|
||||
}
|
||||
}
|
||||
|
||||
return sdk.Result{
|
||||
// TODO: Tags
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handleMetaData(store sdk.KVStore, issuer sdk.Address, coin sdk.Coin) sdk.Result {
|
||||
bz := store.Get([]byte(coin.Denom))
|
||||
var metadata CoinMetadata
|
||||
|
||||
if bz == nil {
|
||||
// Coin not set yet, initialize with issuer and default values
|
||||
// Coin amount can't be above default value
|
||||
if coin.Amount.GT(sdk.NewInt(1000000)) {
|
||||
return sdk.ErrInvalidCoins("Cannot issue that many new coins").Result()
|
||||
}
|
||||
metadata = CoinMetadata{
|
||||
TotalSupply: sdk.NewInt(1000000),
|
||||
CurrentSupply: sdk.NewInt(0),
|
||||
Issuer: issuer,
|
||||
Decimal: 10,
|
||||
}
|
||||
} else {
|
||||
// Decode coin metadata
|
||||
err := json.Unmarshal(bz, &metadata)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal("Decoding coin metadata failed").Result()
|
||||
}
|
||||
}
|
||||
|
||||
// Msg Issuer is not authorized to issue these coins
|
||||
if !reflect.DeepEqual(metadata.Issuer, issuer) {
|
||||
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
|
||||
}
|
||||
|
||||
// Update coin current circulating supply
|
||||
metadata.CurrentSupply = metadata.CurrentSupply.Add(coin.Amount)
|
||||
|
||||
// Current supply cannot exceed total supply
|
||||
if metadata.TotalSupply.LT(metadata.CurrentSupply) {
|
||||
return sdk.ErrInsufficientCoins("Issuer cannot issue more than total supply of coin").Result()
|
||||
}
|
||||
|
||||
val, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal(fmt.Sprintf("Error encoding metadata: %s", err.Error())).Result()
|
||||
}
|
||||
|
||||
// Update store with new metadata
|
||||
store.Set([]byte(coin.Denom), val)
|
||||
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Tx
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ package app
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"fmt"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
|
@ -44,7 +44,8 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
// Register message routes.
|
||||
// Note the handler gets access to the account store.
|
||||
app.Router().
|
||||
AddRoute("bank", NewApp3Handler(accountKeeper, metadataMapper))
|
||||
AddRoute("send", betterHandleMsgSend(accountKeeper)).
|
||||
AddRoute("issue", betterHandleMsgIssue(metadataMapper, accountKeeper))
|
||||
|
||||
// Mount stores and load the latest state.
|
||||
app.MountStoresIAVL(keyAccount, keyMain, keyFees)
|
||||
|
@ -55,91 +56,105 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
return app
|
||||
}
|
||||
|
||||
func NewApp3Handler(accountKeeper bank.Keeper, metadataMapper MetaDataMapper) sdk.Handler {
|
||||
func betterHandleMsgSend(accountKeeper bank.Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MsgSend:
|
||||
return betterHandleMsgSend(ctx, accountKeeper, msg)
|
||||
case MsgIssue:
|
||||
return betterHandleMsgIssue(ctx, metadataMapper, accountKeeper, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
sendMsg, ok := msg.(MsgSend)
|
||||
if !ok {
|
||||
return sdk.NewError(2, 1, "Send Message Malformed").Result()
|
||||
}
|
||||
|
||||
// Subtract coins from sender account
|
||||
_, _, err := accountKeeper.SubtractCoins(ctx, sendMsg.From, sendMsg.Amount)
|
||||
if err != nil {
|
||||
// if error, return its result
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
// Add coins to receiver account
|
||||
_, _, err = accountKeeper.AddCoins(ctx, sendMsg.To, sendMsg.Amount)
|
||||
if err != nil {
|
||||
// if error, return its result
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
return sdk.Result{
|
||||
Tags: sendMsg.Tags(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func betterHandleMsgSend(ctx sdk.Context, accountKeeper bank.Keeper, msg MsgSend) sdk.Result {
|
||||
// Subtract coins from sender account
|
||||
_, _, err := accountKeeper.SubtractCoins(ctx, msg.From, msg.Amount)
|
||||
if err != nil {
|
||||
// if error, return its result
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
// Add coins to receiver account
|
||||
_, _, err = accountKeeper.AddCoins(ctx, msg.To, msg.Amount)
|
||||
if err != nil {
|
||||
// if error, return its result
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
func betterHandleMsgIssue(ctx sdk.Context, metadataMapper MetaDataMapper, accountKeeper bank.Keeper, msg MsgIssue) sdk.Result {
|
||||
for _, o := range msg.Outputs {
|
||||
for _, coin := range o.Coins {
|
||||
metadata := metadataMapper.GetMetaData(ctx, coin.Denom)
|
||||
if len(metadata.Issuer) == 0 {
|
||||
// coin doesn't have issuer yet, set issuer to msg issuer
|
||||
metadata.Issuer = msg.Issuer
|
||||
}
|
||||
|
||||
// Check that msg Issuer is authorized to issue these coins
|
||||
if !reflect.DeepEqual(metadata.Issuer, msg.Issuer) {
|
||||
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue these coins: %s", coin.Denom)).Result()
|
||||
}
|
||||
|
||||
// Issuer cannot issue more than remaining supply
|
||||
issuerSupply := metadata.TotalSupply.Sub(metadata.CurrentSupply)
|
||||
if coin.Amount.GT(issuerSupply) {
|
||||
return sdk.ErrInsufficientCoins(fmt.Sprintf("Issuer cannot issue that many coins. Current issuer supply: %d", issuerSupply.Int64())).Result()
|
||||
}
|
||||
|
||||
// update metadata current circulating supply
|
||||
metadata.CurrentSupply = metadata.CurrentSupply.Add(coin.Amount)
|
||||
|
||||
metadataMapper.SetMetaData(ctx, coin.Denom, metadata)
|
||||
func betterHandleMsgIssue(metadataMapper MetaDataMapper, accountKeeper bank.Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
issueMsg, ok := msg.(MsgIssue)
|
||||
if !ok {
|
||||
return sdk.NewError(2, 1, "Issue Message Malformed").Result()
|
||||
}
|
||||
|
||||
// Handle updating metadata
|
||||
if res := betterHandleMetaData(ctx, metadataMapper, issueMsg.Issuer, issueMsg.Coin); !res.IsOK() {
|
||||
return res
|
||||
}
|
||||
|
||||
// Add newly issued coins to output address
|
||||
_, _, err := accountKeeper.AddCoins(ctx, o.Address, o.Coins)
|
||||
_, _, err := accountKeeper.AddCoins(ctx, issueMsg.Receiver, []sdk.Coin{issueMsg.Coin})
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
return sdk.Result{
|
||||
// Return result with Issue msg tags
|
||||
Tags: issueMsg.Tags(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func betterHandleMetaData(ctx sdk.Context, metadataMapper MetaDataMapper, issuer sdk.Address, coin sdk.Coin) sdk.Result {
|
||||
metadata := metadataMapper.GetMetaData(ctx, coin.Denom)
|
||||
|
||||
// Metadata was created fresh, should set issuer to msg issuer
|
||||
if len(metadata.Issuer) == 0 {
|
||||
metadata.Issuer = issuer
|
||||
}
|
||||
|
||||
// Msg Issuer is not authorized to issue these coins
|
||||
if !reflect.DeepEqual(metadata.Issuer, issuer) {
|
||||
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
|
||||
}
|
||||
|
||||
// Update current circulating supply
|
||||
metadata.CurrentSupply = metadata.CurrentSupply.Add(coin.Amount)
|
||||
|
||||
// Current supply cannot exceed total supply
|
||||
if metadata.TotalSupply.LT(metadata.CurrentSupply) {
|
||||
return sdk.ErrInsufficientCoins("Issuer cannot issue more than total supply of coin").Result()
|
||||
}
|
||||
|
||||
metadataMapper.SetMetaData(ctx, coin.Denom, metadata)
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Mapper for Coin Metadata
|
||||
// Example of a very simple user-defined mapper
|
||||
|
||||
// Example of a very simple user-defined mapper interface
|
||||
type MetaDataMapper interface {
|
||||
GetMetaData(sdk.Context, string) CoinMetadata
|
||||
SetMetaData(sdk.Context, string, CoinMetadata)
|
||||
}
|
||||
|
||||
// Implements MetaDataMapper
|
||||
type App3MetaDataMapper struct {
|
||||
mainKey *sdk.KVStoreKey
|
||||
}
|
||||
|
||||
// Construct new App3MetaDataMapper
|
||||
func NewApp3MetaDataMapper(key *sdk.KVStoreKey) App3MetaDataMapper {
|
||||
return App3MetaDataMapper{mainKey: key}
|
||||
}
|
||||
|
||||
// Implements MetaDataMpper. Returns metadata for coin
|
||||
// If metadata does not exist in store, function creates default metadata and returns it
|
||||
// without adding it to the store.
|
||||
func (mdm App3MetaDataMapper) GetMetaData(ctx sdk.Context, denom string) CoinMetadata {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
|
@ -160,6 +175,7 @@ func (mdm App3MetaDataMapper) GetMetaData(ctx sdk.Context, denom string) CoinMet
|
|||
return metadata
|
||||
}
|
||||
|
||||
// Implements MetaDataMapper. Sets metadata in store with key equal to denom.
|
||||
func (mdm App3MetaDataMapper) SetMetaData(ctx sdk.Context, denom string, metadata CoinMetadata) {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ func NewApp4(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
// Register message routes.
|
||||
// Note the handler gets access to the account store.
|
||||
app.Router().
|
||||
AddRoute("bank", NewApp4Handler(accountKeeper, metadataMapper))
|
||||
AddRoute("send", betterHandleMsgSend(accountKeeper)).
|
||||
AddRoute("issue", evenBetterHandleMsgIssue(metadataMapper, accountKeeper))
|
||||
|
||||
// Mount stores and load the latest state.
|
||||
app.MountStoresIAVL(keyAccount, keyMain, keyFees)
|
||||
|
@ -58,6 +59,7 @@ func NewApp4(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
|||
return app
|
||||
}
|
||||
|
||||
// Application state at Genesis has accounts with starting balances and coins with starting metadata
|
||||
type GenesisState struct {
|
||||
Accounts []*GenesisAccount `json:"accounts"`
|
||||
Coins []*GenesisCoin `json:"coins"`
|
||||
|
@ -69,6 +71,7 @@ type GenesisAccount struct {
|
|||
Coins sdk.Coins `json:"coins"`
|
||||
}
|
||||
|
||||
// Converts GenesisAccount to auth.BaseAccount for storage in account store
|
||||
func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount, err error) {
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: ga.Address,
|
||||
|
@ -85,6 +88,7 @@ type GenesisCoin struct {
|
|||
Decimal uint64 `json:"decimals"`
|
||||
}
|
||||
|
||||
// Converts GenesisCoin to its denom and metadata for storage in main store
|
||||
func (gc *GenesisCoin) ToMetaData() (string, CoinMetadata) {
|
||||
return gc.Denom, CoinMetadata{
|
||||
Issuer: gc.Issuer,
|
||||
|
@ -128,66 +132,76 @@ func NewInitChainer(cdc *wire.Codec, accountMapper auth.AccountMapper, metadataM
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Now that initializing coin metadata is done in InitChainer we can simplifiy handleMsgIssue
|
||||
// Now that initializing coin metadata is done in InitChainer we can simplify handleMsgIssue
|
||||
|
||||
func NewApp4Handler(accountKeeper bank.Keeper, metadataMapper MetaDataMapper) sdk.Handler {
|
||||
// New MsgIssue handler will no longer generate coin metadata on the fly.
|
||||
// Allows issuers (permissioned at genesis) to issue coin to receiver.
|
||||
func evenBetterHandleMsgIssue(metadataMapper MetaDataMapper, accountKeeper bank.Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MsgSend:
|
||||
return betterHandleMsgSend(ctx, accountKeeper, msg)
|
||||
case MsgIssue:
|
||||
// use new MsgIssue handler
|
||||
return evenBetterHandleMsgIssue(ctx, metadataMapper, accountKeeper, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
issueMsg, ok := msg.(MsgIssue)
|
||||
if !ok {
|
||||
return sdk.NewError(2, 1, "Issue Message Malformed").Result()
|
||||
}
|
||||
|
||||
// Handle updating metadata
|
||||
if res := evenBetterHandleMetaData(ctx, metadataMapper, issueMsg.Issuer, issueMsg.Coin); !res.IsOK() {
|
||||
return res
|
||||
}
|
||||
|
||||
// Add newly issued coins to output address
|
||||
_, _, err := accountKeeper.AddCoins(ctx, issueMsg.Receiver, []sdk.Coin{issueMsg.Coin})
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
return sdk.Result{
|
||||
// Return result with Issue msg tags
|
||||
Tags: issueMsg.Tags(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func evenBetterHandleMsgIssue(ctx sdk.Context, metadataMapper MetaDataMapper, accountKeeper bank.Keeper, msg MsgIssue) sdk.Result {
|
||||
for _, o := range msg.Outputs {
|
||||
for _, coin := range o.Coins {
|
||||
// Metadata is no longer created on the fly since it is initalized at genesis with InitChain
|
||||
metadata := metadataMapper.GetMetaData(ctx, coin.Denom)
|
||||
// Check that msg Issuer is authorized to issue these coins
|
||||
if !reflect.DeepEqual(metadata.Issuer, msg.Issuer) {
|
||||
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue these coins: %s", coin.Denom)).Result()
|
||||
}
|
||||
// No longer generates metadata on the fly.
|
||||
// Returns error result when it cannot find coin metadata
|
||||
func evenBetterHandleMetaData(ctx sdk.Context, metadataMapper MetaDataMapper, issuer sdk.Address, coin sdk.Coin) sdk.Result {
|
||||
metadata := metadataMapper.GetMetaData(ctx, coin.Denom)
|
||||
|
||||
// Issuer cannot issue more than remaining supply
|
||||
issuerSupply := metadata.TotalSupply.Sub(metadata.CurrentSupply)
|
||||
if coin.Amount.GT(issuerSupply) {
|
||||
return sdk.ErrInsufficientCoins(fmt.Sprintf("Issuer cannot issue that many coins. Current issuer supply: %d", issuerSupply.Int64())).Result()
|
||||
}
|
||||
|
||||
// update metadata current circulating supply
|
||||
metadata.CurrentSupply = metadata.CurrentSupply.Add(coin.Amount)
|
||||
|
||||
metadataMapper.SetMetaData(ctx, coin.Denom, metadata)
|
||||
}
|
||||
|
||||
// Add newly issued coins to output address
|
||||
_, _, err := accountKeeper.AddCoins(ctx, o.Address, o.Coins)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
// Coin metadata does not exist in store
|
||||
if reflect.DeepEqual(metadata, CoinMetadata{}) {
|
||||
return sdk.ErrInvalidCoins(fmt.Sprintf("Cannot find metadata for coin: %s", coin.Denom)).Result()
|
||||
}
|
||||
|
||||
// Msg Issuer not authorized to issue these coins
|
||||
if !reflect.DeepEqual(metadata.Issuer, issuer) {
|
||||
return sdk.ErrUnauthorized(fmt.Sprintf("Msg Issuer cannot issue tokens: %s", coin.Denom)).Result()
|
||||
}
|
||||
|
||||
// Update current circulating supply
|
||||
metadata.CurrentSupply = metadata.CurrentSupply.Add(coin.Amount)
|
||||
|
||||
// Current supply cannot exceed total supply
|
||||
if metadata.TotalSupply.LT(metadata.CurrentSupply) {
|
||||
return sdk.ErrInsufficientCoins("Issuer cannot issue more than total supply of coin").Result()
|
||||
}
|
||||
|
||||
metadataMapper.SetMetaData(ctx, coin.Denom, metadata)
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Simpler MetaDataMapper no longer able to initalize default CoinMetaData
|
||||
// Simpler MetaDataMapper no longer able to initialize default CoinMetaData
|
||||
|
||||
// Implements MetaDataMapper
|
||||
type App4MetaDataMapper struct {
|
||||
mainKey *sdk.KVStoreKey
|
||||
}
|
||||
|
||||
// Constructs new App4MetaDataMapper
|
||||
func NewApp4MetaDataMapper(key *sdk.KVStoreKey) App4MetaDataMapper {
|
||||
return App4MetaDataMapper{mainKey: key}
|
||||
}
|
||||
|
||||
// Returns coin Metadata. If metadata not found in store, function returns empty struct.
|
||||
func (mdm App4MetaDataMapper) GetMetaData(ctx sdk.Context, denom string) CoinMetadata {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
|
@ -206,6 +220,7 @@ func (mdm App4MetaDataMapper) GetMetaData(ctx sdk.Context, denom string) CoinMet
|
|||
return metadata
|
||||
}
|
||||
|
||||
// Sets metadata in store with key equal to coin denom. Same behavior as App3 implementation.
|
||||
func (mdm App4MetaDataMapper) SetMetaData(ctx sdk.Context, denom string, metadata CoinMetadata) {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
|
|
Loading…
Reference in New Issue