Finish app1-3, app4 done minus staking
This commit is contained in:
parent
e6aec82f40
commit
d1c9b8bdb9
@ -114,7 +114,6 @@ func NewApp1Handler(keyAcc *sdk.KVStoreKey) sdk.Handler {
|
||||
// Handle MsgSend.
|
||||
func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result {
|
||||
// NOTE: from, to, and amount were already validated
|
||||
|
||||
store := ctx.KVStore(key)
|
||||
|
||||
// deduct msg amount from sender account
|
||||
@ -155,10 +154,10 @@ func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result
|
||||
bz = store.Get(msg.To)
|
||||
var acc2 account
|
||||
if bz == nil {
|
||||
// Sender account does not already exist, create a new one.
|
||||
acc2 = account{}
|
||||
// Receiver account does not already exist, create a new one.
|
||||
acc2 = account{Address: msg.To}
|
||||
} else {
|
||||
// Sender account already exists. Retrieve and decode it.
|
||||
// Receiver account already exists. Retrieve and decode it.
|
||||
err = json.Unmarshal(bz, &acc2)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal("Account decoding error").Result()
|
||||
@ -185,7 +184,9 @@ func handleMsgSend(ctx sdk.Context, key *sdk.KVStoreKey, msg MsgSend) sdk.Result
|
||||
}
|
||||
|
||||
type account struct {
|
||||
Address sdk.Address `json:"address"`
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
SequenceNumber int64 `json:"sequence"`
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
@ -2,23 +2,35 @@ package app
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
"github.com/tendermint/go-crypto"
|
||||
|
||||
bapp "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
)
|
||||
|
||||
const (
|
||||
app2Name = "App2"
|
||||
)
|
||||
|
||||
var (
|
||||
issuer = crypto.GenPrivKeyEd25519().PubKey().Address()
|
||||
)
|
||||
|
||||
func NewCodec() *wire.Codec {
|
||||
// TODO register
|
||||
return nil
|
||||
cdc := wire.NewCodec()
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(MsgSend{}, "example/MsgSend", nil)
|
||||
cdc.RegisterConcrete(MsgIssue{}, "example/MsgIssue", nil)
|
||||
return cdc
|
||||
}
|
||||
|
||||
func NewApp2(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
||||
@ -29,16 +41,19 @@ func NewApp2(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
||||
app := bapp.NewBaseApp(app2Name, cdc, logger, db)
|
||||
|
||||
// Create a key for accessing the account store.
|
||||
keyMain := sdk.NewKVStoreKey("main")
|
||||
keyAccount := sdk.NewKVStoreKey("acc")
|
||||
keyIssuer := sdk.NewKVStoreKey("issuer")
|
||||
|
||||
// set antehandler function
|
||||
app.SetAnteHandler(antehandler)
|
||||
|
||||
// Register message routes.
|
||||
// Note the handler gets access to the account store.
|
||||
app.Router().
|
||||
AddRoute("bank", NewApp2Handler(keyAccount, keyIssuer))
|
||||
AddRoute("bank", NewApp2Handler(keyAccount, keyMain))
|
||||
|
||||
// Mount stores and load the latest state.
|
||||
app.MountStoresIAVL(keyAccount, keyIssuer)
|
||||
app.MountStoresIAVL(keyAccount, keyMain)
|
||||
err := app.LoadLatestVersion(keyAccount)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
@ -46,21 +61,73 @@ func NewApp2(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
||||
return app
|
||||
}
|
||||
|
||||
// Coin Metadata
|
||||
type CoinMetadata struct {
|
||||
TotalSupply sdk.Int
|
||||
CurrentSupply sdk.Int
|
||||
Issuer sdk.Address
|
||||
Decimal uint64
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Msgs
|
||||
|
||||
// TODO: MsgIssue
|
||||
// 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
|
||||
// Implements sdk.Msg Interface
|
||||
type MsgIssue struct {
|
||||
Issuer sdk.Address
|
||||
Outputs []Output
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (msg MsgIssue) Type() string { return "bank" }
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg MsgIssue) GetSignBytes() []byte {
|
||||
bz, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bz
|
||||
}
|
||||
|
||||
// Implements Msg. Return the signer.
|
||||
func (msg MsgIssue) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.Issuer}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Handler for the message
|
||||
|
||||
func NewApp1Handler(keyAcc *sdk.KVStoreKey) sdk.Handler {
|
||||
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:
|
||||
// TODO
|
||||
return handleMsgIssue(ctx, keyMain, keyAcc, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
@ -68,12 +135,102 @@ func NewApp1Handler(keyAcc *sdk.KVStoreKey) sdk.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Add coins to receiver account
|
||||
bz := accStore.Get(o.Address)
|
||||
var acc account
|
||||
if bz == nil {
|
||||
// Receiver account does not already exist, create a new one.
|
||||
acc = account{}
|
||||
} else {
|
||||
// Receiver account already exists. Retrieve and decode it.
|
||||
err := json.Unmarshal(bz, &acc)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal("Account decoding error").Result()
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// set account with new issued coins in store
|
||||
store.Set(o.Address, val)
|
||||
}
|
||||
|
||||
return sdk.Result{
|
||||
// TODO: Tags
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Tx
|
||||
|
||||
// Simple tx to wrap the Msg.
|
||||
type app2Tx struct {
|
||||
sdk.Msg
|
||||
Signatures []auth.StdSignature
|
||||
}
|
||||
|
||||
// This tx only has one Msg.
|
||||
@ -85,3 +242,46 @@ func (tx app2Tx) GetMsgs() []sdk.Msg {
|
||||
func (tx app2Tx) GetMemo() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (tx app2Tx) GetSignatures() []auth.StdSignature {
|
||||
return tx.Signatures
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// Simple antehandler that ensures msg signers has signed over msg signBytes w/ no replay protection
|
||||
// Implement sdk.AnteHandler interface
|
||||
func antehandler(ctx sdk.Context, tx sdk.Tx) (_ sdk.Context, _ sdk.Result, abort bool) {
|
||||
appTx, ok := tx.(app2Tx)
|
||||
if !ok {
|
||||
// set abort boolean to true so that we don't continue to process failed tx
|
||||
return ctx, sdk.ErrTxDecode("Tx must be of format app2Tx").Result(), true
|
||||
}
|
||||
|
||||
// expect only one msg in app2Tx
|
||||
msg := tx.GetMsgs()[0]
|
||||
|
||||
signerAddrs := msg.GetSigners()
|
||||
signBytes := msg.GetSignBytes()
|
||||
|
||||
if len(signerAddrs) != len(appTx.GetSignatures()) {
|
||||
return ctx, sdk.ErrUnauthorized("Number of signatures do not match required amount").Result(), true
|
||||
}
|
||||
|
||||
for i, addr := range signerAddrs {
|
||||
sig := appTx.GetSignatures()[i]
|
||||
|
||||
// check that submitted pubkey belongs to required address
|
||||
if !reflect.DeepEqual(sig.PubKey.Address(), addr) {
|
||||
return ctx, sdk.ErrUnauthorized("Provided Pubkey does not match required address").Result(), true
|
||||
}
|
||||
|
||||
// check that signature is over expected signBytes
|
||||
if !sig.PubKey.VerifyBytes(signBytes, sig.Signature) {
|
||||
return ctx, sdk.ErrUnauthorized("Signature verification failed").Result(), true
|
||||
}
|
||||
}
|
||||
|
||||
// authentication passed, app to continue processing by sending msg to handler
|
||||
return ctx, sdk.Result{}, false
|
||||
}
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
bapp "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"
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
@ -22,17 +29,24 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
||||
|
||||
// Create a key for accessing the account store.
|
||||
keyAccount := sdk.NewKVStoreKey("acc")
|
||||
keyIssuer := sdk.NewKVStoreKey("issuer")
|
||||
keyMain := sdk.NewKVStoreKey("main")
|
||||
keyFees := sdk.NewKVStoreKey("fee")
|
||||
|
||||
// TODO: accounts, ante handler
|
||||
// Set various mappers/keepers to interact easily with underlying stores
|
||||
accountMapper := auth.NewAccountMapper(cdc, keyAccount, &auth.BaseAccount{})
|
||||
accountKeeper := bank.NewKeeper(accountMapper)
|
||||
metadataMapper := NewApp3MetaDataMapper(keyMain)
|
||||
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
|
||||
|
||||
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
|
||||
|
||||
// Register message routes.
|
||||
// Note the handler gets access to the account store.
|
||||
app.Router().
|
||||
AddRoute("bank", NewApp2Handler(keyAccount, keyIssuer))
|
||||
AddRoute("bank", NewApp3Handler(accountKeeper, metadataMapper))
|
||||
|
||||
// Mount stores and load the latest state.
|
||||
app.MountStoresIAVL(keyAccount, keyIssuer)
|
||||
app.MountStoresIAVL(keyAccount, keyMain, keyFees)
|
||||
err := app.LoadLatestVersion(keyAccount)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
@ -40,6 +54,124 @@ func NewApp3(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
||||
return app
|
||||
}
|
||||
|
||||
func NewApp3Handler(accountKeeper bank.Keeper, metadataMapper MetaDataMapper) 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Add newly issued coins to output address
|
||||
_, _, err := accountKeeper.AddCoins(ctx, o.Address, o.Coins)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
}
|
||||
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Mapper for Coin Metadata
|
||||
// Example of a very simple user-defined mapper
|
||||
|
||||
type MetaDataMapper interface {
|
||||
GetMetaData(sdk.Context, string) CoinMetadata
|
||||
SetMetaData(sdk.Context, string, CoinMetadata)
|
||||
}
|
||||
|
||||
type App3MetaDataMapper struct {
|
||||
mainKey *sdk.KVStoreKey
|
||||
}
|
||||
|
||||
func NewApp3MetaDataMapper(key *sdk.KVStoreKey) App3MetaDataMapper {
|
||||
return App3MetaDataMapper{mainKey: key}
|
||||
}
|
||||
|
||||
func (mdm App3MetaDataMapper) GetMetaData(ctx sdk.Context, denom string) CoinMetadata {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
bz := store.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
// Coin metadata doesn't exist, create new metadata with default params
|
||||
return CoinMetadata{
|
||||
TotalSupply: sdk.NewInt(1000000),
|
||||
}
|
||||
}
|
||||
|
||||
var metadata CoinMetadata
|
||||
err := json.Unmarshal(bz, &metadata)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (mdm App3MetaDataMapper) SetMetaData(ctx sdk.Context, denom string, metadata CoinMetadata) {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
val, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
store.Set([]byte(denom), val)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// StdTx
|
||||
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"fmt"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
|
||||
bapp "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -18,23 +26,31 @@ func NewApp4(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
||||
cdc := NewCodec()
|
||||
|
||||
// Create the base application object.
|
||||
app := bapp.NewBaseApp(app4Name, cdc, logger, db)
|
||||
app := bapp.NewBaseApp(app3Name, cdc, logger, db)
|
||||
|
||||
// Create a key for accessing the account store.
|
||||
keyAccount := sdk.NewKVStoreKey("acc")
|
||||
keyIssuer := sdk.NewKVStoreKey("issuer")
|
||||
keyMain := sdk.NewKVStoreKey("main")
|
||||
keyFees := sdk.NewKVStoreKey("fee")
|
||||
|
||||
// TODO: accounts, ante handler
|
||||
// Set various mappers/keepers to interact easily with underlying stores
|
||||
accountMapper := auth.NewAccountMapper(cdc, keyAccount, &auth.BaseAccount{})
|
||||
accountKeeper := bank.NewKeeper(accountMapper)
|
||||
metadataMapper := NewApp4MetaDataMapper(keyMain)
|
||||
feeKeeper := auth.NewFeeCollectionKeeper(cdc, keyFees)
|
||||
|
||||
// TODO: AccountMapper, CoinKeepr
|
||||
app.SetAnteHandler(auth.NewAnteHandler(accountMapper, feeKeeper))
|
||||
|
||||
// Set InitChainer
|
||||
app.SetInitChainer(NewInitChainer(cdc, accountMapper, metadataMapper))
|
||||
|
||||
// Register message routes.
|
||||
// Note the handler gets access to the account store.
|
||||
app.Router().
|
||||
AddRoute("bank", NewApp2Handler(keyAccount, keyIssuer))
|
||||
AddRoute("bank", NewApp4Handler(accountKeeper, metadataMapper))
|
||||
|
||||
// Mount stores and load the latest state.
|
||||
app.MountStoresIAVL(keyAccount, keyIssuer)
|
||||
app.MountStoresIAVL(keyAccount, keyMain, keyFees)
|
||||
err := app.LoadLatestVersion(keyAccount)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
@ -42,6 +58,159 @@ func NewApp4(logger log.Logger, db dbm.DB) *bapp.BaseApp {
|
||||
return app
|
||||
}
|
||||
|
||||
type GenesisState struct {
|
||||
Accounts []*GenesisAccount `json:"accounts"`
|
||||
Coins []*GenesisCoin `json:"coins"`
|
||||
}
|
||||
|
||||
// GenesisAccount doesn't need pubkey or sequence
|
||||
type GenesisAccount struct {
|
||||
Address sdk.Address `json:"address"`
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
}
|
||||
|
||||
func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount, err error) {
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: ga.Address,
|
||||
Coins: ga.Coins.Sort(),
|
||||
}
|
||||
return &baseAcc, nil
|
||||
}
|
||||
|
||||
// GenesisCoin enforces CurrentSupply is 0 at genesis.
|
||||
type GenesisCoin struct {
|
||||
Issuer sdk.Address `json:"issuer"`
|
||||
TotalSupply sdk.Int `json:"total_supply`
|
||||
Decimal uint64 `json:"decimals"`
|
||||
}
|
||||
|
||||
func (gc *GenesisCoin) ToMetaData() CoinMetadata {
|
||||
return CoinMetadata{
|
||||
Issuer: gc.Issuer,
|
||||
TotalSupply: gc.TotalSupply,
|
||||
Decimal: gc.Decimal,
|
||||
}
|
||||
}
|
||||
|
||||
// InitChainer will set initial balances for accounts as well as initial coin metadata
|
||||
// MsgIssue can no longer be used to create new coin
|
||||
func NewInitChainer(cdc *wire.Codec, accountMapper auth.AccountMapper, metadataMapper MetaDataMapper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
stateJSON := req.AppStateBytes
|
||||
|
||||
genesisState := new(GenesisState)
|
||||
err := cdc.UnmarshalJSON(stateJSON, genesisState)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
|
||||
for _, gacc := range genesisState.Accounts {
|
||||
acc, err := gacc.ToAccount()
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
acc.AccountNumber = accountMapper.GetNextAccountNumber(ctx)
|
||||
accountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
return abci.ResponseInitChain{}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Now that initializing coin metadata is done in InitChainer we can simplifiy handleMsgIssue
|
||||
|
||||
func NewApp4Handler(accountKeeper bank.Keeper, metadataMapper MetaDataMapper) 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Simpler MetaDataMapper no longer able to initalize default CoinMetaData
|
||||
|
||||
type App4MetaDataMapper struct {
|
||||
mainKey *sdk.KVStoreKey
|
||||
}
|
||||
|
||||
func NewApp4MetaDataMapper(key *sdk.KVStoreKey) App4MetaDataMapper {
|
||||
return App4MetaDataMapper{mainKey: key}
|
||||
}
|
||||
|
||||
func (mdm App4MetaDataMapper) GetMetaData(ctx sdk.Context, denom string) CoinMetadata {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
bz := store.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
// Coin metadata doesn't exist, create new metadata with default params
|
||||
return CoinMetadata{}
|
||||
}
|
||||
|
||||
var metadata CoinMetadata
|
||||
err := json.Unmarshal(bz, &metadata)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (mdm App4MetaDataMapper) SetMetaData(ctx sdk.Context, denom string, metadata CoinMetadata) {
|
||||
store := ctx.KVStore(mdm.mainKey)
|
||||
|
||||
val, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
store.Set([]byte(denom), val)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// AccountMapper
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user