func (k Keeper) closeChannel(ctx sdk.Context, update types.Update) (sdk.Tags, sdk.Error) {
	var err sdk.Error
	channel, _ := k.getChannel(ctx, update.ChannelID)
	for i, coins := range update.Payout {
		_, err = k.bankKeeper.AddCoins(ctx, channel.Participants[i], coins)
		if err != nil {
			panic(err)
		}
	}
	k.deleteChannel(ctx, update.ChannelID)
	return sdk.EmptyTags(), nil
}
closeChannel closes a payment channel without any checks.
It doesn't check if the given update matches an existing channel.
package paychan
import (
	"bytes"
	"github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/x/bank"
	"github.com/kava-labs/cosmos-paychan/paychan/types"
)
type Keeper struct {
	storeKey   sdk.StoreKey
	cdc        *codec.Codec
	bankKeeper bank.Keeper
}
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, bk bank.Keeper) Keeper {
	keeper := Keeper{
		storeKey:   key,
		cdc:        cdc,
		bankKeeper: bk,
	}
	return keeper
}
func (k Keeper) CreateChannel(ctx sdk.Context, sender sdk.AccAddress, receiver sdk.AccAddress, coins sdk.Coins) (sdk.Tags, sdk.Error) {
	if sender.Empty() {
		return nil, sdk.ErrInvalidAddress(sender.String())
	}
	if receiver.Empty() {
		return nil, sdk.ErrInvalidAddress(receiver.String())
	}
	if !coins.IsValid() {
		return nil, sdk.ErrInvalidCoins(coins.String())
	}
	if !coins.IsAllPositive() {
		return nil, sdk.ErrInvalidCoins(coins.String())
	}
	_, err := k.bankKeeper.SubtractCoins(ctx, sender, coins)
	if err != nil {
		return nil, err
	}
	id := k.getNewChannelID(ctx)
	channel := types.Channel{
		ID:           id,
		Participants: [2]sdk.AccAddress{sender, receiver},
		Coins:        coins,
	}
	k.setChannel(ctx, channel)
	return sdk.EmptyTags(), err
}
func (k Keeper) InitCloseChannelBySender(ctx sdk.Context, update types.Update) (sdk.Tags, sdk.Error) {
	channel, found := k.getChannel(ctx, update.ChannelID)
	if !found {
		return nil, sdk.ErrInternal("Channel doesn't exist")
	}
	err := VerifyUpdate(channel, update)
	if err != nil {
		return nil, err
	}
	q := k.getSubmittedUpdatesQueue(ctx)
	if q.Contains(update.ChannelID) {
		return nil, sdk.ErrInternal("Sender can't submit an update for channel if one has already been submitted.")
	} else {
		submittedUpdate := types.SubmittedUpdate{
			Update:        update,
			ExecutionTime: ctx.BlockHeight() + types.ChannelDisputeTime,
		}
		k.addToSubmittedUpdatesQueue(ctx, submittedUpdate)
	}
	return sdk.EmptyTags(), nil
}
func (k Keeper) CloseChannelByReceiver(ctx sdk.Context, update types.Update) (sdk.Tags, sdk.Error) {
	channel, found := k.getChannel(ctx, update.ChannelID)
	if !found {
		return nil, sdk.ErrInternal("Channel doesn't exist")
	}
	err := VerifyUpdate(channel, update)
	if err != nil {
		return nil, err
	}
	q := k.getSubmittedUpdatesQueue(ctx)
	if q.Contains(update.ChannelID) {
		k.removeFromSubmittedUpdatesQueue(ctx, update.ChannelID)
	}
	tags, err := k.closeChannel(ctx, update)
	return tags, err
}
func VerifyUpdate(channel types.Channel, update types.Update) sdk.Error {
	if len(update.Payout) != len(channel.Participants) {
		return sdk.ErrInternal("Payout doesn't match number of channel participants")
	}
	for _, coins := range update.Payout {
		if !coins.IsValid() {
			return sdk.ErrInternal("Payout coins aren't formatted correctly")
		}
	}
	if update.Payout.IsAnyNegative() {
		return sdk.ErrInternal("Payout cannot be negative")
	}
	if !channel.Coins.IsEqual(update.Payout.Sum()) {
		return sdk.ErrInternal("Payout amount doesn't match channel amount")
	}
	if !verifySignatures(channel, update) {
		return sdk.ErrInternal("Signature on update not valid")
	}
	return nil
}