Skip to content

Commit

Permalink
fix!(manifest): only poa is allowed to burn tokens (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmorency authored Jun 3, 2024
1 parent 53591a6 commit 850a9dd
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 142 deletions.
139 changes: 70 additions & 69 deletions api/manifest/v1/tx.pulsar.go

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions interchaintest/helpers/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,10 @@ func ManifestQueryParams(ctx context.Context, node *cosmos.ChainNode) (*manifest
res, err := manifesttypes.NewQueryClient(node.GrpcConn).Params(ctx, &manifesttypes.QueryParamsRequest{})
return res.GetParams(), err
}

func ManifestBurnTokens(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, keyName string, amount string, flags ...string) (sdk.TxResponse, error) {
txCmd := []string{"tx", "manifest", "burn-coins", amount}
fmt.Println("ManifestBurnTokens", txCmd)
cmd := TxCommandBuilder(ctx, chain, txCmd, keyName, flags...)
return ExecuteTransaction(ctx, chain, cmd)
}
55 changes: 55 additions & 0 deletions interchaintest/mainfest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,61 @@ func TestManifestModule(t *testing.T) {
require.Error(t, err)
})

t.Run("fail: invalid burn authority", func(t *testing.T) {
accBal, err := appChain.GetBalance(ctx, uaddr, Denom)
require.NoError(t, err)
o, err := helpers.ManifestBurnTokens(t, ctx, appChain, uaddr, "1"+Denom)
require.NoError(t, err) // The tx is successful but the burn fails
tx, err := appChain.GetTransaction(o.TxHash)
require.NoError(t, err)
require.NotEqual(t, tx.Code, uint32(0x0)) // The burn failed
require.Contains(t, tx.RawLog, "invalid authority")
accBal2, err := appChain.GetBalance(ctx, uaddr, Denom)
require.NoError(t, err)
require.EqualValues(t, accBal, accBal2)
})

t.Run("success: burn tokens as poa admin", func(t *testing.T) {
poaAdminAddr := poaAdmin.FormattedAddress()
accBal, err := appChain.GetBalance(ctx, poaAdminAddr, Denom)
require.NoError(t, err)
o, err := helpers.ManifestBurnTokens(t, ctx, appChain, poaAdminAddr, "1"+Denom)
require.NoError(t, err)
tx, err := appChain.GetTransaction(o.TxHash)
require.NoError(t, err)
require.Equal(t, tx.Code, uint32(0x0))
accBal2, err := appChain.GetBalance(ctx, poaAdminAddr, Denom)
require.NoError(t, err)
require.EqualValues(t, accBal2, accBal.Sub(sdkmath.OneInt()))
})

t.Run("fail: burn unknown denom as poa admin", func(t *testing.T) {
poaAdminAddr := poaAdmin.FormattedAddress()
accBal, err := appChain.GetBalance(ctx, poaAdminAddr, Denom)
require.NoError(t, err)
o, err := helpers.ManifestBurnTokens(t, ctx, appChain, poaAdminAddr, "1foobar")
require.NoError(t, err) // The tx is successful but the burn fails
tx, err := appChain.GetTransaction(o.TxHash)
require.NoError(t, err)
require.NotEqual(t, tx.Code, uint32(0x0)) // The burn failed
require.Contains(t, tx.RawLog, "insufficient funds ")
accBal2, err := appChain.GetBalance(ctx, poaAdminAddr, Denom)
require.NoError(t, err)
require.EqualValues(t, accBal, accBal2)
})

t.Run("fail: burn invalid coin expression as poa admin", func(t *testing.T) {
poaAdminAddr := poaAdmin.FormattedAddress()
accBal, err := appChain.GetBalance(ctx, poaAdminAddr, Denom)
require.NoError(t, err)
_, err = helpers.ManifestBurnTokens(t, ctx, appChain, poaAdminAddr, "foobar")
require.Error(t, err)
require.Contains(t, err.Error(), "invalid decimal coin expression")
accBal2, err := appChain.GetBalance(ctx, poaAdminAddr, Denom)
require.NoError(t, err)
require.EqualValues(t, accBal, accBal2)
})

t.Cleanup(func() {
CopyCoverageFromContainer(ctx, t, client, appChain.GetNode().ContainerID(), appChain.HomeDir())
_ = ic.Close()
Expand Down
2 changes: 1 addition & 1 deletion interchaintest/poa_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ func createManifestPayoutProposal(authority string, payouts []manifesttypes.Payo

func createManifestBurnProposal(sender string, amounts sdk.Coins) manifesttypes.MsgBurnHeldBalance {
return manifesttypes.MsgBurnHeldBalance{
Sender: sender,
Authority: sender,
BurnCoins: amounts,
}
}
Expand Down
4 changes: 2 additions & 2 deletions proto/manifest/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ message MsgPayoutResponse {}

// MsgPayout is the Msg/BurnHeldBalance request type.
message MsgBurnHeldBalance {
option (cosmos.msg.v1.signer) = "sender";
option (cosmos.msg.v1.signer) = "authority";
option (gogoproto.equal) = false;

// sender is the address of the tokenholder.
string sender = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];

// burn_coins are the coins to be burned by the tokenholder.
repeated cosmos.base.v1beta1.Coin burn_coins = 2 [
Expand Down
13 changes: 8 additions & 5 deletions x/manifest/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,18 @@ func (ms msgServer) Payout(ctx context.Context, req *types.MsgPayout) (*types.Ms
return nil, ms.k.Payout(ctx, req.PayoutPairs)
}

func (ms msgServer) BurnHeldBalance(ctx context.Context, msg *types.MsgBurnHeldBalance) (*types.MsgBurnHeldBalanceResponse, error) {
addr, err := sdk.AccAddressFromBech32(msg.Sender)
func (ms msgServer) BurnHeldBalance(ctx context.Context, req *types.MsgBurnHeldBalance) (*types.MsgBurnHeldBalanceResponse, error) {
if ms.k.authority != req.Authority {
return nil, fmt.Errorf("invalid authority; expected %s, got %s", ms.k.authority, req.Authority)
}
addr, err := sdk.AccAddressFromBech32(req.Authority)
if err != nil {
return nil, err
}

if err := ms.k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, msg.BurnCoins); err != nil {
return nil, fmt.Errorf("not enough balance to burn %s: %w", msg.BurnCoins, err)
if err := ms.k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, req.BurnCoins); err != nil {
return nil, fmt.Errorf("not enough balance to burn %s: %w", req.BurnCoins, err)
}

return &types.MsgBurnHeldBalanceResponse{}, ms.k.bankKeeper.BurnCoins(ctx, types.ModuleName, msg.BurnCoins)
return &types.MsgBurnHeldBalanceResponse{}, ms.k.bankKeeper.BurnCoins(ctx, types.ModuleName, req.BurnCoins)
}
40 changes: 28 additions & 12 deletions x/manifest/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,14 @@ func TestBurnCoins(t *testing.T) {
k := f.App.ManifestKeeper
k.SetAuthority(authority.String())
ms := keeper.NewMsgServerImpl(k)
_, _, acc := testdata.KeyTestPubAddr()

type tc struct {
name string
initial sdk.Coins
burn sdk.Coins
expected sdk.Coins
address string
address sdk.AccAddress
success bool
}

Expand All @@ -148,50 +149,55 @@ func TestBurnCoins(t *testing.T) {
initial: sdk.NewCoins(),
burn: sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(7))),
expected: sdk.NewCoins(),
address: authority,
},
{
name: "fail; bad address",
initial: sdk.NewCoins(),
burn: sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(7))),
expected: sdk.NewCoins(),
address: "xyz",
address: sdk.AccAddress{0x0},
},
{
name: "success; burn 1 token successfully",
name: "success; burn tokens successfully",
initial: sdk.NewCoins(stake, mfx),
burn: sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(7))),
expected: sdk.NewCoins(mfx, stake.SubAmount(sdkmath.NewInt(7))),
address: authority,
success: true,
},
{
name: "success; burn many tokens successfully",
initial: sdk.NewCoins(stake, mfx),
burn: sdk.NewCoins(sdk.NewCoin("umfx", sdkmath.NewInt(9)), sdk.NewCoin("stake", sdkmath.NewInt(7))),
expected: sdk.NewCoins(mfx.SubAmount(sdkmath.NewInt(9)), stake.SubAmount(sdkmath.NewInt(7))),
address: authority,
success: true,
},
{
name: "fail; invalid authority",
initial: sdk.NewCoins(stake, mfx),
burn: sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(7))),
expected: sdk.NewCoins(stake, mfx),
address: acc,
},
}

for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
_, _, acc := testdata.KeyTestPubAddr()
if c.address == "" {
c.address = acc.String()
}

// setup initial balances for the new account
if len(c.initial) > 0 {
require.NoError(t, f.App.BankKeeper.MintCoins(f.Ctx, "mint", c.initial))
require.NoError(t, f.App.BankKeeper.SendCoinsFromModuleToAccount(f.Ctx, "mint", acc, c.initial))
require.NoError(t, f.App.BankKeeper.SendCoinsFromModuleToAccount(f.Ctx, "mint", c.address, c.initial))
}

// validate initial balance
require.Equal(t, c.initial, f.App.BankKeeper.GetAllBalances(f.Ctx, acc))
require.Equal(t, c.initial, f.App.BankKeeper.GetAllBalances(f.Ctx, c.address))

// burn coins
_, err := ms.BurnHeldBalance(f.Ctx, &types.MsgBurnHeldBalance{
Sender: c.address,
Authority: c.address.String(),
BurnCoins: c.burn,
})
if c.success {
Expand All @@ -200,7 +206,17 @@ func TestBurnCoins(t *testing.T) {
require.Error(t, err)
}

require.Equal(t, c.expected, f.App.BankKeeper.GetAllBalances(f.Ctx, acc))
allBalance := f.App.BankKeeper.GetAllBalances(f.Ctx, c.address)
require.Equal(t, c.expected, allBalance)

// burn the rest of the coins to reset the balance to 0 for the next test if the test was successful
if c.success {
_, err = ms.BurnHeldBalance(f.Ctx, &types.MsgBurnHeldBalance{
Authority: c.address.String(),
BurnCoins: allBalance,
})
require.NoError(t, err)
}
})
}
}
6 changes: 3 additions & 3 deletions x/manifest/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func NewMsgBurnHeldBalance(
coins sdk.Coins,
) *MsgBurnHeldBalance {
return &MsgBurnHeldBalance{
Sender: sender.String(),
Authority: sender.String(),
BurnCoins: coins,
}
}
Expand All @@ -151,13 +151,13 @@ func (msg MsgBurnHeldBalance) GetSignBytes() []byte {

// GetSigners returns the expected signers for the message.
func (msg *MsgBurnHeldBalance) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(msg.Sender)
addr, _ := sdk.AccAddressFromBech32(msg.Authority)
return []sdk.AccAddress{addr}
}

// ValidateBasic does a sanity check on the provided data.
func (msg *MsgBurnHeldBalance) Validate() error {
if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil {
if _, err := sdk.AccAddressFromBech32(msg.Authority); err != nil {
return errors.Wrap(err, "invalid authority address")
}

Expand Down
99 changes: 49 additions & 50 deletions x/manifest/types/tx.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 850a9dd

Please sign in to comment.