From 9878c519c683ca7a2d8ae96f79b4d782a2b657dc Mon Sep 17 00:00:00 2001 From: Segue Date: Thu, 5 Sep 2019 16:46:40 +0800 Subject: [PATCH 1/3] add claim and refund for htlc --- app/v2/htlc/abci.go | 9 +- app/v2/htlc/alias.go | 11 +- app/v2/htlc/handler.go | 35 +++++- app/v2/htlc/internal/keeper/keeper.go | 85 +++++++++++++++ app/v2/htlc/internal/types/errors.go | 15 +++ app/v2/htlc/internal/types/htlc.go | 31 +++++- app/v2/htlc/internal/types/msgs.go | 146 +++++++++++++++++++++++++- app/v2/htlc/internal/types/tags.go | 1 + types/contexttags.go | 3 + 9 files changed, 328 insertions(+), 8 deletions(-) diff --git a/app/v2/htlc/abci.go b/app/v2/htlc/abci.go index 0063110d6..fa01fe98b 100644 --- a/app/v2/htlc/abci.go +++ b/app/v2/htlc/abci.go @@ -6,7 +6,14 @@ import ( // EndBlocker handles block ending logic func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { - // TODO + // TODO: check and reset state (Open => Expired) + + + + // TODO: alternative + // check timeout => + // refund => + // delete HTLC from expire queue return nil } diff --git a/app/v2/htlc/alias.go b/app/v2/htlc/alias.go index a7dbcc7d4..4443a4da9 100644 --- a/app/v2/htlc/alias.go +++ b/app/v2/htlc/alias.go @@ -8,7 +8,10 @@ import ( // exported types type ( MsgCreateHTLC = types.MsgCreateHTLC - HTLC = types.HTLC + MsgClaimHTLC = types.MsgClaimHTLC + MsgRefundHTLC = types.MsgRefundHTLC + + HTLC = types.HTLC Params = types.Params GenesisState = types.GenesisState @@ -28,8 +31,14 @@ var ( RegisterCodec = types.RegisterCodec NewMsgCreateHTLC = types.NewMsgCreateHTLC + NewMsgClaimHTLC = types.NewMsgClaimHTLC + NewMsgRefundHTLC = types.NewMsgRefundHTLC NewHTLC = types.NewHTLC + StateOpen = types.StateOpen + StateCompleted = types.StateCompleted + StateExpired = types.StateExpired + ValidateSecretHashLock = types.ValidateSecretHashLock QueryHTLC = types.QueryHTLC diff --git a/app/v2/htlc/handler.go b/app/v2/htlc/handler.go index 6fbf6e084..46348d5db 100644 --- a/app/v2/htlc/handler.go +++ b/app/v2/htlc/handler.go @@ -12,11 +12,13 @@ func NewHandler(k Keeper) sdk.Handler { switch msg := msg.(type) { case MsgCreateHTLC: return handleMsgCreateHTLC(ctx, k, msg) + case MsgClaimHTLC: + return handleMsgClaimHTLC(ctx, k, msg) + case MsgRefundHTLC: + return handleMsgRefundHTLC(ctx, k, msg) default: return sdk.ErrTxDecode("invalid message parsed in HTLC module").Result() } - - return sdk.ErrTxDecode("invalid message parsed in HTLC module").Result() } } @@ -24,7 +26,7 @@ func NewHandler(k Keeper) sdk.Handler { func handleMsgCreateHTLC(ctx sdk.Context, k Keeper, msg MsgCreateHTLC) sdk.Result { secret := make([]byte, 32) expireHeight := msg.TimeLock + uint64(ctx.BlockHeight()) - state := uint8(0) + state := StateOpen htlc := NewHTLC(msg.Sender, msg.Receiver, msg.ReceiverOnOtherChain, msg.OutAmount, msg.InAmount, secret, msg.Timestamp, expireHeight, state) secretHashLock, _ := hex.DecodeString(msg.SecretHashLock) @@ -38,3 +40,30 @@ func handleMsgCreateHTLC(ctx sdk.Context, k Keeper, msg MsgCreateHTLC) sdk.Resul Tags: tags, } } + +// handleMsgClaimHTLC handles MsgClaimHTLC +func handleMsgClaimHTLC(ctx sdk.Context, k Keeper, msg MsgClaimHTLC) sdk.Result { + secret, _ := hex.DecodeString(msg.Secret) + secretHash, _ := hex.DecodeString(msg.SecretHashLock) + tags, err := k.ClaimHTLC(ctx, secret, secretHash) + if err != nil { + return err.Result() + } + + return sdk.Result{ + Tags: tags, + } +} + +// handleMsgRefundHTLC handles MsgRefundHTLC +func handleMsgRefundHTLC(ctx sdk.Context, k Keeper, msg MsgRefundHTLC) sdk.Result { + secretHash, _ := hex.DecodeString(msg.SecretHashLock) + tags, err := k.RefundHTLC(ctx, secretHash) + if err != nil { + return err.Result() + } + + return sdk.Result{ + Tags: tags, + } +} diff --git a/app/v2/htlc/internal/keeper/keeper.go b/app/v2/htlc/internal/keeper/keeper.go index 9aeb6ff92..d83b53801 100644 --- a/app/v2/htlc/internal/keeper/keeper.go +++ b/app/v2/htlc/internal/keeper/keeper.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "encoding/hex" "fmt" @@ -55,6 +56,9 @@ func (k Keeper) CreateHTLC(ctx sdk.Context, htlc types.HTLC, secretHashLock []by return nil, err } + // add to coinflow + ctx.CoinFlowTags().AppendCoinFlowTag(ctx, htlc.Sender.String(), htlcAddr.String(), htlc.OutAmount.String(), sdk.CoinHTLCCreateFlow, "") + // set the htlc k.SetHTLC(ctx, htlc, secretHashLock) @@ -71,6 +75,87 @@ func (k Keeper) CreateHTLC(ctx sdk.Context, htlc types.HTLC, secretHashLock []by return createTags, nil } +func (k Keeper) ClaimHTLC(ctx sdk.Context, secret []byte, secretHashLock []byte) (sdk.Tags, sdk.Error) { + + // get the htlc + htlc, err := k.GetHTLC(ctx, secretHashLock) + if err != nil { + return nil, err + } + + // check if not open + if htlc.State != types.StateOpen { + return nil, types.ErrStateIsNotOpen(k.codespace, fmt.Sprintf("HTLC state is not Open.")) + } + + // check if secret not valid + if !bytes.Equal(k.GetSecretHashLock(secret, htlc.Timestamp), secretHashLock) { + return nil, types.ErrInvalidSecret(k.codespace, fmt.Sprintf("invalid secret: %s", hex.EncodeToString(secret))) + } + + // do claim + htlcAddr := getHTLCAddress(htlc.OutAmount.Denom) + if _, err := k.bk.SendCoins(ctx, htlcAddr, htlc.Receiver, sdk.Coins{htlc.OutAmount}); err != nil { + return nil, err + } + + // update secret and state in HTLC + htlc.Secret = secret + htlc.State = types.StateCompleted + k.SetHTLC(ctx, htlc, secretHashLock) + + // add to coinflow + ctx.CoinFlowTags().AppendCoinFlowTag(ctx, htlcAddr.String(), htlc.Receiver.String(), htlc.OutAmount.String(), sdk.CoinHTLCClaimFlow, "") + + calimTags := sdk.NewTags( + types.TagSender, []byte(htlc.Sender), + types.TagReceiver, []byte(htlc.Receiver), + types.TagSecretHashLock, []byte(hex.EncodeToString(secretHashLock)), + types.TagSecret, []byte(hex.EncodeToString(secret)), + ) + + return calimTags, nil +} + +func (k Keeper) RefundHTLC(ctx sdk.Context, secretHashLock []byte) (sdk.Tags, sdk.Error) { + + // get the htlc + htlc, err := k.GetHTLC(ctx, secretHashLock) + if err != nil { + return nil, err + } + + // check if not expired + if htlc.State != types.StateExpired { + return nil, types.ErrStateIsNotOpen(k.codespace, fmt.Sprintf("HTLC state is not Expired.")) + } + + // do refund + htlcAddr := getHTLCAddress(htlc.OutAmount.Denom) + if _, err := k.bk.SendCoins(ctx, htlcAddr, htlc.Sender, sdk.Coins{htlc.OutAmount}); err != nil { + return nil, err + } + + // update state in HTLC + htlc.State = types.StateRefunded + k.SetHTLC(ctx, htlc, secretHashLock) + + // add to coinflow + ctx.CoinFlowTags().AppendCoinFlowTag(ctx, htlcAddr.String(), htlc.Sender.String(), htlc.OutAmount.String(), sdk.CoinHTLCRefundFlow, "") + + refundTags := sdk.NewTags( + types.TagSender, []byte(htlc.Sender), + types.TagSecretHashLock, []byte(hex.EncodeToString(secretHashLock)), + ) + + return refundTags, nil +} + +// GetSecretHashLock calculates the secret hash lock +func (k Keeper) GetSecretHashLock(secret []byte, timestamp uint64) []byte { + return sdk.SHA256(append(secret, sdk.Uint64ToBigEndian(timestamp)...)) +} + func (k Keeper) HasSecretHashLock(ctx sdk.Context, secretHashLock []byte) bool { store := ctx.KVStore(k.storeKey) return store.Has(KeyHTLC(secretHashLock)) diff --git a/app/v2/htlc/internal/types/errors.go b/app/v2/htlc/internal/types/errors.go index 4eb11ad32..394cc9b2c 100644 --- a/app/v2/htlc/internal/types/errors.go +++ b/app/v2/htlc/internal/types/errors.go @@ -14,6 +14,9 @@ const ( CodeInvalidSecretHashLock sdk.CodeType = 102 CodeSecretHashLockAlreadyExists sdk.CodeType = 103 CodeInvalidTimeLock sdk.CodeType = 104 + CodeInvalidSecret sdk.CodeType = 105 + CodeStateIsNotOpen sdk.CodeType = 106 + CodeStateIsNotExpired sdk.CodeType = 107 ) //---------------------------------------- @@ -38,3 +41,15 @@ func ErrSecretHashLockAlreadyExists(codespace sdk.CodespaceType, msg string) sdk func ErrInvalidTimeLock(codespace sdk.CodespaceType, msg string) sdk.Error { return sdk.NewError(codespace, CodeInvalidTimeLock, msg) } + +func ErrInvalidSecret(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidSecret, msg) +} + +func ErrStateIsNotOpen(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeStateIsNotOpen, msg) +} + +func ErrStateIsNotExpired(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeStateIsNotExpired, msg) +} diff --git a/app/v2/htlc/internal/types/htlc.go b/app/v2/htlc/internal/types/htlc.go index 7f2b47285..fbc22f649 100644 --- a/app/v2/htlc/internal/types/htlc.go +++ b/app/v2/htlc/internal/types/htlc.go @@ -7,6 +7,14 @@ import ( sdk "github.com/irisnet/irishub/types" ) +// the state of the HTLC +const ( + StateOpen = uint8(0) // can claim + StateCompleted = uint8(1) // claimed + StateExpired = uint8(2) // Expired + StateRefunded = uint8(3) // Refunded +) + // HTLC represents a HTLC type HTLC struct { Sender sdk.AccAddress `json:"sender"` // the initiator address @@ -21,7 +29,17 @@ type HTLC struct { } // NewHTLC constructs a HTLC -func NewHTLC(sender sdk.AccAddress, receiver sdk.AccAddress, receiverOnOtherChain []byte, outAmount sdk.Coin, inAmount uint64, secret []byte, timestamp uint64, expireHeight uint64, state uint8) HTLC { +func NewHTLC( + sender sdk.AccAddress, + receiver sdk.AccAddress, + receiverOnOtherChain []byte, + outAmount sdk.Coin, + inAmount uint64, + secret []byte, + timestamp uint64, + expireHeight uint64, + state uint8, +) HTLC { return HTLC{ Sender: sender, Receiver: receiver, @@ -52,5 +70,14 @@ func (h HTLC) String() string { Timestamp: %d ExpireHeight: %d State: %d`, - h.Sender, h.Receiver, h.ReceiverOnOtherChain, h.OutAmount.String(), h.InAmount, hex.EncodeToString(h.Secret), h.Timestamp, h.ExpireHeight, h.State) + h.Sender, + h.Receiver, + h.ReceiverOnOtherChain, + h.OutAmount.String(), + h.InAmount, + hex.EncodeToString(h.Secret), + h.Timestamp, + h.ExpireHeight, + h.State, + ) } diff --git a/app/v2/htlc/internal/types/msgs.go b/app/v2/htlc/internal/types/msgs.go index d6679b40e..cd45f610d 100644 --- a/app/v2/htlc/internal/types/msgs.go +++ b/app/v2/htlc/internal/types/msgs.go @@ -14,6 +14,12 @@ const ( // type for MsgCreateHTLC TypeMsgCreateHTLC = "create_htlc" + // type for MsgClaimHTLC + TypeMsgClaimHTLC = "claim_htlc" + + // type for MsgRefundHTLC + TypeMsgRefundHTLC = "refund_htlc" + SecretLength = 32 // the length for secret MaxLengthForAddressOnOtherChain = 32 // maximal length in bytes for the address on other chains DecimalNumForInAmount = 8 // the default decimal number for InAmount @@ -22,6 +28,8 @@ const ( ) var _ sdk.Msg = &MsgCreateHTLC{} +var _ sdk.Msg = &MsgClaimHTLC{} +var _ sdk.Msg = &MsgRefundHTLC{} // MsgCreateHTLC represents a msg for creating a HTLC type MsgCreateHTLC struct { @@ -36,7 +44,16 @@ type MsgCreateHTLC struct { } // NewMsgCreateHTLC constructs a MsgCreateHTLC -func NewMsgCreateHTLC(sender sdk.AccAddress, receiver sdk.AccAddress, receiverOnOtherChain []byte, outAmount sdk.Coin, inAmount uint64, secretHashLock string, timestamp uint64, timeLock uint64) MsgCreateHTLC { +func NewMsgCreateHTLC( + sender sdk.AccAddress, + receiver sdk.AccAddress, + receiverOnOtherChain []byte, + outAmount sdk.Coin, + inAmount uint64, + secretHashLock string, + timestamp uint64, + timeLock uint64, +) MsgCreateHTLC { return MsgCreateHTLC{ Sender: sender, Receiver: receiver, @@ -112,3 +129,130 @@ func (msg MsgCreateHTLC) GetSignBytes() []byte { func (msg MsgCreateHTLC) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} } + +// ----------------------------------------------------------------------------- + +// MsgClaimHTLC represents a msg for claim a HTLC +type MsgClaimHTLC struct { + Sender sdk.AccAddress `json:"sender"` // the initiator address + Secret string `json:"secret"` // the secret for claim + SecretHashLock string `json:"secret_hash_lock"` // the hash lock generated from secret and timestamp +} + +// NewMsgClaimHTLC constructs a MsgClaimHTLC +func NewMsgClaimHTLC( + sender sdk.AccAddress, + secret string, + secretHashLock string, +) MsgClaimHTLC { + return MsgClaimHTLC{ + Sender: sender, + Secret: secret, + SecretHashLock: secretHashLock, + } +} + +// Implements Msg. +func (msg MsgClaimHTLC) Route() string { return MsgRoute } + +// Implements Msg. +func (msg MsgClaimHTLC) Type() string { return TypeMsgClaimHTLC } + +// Implements Msg. +func (msg MsgClaimHTLC) ValidateBasic() sdk.Error { + if len(msg.Sender) == 0 { + return ErrInvalidAddress(DefaultCodespace, "the sender address must be specified") + } + + if err := ValidateSecret(msg.Secret); err != nil { + return ErrInvalidSecret(DefaultCodespace, err.Error()) + } + + if err := ValidateSecretHashLock(msg.SecretHashLock); err != nil { + return ErrInvalidSecretHashLock(DefaultCodespace, err.Error()) + } + + return nil +} + +// ValidateSecretHashLock validates the secret hash lock +func ValidateSecret(secret string) sdk.Error { + secretHex, err := hex.DecodeString(secret) + if err != nil { + return ErrInvalidSecret(DefaultCodespace, fmt.Sprintf("invalid secret: %s", err.Error())) + } + + if len(secretHex) != 32 { + return ErrInvalidSecret(DefaultCodespace, fmt.Sprintf("invalid secret: %s", secretHex)) + } + + return nil +} + +// Implements Msg. +func (msg MsgClaimHTLC) GetSignBytes() []byte { + b, err := msgCdc.MarshalJSON(msg) + if err != nil { + panic(err) + } + + return sdk.MustSortJSON(b) +} + +// Implements Msg. +func (msg MsgClaimHTLC) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Sender} +} + +// ----------------------------------------------------------------------------- + +// MsgRefundHTLC represents a msg for refund a HTLC +type MsgRefundHTLC struct { + Sender sdk.AccAddress `json:"sender"` // the initiator address + SecretHashLock string `json:"secret_hash_lock"` // the hash lock generated from secret and timestamp +} + +// NewMsgClaimHTLC constructs a MsgClaimHTLC +func NewMsgRefundHTLC( + sender sdk.AccAddress, + secretHashLock string, +) MsgRefundHTLC { + return MsgRefundHTLC{ + Sender: sender, + SecretHashLock: secretHashLock, + } +} + +// Implements Msg. +func (msg MsgRefundHTLC) Route() string { return MsgRoute } + +// Implements Msg. +func (msg MsgRefundHTLC) Type() string { return TypeMsgRefundHTLC } + +// Implements Msg. +func (msg MsgRefundHTLC) ValidateBasic() sdk.Error { + if len(msg.Sender) == 0 { + return ErrInvalidAddress(DefaultCodespace, "the sender address must be specified") + } + + if err := ValidateSecretHashLock(msg.SecretHashLock); err != nil { + return ErrInvalidSecretHashLock(DefaultCodespace, err.Error()) + } + + return nil +} + +// Implements Msg. +func (msg MsgRefundHTLC) GetSignBytes() []byte { + b, err := msgCdc.MarshalJSON(msg) + if err != nil { + panic(err) + } + + return sdk.MustSortJSON(b) +} + +// Implements Msg. +func (msg MsgRefundHTLC) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Sender} +} diff --git a/app/v2/htlc/internal/types/tags.go b/app/v2/htlc/internal/types/tags.go index 1bf16d79e..7e81db0d3 100644 --- a/app/v2/htlc/internal/types/tags.go +++ b/app/v2/htlc/internal/types/tags.go @@ -6,4 +6,5 @@ var ( TagReceiver = "receiver" TagReceiverOnOtherChain = "receiver-on-other-chain" TagSecretHashLock = "secret-hash-lock" + TagSecret = "secret" ) diff --git a/types/contexttags.go b/types/contexttags.go index 98a70336a..993844de9 100644 --- a/types/contexttags.go +++ b/types/contexttags.go @@ -27,6 +27,9 @@ const ( CoinSwapOutputFlow = "CoinSwapOutput" CoinSwapAddLiquidityFlow = "AddLiquidity" CoinSwapRemoveLiquidityFlow = "RemoveLiquidity" + CoinHTLCCreateFlow = "CreateHTLC" + CoinHTLCClaimFlow = "ClaimHTLC" + CoinHTLCRefundFlow = "RefundHTLC" //Trigger: transaction hash, module endBlock GovEndBlocker = "govEndBlocker" From 2aed1ef71091034ac279e0a48acdb42df005b3f9 Mon Sep 17 00:00:00 2001 From: Segue Date: Thu, 5 Sep 2019 17:14:47 +0800 Subject: [PATCH 2/3] fix --- app/v2/htlc/internal/keeper/keeper.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/v2/htlc/internal/keeper/keeper.go b/app/v2/htlc/internal/keeper/keeper.go index d83b53801..882e719c5 100644 --- a/app/v2/htlc/internal/keeper/keeper.go +++ b/app/v2/htlc/internal/keeper/keeper.go @@ -66,8 +66,8 @@ func (k Keeper) CreateHTLC(ctx sdk.Context, htlc types.HTLC, secretHashLock []by k.AddHTLCToExpireQueue(ctx, htlc.ExpireHeight, secretHashLock) createTags := sdk.NewTags( - types.TagSender, []byte(htlc.Sender), - types.TagReceiver, []byte(htlc.Receiver), + types.TagSender, []byte(htlc.Sender.String()), + types.TagReceiver, []byte(htlc.Receiver.String()), types.TagReceiverOnOtherChain, htlc.ReceiverOnOtherChain, types.TagSecretHashLock, []byte(hex.EncodeToString(secretHashLock)), ) @@ -108,8 +108,8 @@ func (k Keeper) ClaimHTLC(ctx sdk.Context, secret []byte, secretHashLock []byte) ctx.CoinFlowTags().AppendCoinFlowTag(ctx, htlcAddr.String(), htlc.Receiver.String(), htlc.OutAmount.String(), sdk.CoinHTLCClaimFlow, "") calimTags := sdk.NewTags( - types.TagSender, []byte(htlc.Sender), - types.TagReceiver, []byte(htlc.Receiver), + types.TagSender, []byte(htlc.Sender.String()), + types.TagReceiver, []byte(htlc.Receiver.String()), types.TagSecretHashLock, []byte(hex.EncodeToString(secretHashLock)), types.TagSecret, []byte(hex.EncodeToString(secret)), ) @@ -144,7 +144,7 @@ func (k Keeper) RefundHTLC(ctx sdk.Context, secretHashLock []byte) (sdk.Tags, sd ctx.CoinFlowTags().AppendCoinFlowTag(ctx, htlcAddr.String(), htlc.Sender.String(), htlc.OutAmount.String(), sdk.CoinHTLCRefundFlow, "") refundTags := sdk.NewTags( - types.TagSender, []byte(htlc.Sender), + types.TagSender, []byte(htlc.Sender.String()), types.TagSecretHashLock, []byte(hex.EncodeToString(secretHashLock)), ) From 6ed0eff6ba655e1ec2dc038cbcbe7b005e78cf48 Mon Sep 17 00:00:00 2001 From: Segue Date: Thu, 5 Sep 2019 19:14:41 +0800 Subject: [PATCH 3/3] handle htlc expiration in EndBlocker --- app/v2/htlc/abci.go | 29 +++++++++++--- app/v2/htlc/internal/keeper/keeper.go | 7 ++++ app/v2/htlc/internal/keeper/keeper_keys.go | 5 +++ app/v2/htlc/internal/keeper/keeper_test.go | 10 ++++- app/v2/htlc/internal/types/msgs_test.go | 44 ++++++++++++++++++++++ 5 files changed, 88 insertions(+), 7 deletions(-) diff --git a/app/v2/htlc/abci.go b/app/v2/htlc/abci.go index fa01fe98b..d21e02bca 100644 --- a/app/v2/htlc/abci.go +++ b/app/v2/htlc/abci.go @@ -1,19 +1,36 @@ package htlc import ( + "encoding/hex" + "fmt" + + "github.com/irisnet/irishub/app/v2/htlc/internal/types" sdk "github.com/irisnet/irishub/types" ) // EndBlocker handles block ending logic -func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { - // TODO: check and reset state (Open => Expired) - +func EndBlocker(ctx sdk.Context, k Keeper) (resTags sdk.Tags) { + // check htlc expire and set state from Open to Expired + ctx = ctx.WithLogger(ctx.Logger().With("handler", "EndBlock").With("module", "iris/htlc")) + + currentBlockHeight := uint64(ctx.BlockHeight()) + iterator := k.IterateHTLCExpireQueueByHeight(ctx, currentBlockHeight) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + + secretHashLock := iterator.Key() + htlc, _ := k.GetHTLC(ctx, secretHashLock) + + htlc.State = types.StateExpired + k.SetHTLC(ctx, htlc, secretHashLock) + k.DeleteHTLCFromExpireQueue(ctx, currentBlockHeight, secretHashLock) + ctx.Logger().Info(fmt.Sprintf("HTLC [%s] is expired", hex.EncodeToString(secretHashLock))) + } // TODO: alternative - // check timeout => - // refund => - // delete HTLC from expire queue + // check expire => refund => delete HTLC from expire queue return nil } diff --git a/app/v2/htlc/internal/keeper/keeper.go b/app/v2/htlc/internal/keeper/keeper.go index 882e719c5..26c0f318d 100644 --- a/app/v2/htlc/internal/keeper/keeper.go +++ b/app/v2/htlc/internal/keeper/keeper.go @@ -103,6 +103,7 @@ func (k Keeper) ClaimHTLC(ctx sdk.Context, secret []byte, secretHashLock []byte) htlc.Secret = secret htlc.State = types.StateCompleted k.SetHTLC(ctx, htlc, secretHashLock) + k.DeleteHTLCFromExpireQueue(ctx, uint64(ctx.BlockHeight()), secretHashLock) // add to coinflow ctx.CoinFlowTags().AppendCoinFlowTag(ctx, htlcAddr.String(), htlc.Receiver.String(), htlc.OutAmount.String(), sdk.CoinHTLCClaimFlow, "") @@ -204,3 +205,9 @@ func (k Keeper) DeleteHTLCFromExpireQueue(ctx sdk.Context, expireHeight uint64, func getHTLCAddress(denom string) sdk.AccAddress { return sdk.AccAddress(crypto.AddressHash([]byte(denom))) } + +// IterateHTLCExpireQueueByHeight iterates the HTLC expire queue by the specified height +func (k Keeper) IterateHTLCExpireQueueByHeight(ctx sdk.Context, height uint64) sdk.Iterator { + store := ctx.KVStore(k.storeKey) + return sdk.KVStorePrefixIterator(store, KeyHTLCExpireQueueSubspace(height)) +} diff --git a/app/v2/htlc/internal/keeper/keeper_keys.go b/app/v2/htlc/internal/keeper/keeper_keys.go index 7f45616f8..f12d6eeaf 100644 --- a/app/v2/htlc/internal/keeper/keeper_keys.go +++ b/app/v2/htlc/internal/keeper/keeper_keys.go @@ -18,3 +18,8 @@ func KeyHTLC(secretHashLock []byte) []byte { func KeyHTLCExpireQueue(expireHeight uint64, secretHashLock []byte) []byte { return append(append(PrefixHTLCExpireQueue, sdk.Uint64ToBigEndian(expireHeight)...), secretHashLock...) } + +// KeyHTLCExpireQueueSubspace returns the key prefix for HTLC expiration queue +func KeyHTLCExpireQueueSubspace(expireHeight uint64) []byte { + return append(PrefixHTLCExpireQueue, sdk.Uint64ToBigEndian(expireHeight)...) +} diff --git a/app/v2/htlc/internal/keeper/keeper_test.go b/app/v2/htlc/internal/keeper/keeper_test.go index a46c45dea..5d79a8a14 100644 --- a/app/v2/htlc/internal/keeper/keeper_test.go +++ b/app/v2/htlc/internal/keeper/keeper_test.go @@ -20,5 +20,13 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) { } func TestKeeper_CreateHTLC(t *testing.T) { - // TODO + // TODO: +} + +func TestKeeper_ClaimHTLC(t *testing.T) { + // TODO: +} + +func TestKeeper_RefundHTLC(t *testing.T) { + // TODO: } diff --git a/app/v2/htlc/internal/types/msgs_test.go b/app/v2/htlc/internal/types/msgs_test.go index ab5fced3f..4da81303a 100644 --- a/app/v2/htlc/internal/types/msgs_test.go +++ b/app/v2/htlc/internal/types/msgs_test.go @@ -61,3 +61,47 @@ func TestMsgCreateHTLCGetSigners(t *testing.T) { expected := "[73656E646572]" require.Equal(t, expected, fmt.Sprintf("%v", res)) } + +// TODO: Claim HTLC test + +func TestNewMsgClaimHTLC(t *testing.T) { + // TODO: +} + +func TestMsgClaimHTLCRoute(t *testing.T) { + // TODO: +} + +func TestMsgClaimHTLCValidation(t *testing.T) { + // TODO: +} + +func TestMsgClaimHTLCGetSignBytes(t *testing.T) { + // TODO: +} + +func TestMsgClaimHTLCGetSigners(t *testing.T) { + // TODO: +} + +// TODO: Refund HTLC test + +func TestNewMsgRefundHTLC(t *testing.T) { +// TODO: +} + +func TestMsgRefundHTLCRoute(t *testing.T) { + // TODO: +} + +func TestMsgRefundHTLCValidation(t *testing.T) { + // TODO: +} + +func TestMsgRefundHTLCGetSignBytes(t *testing.T) { + // TODO: +} + +func TestMsgRefundHTLCGetSigners(t *testing.T) { + // TODO: +}