Skip to content

Commit

Permalink
Merge pull request #34 from aquarius-kuchain/features/coin-stat-can-brun
Browse files Browse the repository at this point in the history
Add CanBurn Tag in Asset State
  • Loading branch information
aquarius-kuchain authored Aug 18, 2020
2 parents 333ca37 + 25476d0 commit 1e4d433
Show file tree
Hide file tree
Showing 14 changed files with 355 additions and 150 deletions.
146 changes: 146 additions & 0 deletions x/asset/burn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package asset_test

import (
"testing"

"github.com/KuChainNetwork/kuchain/chain/constants"
"github.com/KuChainNetwork/kuchain/chain/types"
"github.com/KuChainNetwork/kuchain/test/simapp"
assetTypes "github.com/KuChainNetwork/kuchain/x/asset/types"
. "github.com/smartystreets/goconvey/convey"
)

func checkBurnState(t *testing.T, app *simapp.SimApp, isSuccess bool, from types.AccountID, amt types.Coin) error {
ctx := app.NewTestContext()

creator, symbol, err := types.CoinAccountsFromDenom(amt.Denom)
So(err, ShouldBeNil)

amtOld := app.AssetKeeper().GetAllBalances(ctx, from)
statOld, err := app.AssetKeeper().GetCoinStat(ctx, creator, symbol)
So(err, ShouldBeNil)

burnErr := BurnCoinTest(t, app, isSuccess, from, amt)

currAmt := amtOld.Sub(simapp.DefaultTestFee)
if isSuccess {
currAmt = currAmt.Sub(types.NewCoins(amt))
}

ctx = app.NewTestContext()
So(app.AssetKeeper().GetAllBalances(ctx, from), simapp.ShouldEq, currAmt)

statNew, err := app.AssetKeeper().GetCoinStat(ctx, creator, symbol)
So(err, ShouldBeNil)

// should no core coin as it can issue per block
if isSuccess {
So(statOld.Supply.Sub(amt), simapp.ShouldEq, statNew.Supply)
} else {
So(statOld.Supply, simapp.ShouldEq, statNew.Supply)
}

return burnErr
}

func TestBurnCoreCoins(t *testing.T) {
app, _ := createAppForTest()
Convey("test core coins", t, func() {
ctx := app.NewTestContext()
acc1Coins := app.AssetKeeper().GetAllBalances(ctx, account1)
burnCoin := types.NewInt64Coin(constants.DefaultBondDenom, 10000)

So(BurnCoinTest(t, app, true, account1, burnCoin), ShouldBeNil)
amt := acc1Coins.Sub(simapp.DefaultTestFee)
ctx = app.NewTestContext()
So(amt.Sub(types.NewCoins(burnCoin)), simapp.ShouldEq, app.AssetKeeper().GetAllBalances(ctx, account1))
})

Convey("test burn all core coins error by no fee", t, func() {
ctx := app.NewTestContext()
acc2Coins := app.AssetKeeper().GetAllBalances(ctx, account2)
burnCoin := types.NewInt64CoreCoin(acc2Coins.AmountOf(constants.DefaultBondDenom).Int64())

So(BurnCoinTest(t, app, false, account2, burnCoin), simapp.ShouldErrIs, assetTypes.ErrAssetCoinNoEnough)
amt := acc2Coins.Sub(simapp.DefaultTestFee)
ctx = app.NewTestContext()
So(amt, simapp.ShouldEq, app.AssetKeeper().GetAllBalances(ctx, account2))
})

Convey("test burn all core coins", t, func() {
ctx := app.NewTestContext()
acc2Coins := app.AssetKeeper().GetAllBalances(ctx, account2)
burnCoin := types.NewInt64CoreCoin(acc2Coins.AmountOf(constants.DefaultBondDenom).Int64())
burnCoin = burnCoin.Sub(simapp.DefaultTestFee[0])

So(BurnCoinTest(t, app, true, account2, burnCoin), ShouldBeNil)
ctx = app.NewTestContext()
So(types.NewCoins(), simapp.ShouldEq, app.AssetKeeper().GetAllBalances(ctx, account2))
})
}

func TestBurnOtherCoins(t *testing.T) {
app, _ := createAppForTest()

var (
symbol = types.MustName("abc")
denom = types.CoinDenom(name4, symbol)
maxSupplyAmt int64 = 10000000000000
issueAmts = types.NewInt64Coin(denom, maxSupplyAmt)
)

Convey("create coins to test at first", t, func() {
So(createCoin(t, app, true, account4, symbol, maxSupplyAmt), ShouldBeNil)
So(issueCoin(t, app, true, account4, symbol, issueAmts), ShouldBeNil)
So(transfer(t, app, true, account4, account1, types.NewInt64Coins(denom, 1000000), account4), ShouldBeNil)
})

Convey("test other coins burn", t, func() {
burnCoin := types.NewInt64Coin(denom, 10000)

So(checkBurnState(t, app, true, account1, burnCoin), ShouldBeNil)
})

Convey("test burn no enough coins", t, func() {
burnCoin := types.NewInt64Coin(denom, 1000000)

So(checkBurnState(t, app, false, account1, burnCoin), simapp.ShouldErrIs, assetTypes.ErrAssetCoinNoEnough)
})

Convey("test burn all core coins", t, func() {
ctx := app.NewTestContext()
acc1Coins := app.AssetKeeper().GetAllBalances(ctx, account1)
burnCoin := types.NewInt64Coin(denom, acc1Coins.AmountOf(denom).Int64())

So(checkBurnState(t, app, true, account1, burnCoin), ShouldBeNil)
})
}

func TestBurnNoCanBurnCoins(t *testing.T) {
app, _ := createAppForTest()

var (
symbol = types.MustName("abcd1")
denom = types.CoinDenom(name4, symbol) // create in last
maxSupplyAmt int64 = 10000000000000
issueAmts = types.NewInt64Coin(denom, 10000000000000)
)

Convey("create coins to test at first", t, func() {
So(createCoinExt(t, app, true,
account4, symbol,
types.NewInt64Coin(denom, maxSupplyAmt),
true, true,
false, // cannot burn
0,
types.NewInt64Coin(denom, 0), []byte("cannot issue")), ShouldBeNil)
So(issueCoin(t, app, true, account4, symbol, issueAmts), ShouldBeNil)
So(transfer(t, app, true, account4, account1, types.NewInt64Coins(denom, 1000000), account4), ShouldBeNil)
})

Convey("test other coins burn", t, func() {
burnCoin := types.NewInt64Coin(denom, 10000)

So(checkBurnState(t, app, false, account1, burnCoin), simapp.ShouldErrIs, assetTypes.ErrAssetCoinCannotBeBurn)
})
}
15 changes: 8 additions & 7 deletions x/asset/client/cli/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import (
// Create will create a account create tx and sign it with the given key.
func Create(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "create [creator] [symbol] [max_supply] [canIssue] [canLock] [issueToHeight] [initSupply] [desc]",
Short: "Create coin, if canIssue is 1 or canLock is 1, the coin cannot issue or lock after 64 blocks",
Args: cobra.ExactArgs(8),
Use: "create [creator] [symbol] [max_supply] [canIssue] [canLock] [canBurn] [issueToHeight] [initSupply] [desc]",
Short: "Create coin, for canIssue, canLock and canBurn, the 1 means true.",
Args: cobra.ExactArgs(9),
RunE: func(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
txBldr := txutil.NewTxBuilderFromCLI(inBuf).WithTxEncoder(txutil.GetTxEncoder(cdc))
Expand Down Expand Up @@ -53,12 +53,13 @@ func Create(cdc *codec.Codec) *cobra.Command {

isCanIssue := args[3] == "1"
isCanLock := args[4] == "1"
issueToHeight, err := strconv.ParseInt(args[5], 10, 64)
isCanBurn := args[5] == "1"
issueToHeight, err := strconv.ParseInt(args[6], 10, 64)
if err != nil {
return err
}

initSupply, err := chainTypes.ParseCoin(args[6])
initSupply, err := chainTypes.ParseCoin(args[7])
if err != nil {
return errors.Wrap(err, "init supply parse error")
}
Expand All @@ -73,12 +74,12 @@ func Create(cdc *codec.Codec) *cobra.Command {
initSupply.Denom, maxSupply.Denom)
}

desc := args[7]
desc := args[8]
if len(desc) > types.CoinDescriptionLen {
return fmt.Errorf("coin desc too long, should be less than %d", types.CoinDescriptionLen)
}

msg := types.NewMsgCreate(auth, creator, symbol, maxSupply, isCanIssue, isCanLock, issueToHeight, initSupply, []byte(desc))
msg := types.NewMsgCreate(auth, creator, symbol, maxSupply, isCanIssue, isCanLock, isCanBurn, issueToHeight, initSupply, []byte(desc))
return txutil.GenerateOrBroadcastMsgs(ctx, txBldr, []sdk.Msg{msg})
},
}
Expand Down
4 changes: 3 additions & 1 deletion x/asset/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type CreateReq struct {
MaxSupply string `json:"max_supply" yaml:"max_supply"`
CanIssue string `json:"can_issue" yaml:"can_issue"`
CanLock string `json:"can_lock" yaml:"can_lock"`
CanBurn string `json:"can_burn" yaml:"can_burn"`
IssueToHeight string `json:"issue_to_height" yaml:"issue_to_height"`
InitSupply string `json:"init_supply" yaml:"init_supply"`
Desc string `json:"desc" yaml:"desc"`
Expand Down Expand Up @@ -141,6 +142,7 @@ func CreateRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {

isCanIssue := req.CanIssue == "1"
isCanLock := req.CanLock == "1"
isCanBurn := req.CanBurn == "1"
issueToHeight, err := strconv.ParseInt(req.IssueToHeight, 10, 64)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
Expand Down Expand Up @@ -170,7 +172,7 @@ func CreateRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return
}

msg := types.NewMsgCreate(auth, creator, symbol, maxSupply, isCanIssue, isCanLock, issueToHeight, initSupply, []byte(req.Desc))
msg := types.NewMsgCreate(auth, creator, symbol, maxSupply, isCanIssue, isCanLock, isCanBurn, issueToHeight, initSupply, []byte(req.Desc))
txutil.WriteGenerateStdTxResponse(w, ctx, req.BaseReq, []sdk.Msg{msg})
}
}
Expand Down
152 changes: 152 additions & 0 deletions x/asset/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package asset_test

import (
"fmt"
"testing"

"github.com/KuChainNetwork/kuchain/chain/constants"
"github.com/KuChainNetwork/kuchain/chain/types"
"github.com/KuChainNetwork/kuchain/test/simapp"
assetTypes "github.com/KuChainNetwork/kuchain/x/asset/types"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/tendermint/abci/types"
)

var (
// values for test
wallet = simapp.NewWallet()
addr1 = wallet.NewAccAddressByName(name1)
addr2 = wallet.NewAccAddressByName(name2)
addr3 = wallet.NewAccAddressByName(name3)
addr4 = wallet.NewAccAddressByName(name4)
addr5 = wallet.NewAccAddressByName(name5)
name1 = types.MustName("test01@chain")
name2 = types.MustName("aaaeeebbbccc")
name3 = types.MustName("aaaeeebbbcc2")
name4 = types.MustName("test")
name5 = types.MustName("foo")
account1 = types.NewAccountIDFromName(name1)
account2 = types.NewAccountIDFromName(name2)
account3 = types.NewAccountIDFromName(name3)
account4 = types.NewAccountIDFromName(name4)
account5 = types.NewAccountIDFromName(name5)
addAccount1 = types.NewAccountIDFromAccAdd(addr1)
)

func createAppForTest() (*simapp.SimApp, sdk.Context) {
asset1 := types.NewCoins(
types.NewInt64Coin("foo/coin", 10000000),
types.NewInt64Coin(constants.DefaultBondDenom, 10000000000))
asset2 := types.NewCoins(
types.NewInt64Coin(constants.DefaultBondDenom, 10000000000))
asset3 := types.NewCoins(
types.NewInt64Coin("foo/coin", 100),
types.NewInt64Coin(constants.DefaultBondDenom, 10000000000))

genAccs := simapp.NewGenesisAccounts(wallet.GetRootAuth(),
simapp.NewSimGenesisAccount(account1, addr1).WithAsset(asset1),
simapp.NewSimGenesisAccount(account2, addr2).WithAsset(asset2),
simapp.NewSimGenesisAccount(account3, addr3).WithAsset(asset3),
simapp.NewSimGenesisAccount(account4, addr4).WithAsset(asset2),
simapp.NewSimGenesisAccount(account5, addr5).WithAsset(asset2),
)
app := simapp.SetupWithGenesisAccounts(genAccs)

ctxCheck := app.BaseApp.NewContext(true, abci.Header{Height: app.LastBlockHeight() + 1})
return app, ctxCheck
}

func transfer(t *testing.T, app *simapp.SimApp, isSuccess bool, from, to types.AccountID, amt types.Coins, payer types.AccountID) error {
return simapp.CommitTransferTx(t, app, wallet, isSuccess, from, to, amt, payer)
}

func createCoin(t *testing.T, app *simapp.SimApp, isSuccess bool,
creator types.AccountID, symbol types.Name, maxSupplyAmount int64) error {
ctx := app.NewTestContext()
creatorName := creator.MustName()

var (
demon = types.CoinDenom(creatorName, symbol)
maxSupply = types.NewCoin(demon, types.NewInt(maxSupplyAmount))
initSupply = types.NewCoin(demon, types.NewInt(0))
desc = []byte(fmt.Sprintf("desc for %s", demon))
)

auth := app.AccountKeeper().GetAccount(ctx, creator).GetAuth()

msg := assetTypes.NewMsgCreate(auth, creatorName, symbol, maxSupply, true, true, true, 0, initSupply, desc)
tx := simapp.NewTxForTest(
creator,
[]sdk.Msg{
&msg,
}, wallet.PrivKey(auth))

if !isSuccess {
tx = tx.WithCannotPass()
}

return simapp.CheckTxs(t, app, ctx, tx)
}

func createCoinExt(t *testing.T, app *simapp.SimApp, isSuccess bool,
creator types.AccountID, symbol types.Name, maxSupply types.Coin, canIssue, canLock, canBurn bool, issue2Height int64, initSupply types.Coin, desc []byte) error {
ctx := app.NewTestContext()
creatorName := creator.MustName()

auth := app.AccountKeeper().GetAccount(ctx, creator).GetAuth()

msg := assetTypes.NewMsgCreate(auth, creatorName, symbol, maxSupply, canIssue, canLock, canBurn, issue2Height, initSupply, desc)
tx := simapp.NewTxForTest(
creator,
[]sdk.Msg{
&msg,
}, wallet.PrivKey(auth))

if !isSuccess {
tx = tx.WithCannotPass()
}

return simapp.CheckTxs(t, app, ctx, tx)
}

func issueCoin(t *testing.T, app *simapp.SimApp, isSuccess bool,
creator types.AccountID, symbol types.Name, amt types.Coin) error {
ctx := app.NewTestContext()
creatorName := creator.MustName()

auth := app.AccountKeeper().GetAccount(ctx, creator).GetAuth()

msg := assetTypes.NewMsgIssue(auth, creatorName, symbol, amt)
tx := simapp.NewTxForTest(
creator,
[]sdk.Msg{
&msg,
}, wallet.PrivKey(auth))

if !isSuccess {
tx = tx.WithCannotPass()
}

return simapp.CheckTxs(t, app, ctx, tx)
}

// BurnCoinTest burn coins test
func BurnCoinTest(t *testing.T, app *simapp.SimApp, isSuccess bool,
from types.AccountID, amt types.Coin) error {
ctx := app.NewTestContext()

auth := app.AccountKeeper().GetAccount(ctx, from).GetAuth()

msg := assetTypes.NewMsgBurn(auth, from, amt)
tx := simapp.NewTxForTest(
from,
[]sdk.Msg{
&msg,
}, wallet.PrivKey(auth))

if !isSuccess {
tx = tx.WithCannotPass()
}

return simapp.CheckTxs(t, app, ctx, tx)
}
2 changes: 1 addition & 1 deletion x/asset/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func InitGenesis(ctx sdk.Context, ak Keeper, bz json.RawMessage) {

initSupply := types.NewCoin(a.GetMaxSupply().Denom, sdk.ZeroInt())

err := ak.Create(ctx, a.GetCreator(), a.GetSymbol(), a.GetMaxSupply(), true, true, 0, initSupply, []byte{}) // TODO: genesis coins support opt
err := ak.Create(ctx, a.GetCreator(), a.GetSymbol(), a.GetMaxSupply(), true, true, true, 0, initSupply, []byte{}) // TODO: genesis coins support opt
if err != nil {
panic(err)
}
Expand Down
Loading

0 comments on commit 1e4d433

Please sign in to comment.