diff --git a/x/bep3/abci.go b/x/bep3/abci.go index b5c2d424..8675da52 100644 --- a/x/bep3/abci.go +++ b/x/bep3/abci.go @@ -4,15 +4,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// BeginBlocker runs at the start of every block +// BeginBlocker on every block expires outdated atomic swaps and removes closed +// swap from long term storage (default storage time of 1 week) func BeginBlocker(ctx sdk.Context, k Keeper) { - err := k.UpdateExpiredAtomicSwaps(ctx) - if err != nil { - panic(err) - } - - err = k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx) - if err != nil { - panic(err) - } + k.UpdateExpiredAtomicSwaps(ctx) + k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx) } diff --git a/x/bep3/abci_test.go b/x/bep3/abci_test.go index 67da0c39..3d5af4db 100644 --- a/x/bep3/abci_test.go +++ b/x/bep3/abci_test.go @@ -52,22 +52,22 @@ func (suite *ABCITestSuite) ResetKeeper() { var randomNumbers []tmbytes.HexBytes for i := 0; i < 10; i++ { // Set up atomic swap variables - expireHeight := int64(360) + expireHeight := uint64(360) amount := cs(c("bnb", int64(100))) timestamp := ts(i) randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) // Create atomic swap and check err to confirm creation err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight, suite.addrs[0], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain, - amount, amount.String(), true) + amount, true) suite.Nil(err) // Store swap's calculated ID and secret random number swapID := bep3.CalculateSwapID(randomNumberHash, suite.addrs[0], TestSenderOtherChain) swapIDs = append(swapIDs, swapID) - randomNumbers = append(randomNumbers, randomNumber.Bytes()) + randomNumbers = append(randomNumbers, randomNumber[:]) } suite.swapIDs = swapIDs suite.randomNumbers = randomNumbers @@ -105,7 +105,7 @@ func (suite *ABCITestSuite) TestBeginBlocker_UpdateExpiredAtomicSwaps() { { name: "after deletion", firstCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400), - secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400 + bep3.DefaultLongtermStorageDuration), + secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400 + int64(bep3.DefaultLongtermStorageDuration)), expectedStatus: bep3.NULL, expectInStorage: false, }, @@ -166,7 +166,7 @@ func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongterm name: "no action with long storage duration", firstCtx: suite.ctx, action: NULL, - secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + bep3.DefaultLongtermStorageDuration), + secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + int64(bep3.DefaultLongtermStorageDuration)), expectInStorage: true, }, { @@ -180,7 +180,7 @@ func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongterm name: "claim with long storage duration", firstCtx: suite.ctx, action: Claim, - secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + bep3.DefaultLongtermStorageDuration), + secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + int64(bep3.DefaultLongtermStorageDuration)), expectInStorage: false, }, { @@ -194,7 +194,7 @@ func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongterm name: "refund with long storage duration", firstCtx: suite.ctx, action: Refund, - secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + bep3.DefaultLongtermStorageDuration), + secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + int64(bep3.DefaultLongtermStorageDuration)), expectInStorage: false, }, } @@ -214,12 +214,12 @@ func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongterm case Refund: for _, swapID := range suite.swapIDs { swap, _ := suite.keeper.GetAtomicSwap(tc.firstCtx, swapID) - refundCtx := suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + swap.ExpireHeight) + refundCtx := suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + int64(swap.ExpireHeight)) bep3.BeginBlocker(refundCtx, suite.keeper) err := suite.keeper.RefundAtomicSwap(refundCtx, suite.addrs[5], swapID) suite.Nil(err) // Add expire height to second ctx block height - tc.secondCtx = tc.secondCtx.WithBlockHeight(tc.secondCtx.BlockHeight() + swap.ExpireHeight) + tc.secondCtx = tc.secondCtx.WithBlockHeight(tc.secondCtx.BlockHeight() + int64(swap.ExpireHeight)) } } diff --git a/x/bep3/alias.go b/x/bep3/alias.go index 15babfab..9e17708b 100644 --- a/x/bep3/alias.go +++ b/x/bep3/alias.go @@ -13,7 +13,6 @@ const ( AttributeKeyAtomicSwapIDs = types.AttributeKeyAtomicSwapIDs AttributeKeyClaimSender = types.AttributeKeyClaimSender AttributeKeyDirection = types.AttributeKeyDirection - AttributeKeyExpectedIncome = types.AttributeKeyExpectedIncome AttributeKeyExpireHeight = types.AttributeKeyExpireHeight AttributeKeyRandomNumber = types.AttributeKeyRandomNumber AttributeKeyRandomNumberHash = types.AttributeKeyRandomNumberHash @@ -29,7 +28,6 @@ const ( CreateAtomicSwap = types.CreateAtomicSwap DefaultLongtermStorageDuration = types.DefaultLongtermStorageDuration DefaultParamspace = types.DefaultParamspace - DepositAtomicSwap = types.DepositAtomicSwap EventTypeClaimAtomicSwap = types.EventTypeClaimAtomicSwap EventTypeCreateAtomicSwap = types.EventTypeCreateAtomicSwap EventTypeRefundAtomicSwap = types.EventTypeRefundAtomicSwap @@ -61,7 +59,6 @@ var ( NewKeeper = keeper.NewKeeper NewQuerier = keeper.NewQuerier RegisterRoutes = rest.RegisterRoutes - BytesToHex = types.BytesToHex CalculateRandomHash = types.CalculateRandomHash CalculateSwapID = types.CalculateSwapID DefaultGenesisState = types.DefaultGenesisState @@ -83,7 +80,6 @@ var ( ErrSwapNotRefundable = types.ErrSwapNotRefundable GenerateSecureRandomNumber = types.GenerateSecureRandomNumber GetAtomicSwapByHeightKey = types.GetAtomicSwapByHeightKey - HexToBytes = types.HexToBytes NewAssetSupply = types.NewAssetSupply NewAtomicSwap = types.NewAtomicSwap NewGenesisState = types.NewGenesisState @@ -98,8 +94,6 @@ var ( NewSwapStatusFromString = types.NewSwapStatusFromString ParamKeyTable = types.ParamKeyTable RegisterCodec = types.RegisterCodec - Uint64FromBytes = types.Uint64FromBytes - Uint64ToBytes = types.Uint64ToBytes // variable aliases AbsoluteMaximumBlockLock = types.AbsoluteMaximumBlockLock diff --git a/x/bep3/client/cli/query.go b/x/bep3/client/cli/query.go index edcca618..751907c9 100644 --- a/x/bep3/client/cli/query.go +++ b/x/bep3/client/cli/query.go @@ -6,31 +6,43 @@ import ( "strconv" "strings" - "github.com/spf13/cobra" - + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - + "github.com/spf13/cobra" + "github.com/spf13/viper" tmtime "github.com/tendermint/tendermint/types/time" "github.com/kava-labs/kava/x/bep3/types" ) +// Query atomic swaps flags +const ( + flagInvolve = "involve" + flagExpiration = "expiration" + flagStatus = "status" + flagDirection = "direction" +) + // GetQueryCmd returns the cli query commands for this module func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { // Group bep3 queries under a subcommand bep3QueryCmd := &cobra.Command{ - Use: "bep3", - Short: "Querying commands for the bep3 module", + Use: "bep3", + Short: "Querying commands for the bep3 module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, } bep3QueryCmd.AddCommand(flags.GetCommands( QueryCalcSwapIDCmd(queryRoute, cdc), QueryCalcRandomNumberHashCmd(queryRoute, cdc), - QueryGetAtomicSwapCmd(queryRoute, cdc), QueryGetAssetSupplyCmd(queryRoute, cdc), + QueryGetAssetSuppliesCmd(queryRoute, cdc), + QueryGetAtomicSwapCmd(queryRoute, cdc), QueryGetAtomicSwapsCmd(queryRoute, cdc), QueryParamsCmd(queryRoute, cdc), )...) @@ -70,10 +82,10 @@ func QueryCalcRandomNumberHashCmd(queryRoute string, cdc *codec.Codec) *cobra.Co if err != nil { return err } - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) // Prepare random number, timestamp, and hash for output - randomNumberStr := fmt.Sprintf("Random number: %s\n", randomNumber) + randomNumberStr := fmt.Sprintf("Random number: %s\n", string(randomNumber[:])) timestampStr := fmt.Sprintf("Timestamp: %d\n", timestamp) randomNumberHashStr := fmt.Sprintf("Random number hash: %s", hex.EncodeToString(randomNumberHash)) output := []string{randomNumberStr, timestampStr, randomNumberHashStr} @@ -87,13 +99,13 @@ func QueryCalcSwapIDCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "calc-swapid [random-number-hash] [sender] [sender-other-chain]", Short: "calculate swap ID for the given random number hash, sender, and sender other chain", - Example: "bep3 calc-swapid 0677bd8a303dd981810f34d8e5cc6507f13b391899b84d3c1be6c6045a17d747 kava15qdefkmwswysgg4qxgcqpqr35k3m49pkx2jdfnw bnb1ud3q90r98l3mhd87kswv3h8cgrymzeljct8qn7", + Example: "bep3 calc-swapid 0677bd8a303dd981810f34d8e5cc6507f13b391899b84d3c1be6c6045a17d747 kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny bnb1ud3q90r98l3mhd87kswv3h8cgrymzeljct8qn7", Args: cobra.MinimumNArgs(3), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) // Parse query params - randomNumberHash, err := types.HexToBytes(args[0]) + randomNumberHash, err := hex.DecodeString(args[0]) if err != nil { return err } @@ -137,6 +149,33 @@ func QueryGetAssetSupplyCmd(queryRoute string, cdc *codec.Codec) *cobra.Command } } +// QueryGetAssetSuppliesCmd queries AssetSupplies in the store +func QueryGetAssetSuppliesCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "supplies", + Short: "get a list of all asset supplies", + Example: "bep3 supplies", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetAssetSupplies), nil) + if err != nil { + return err + } + + var assetSupplies types.AssetSupplies + cdc.MustUnmarshalJSON(res, &assetSupplies) + + if len(assetSupplies) == 0 { + return fmt.Errorf("There are currently no asset supplies") + } + + cliCtx = cliCtx.WithHeight(height) + return cliCtx.PrintOutput(assetSupplies) + }, + } +} + // QueryGetAtomicSwapCmd queries an AtomicSwap by swapID func QueryGetAtomicSwapCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ @@ -148,7 +187,7 @@ func QueryGetAtomicSwapCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) // Decode swapID's hex encoded string to []byte - swapID, err := types.HexToBytes(args[0]) + swapID, err := hex.DecodeString(args[0]) if err != nil { return err } @@ -176,29 +215,97 @@ func QueryGetAtomicSwapCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { // QueryGetAtomicSwapsCmd queries AtomicSwaps in the store func QueryGetAtomicSwapsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "swaps", - Short: "get a list of active atomic swaps", - Example: "bep3 swaps", + cmd := &cobra.Command{ + Use: "swaps", + Short: "query atomic swaps with optional filters", + Long: strings.TrimSpace(`Query for all paginated atomic swaps that match optional filters: +Example: +$ kvcli q bep3 swaps --involve=kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny +$ kvcli q bep3 swaps --expiration=280 +$ kvcli q bep3 swaps --status=(Open|Completed|Expired) +$ kvcli q bep3 swaps --direction=(Incoming|Outgoing) +$ kvcli q bep3 swaps --page=2 --limit=100 +`, + ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + bechInvolveAddr := viper.GetString(flagInvolve) + strExpiration := viper.GetString(flagExpiration) + strSwapStatus := viper.GetString(flagStatus) + strSwapDirection := viper.GetString(flagDirection) + page := viper.GetInt(flags.FlagPage) + limit := viper.GetInt(flags.FlagLimit) - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetAtomicSwaps), nil) + var involveAddr sdk.AccAddress + var expiration uint64 + var swapStatus types.SwapStatus + var swapDirection types.SwapDirection + + params := types.NewQueryAtomicSwaps(page, limit, involveAddr, expiration, swapStatus, swapDirection) + + if len(bechInvolveAddr) != 0 { + involveAddr, err := sdk.AccAddressFromBech32(bechInvolveAddr) + if err != nil { + return err + } + params.Involve = involveAddr + } + + if len(strExpiration) != 0 { + expiration, err := strconv.ParseUint(strExpiration, 10, 64) + if err != nil { + return err + } + params.Expiration = expiration + } + + if len(strSwapStatus) != 0 { + swapStatus := types.NewSwapStatusFromString(strSwapStatus) + if !swapStatus.IsValid() { + return fmt.Errorf("invalid swap status %s", strSwapStatus) + } + params.Status = swapStatus + } + + if len(strSwapDirection) != 0 { + swapDirection := types.NewSwapDirectionFromString(strSwapDirection) + if !swapDirection.IsValid() { + return fmt.Errorf("invalid swap direction %s", strSwapDirection) + } + params.Direction = swapDirection + } + + bz, err := cdc.MarshalJSON(params) if err != nil { return err } - var atomicSwaps types.AtomicSwaps - cdc.MustUnmarshalJSON(res, &atomicSwaps) + cliCtx := context.NewCLIContext().WithCodec(cdc) - if len(atomicSwaps) == 0 { - return fmt.Errorf("There are currently no atomic swaps") + res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetAtomicSwaps), bz) + if err != nil { + return err + } + + var matchingAtomicSwaps types.AtomicSwaps + cdc.UnmarshalJSON(res, &matchingAtomicSwaps) + + if len(matchingAtomicSwaps) == 0 { + return fmt.Errorf("No matching atomic swaps found") } cliCtx = cliCtx.WithHeight(height) - return cliCtx.PrintOutput(atomicSwaps.String()) + return cliCtx.PrintOutput(matchingAtomicSwaps.String()) // nolint:errcheck }, } + + cmd.Flags().Int(flags.FlagPage, 1, "pagination page of atomic swaps to to query for") + cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of atomic swaps to query for") + cmd.Flags().String(flagInvolve, "", "(optional) filter by atomic swaps that involve an address") + cmd.Flags().String(flagExpiration, "", "(optional) filter by atomic swaps that expire before a block height") + cmd.Flags().String(flagStatus, "", "(optional) filter by atomic swap status, status: open/completed/expired") + cmd.Flags().String(flagDirection, "", "(optional) filter by atomic swap direction, direction: incoming/outgoing") + + return cmd } // QueryParamsCmd queries the bep3 module parameters diff --git a/x/bep3/client/cli/tx.go b/x/bep3/client/cli/tx.go index 47c45410..7796de5c 100644 --- a/x/bep3/client/cli/tx.go +++ b/x/bep3/client/cli/tx.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" @@ -25,8 +26,11 @@ import ( // GetTxCmd returns the transaction commands for this module func GetTxCmd(cdc *codec.Codec) *cobra.Command { bep3TxCmd := &cobra.Command{ - Use: "bep3", - Short: "bep3 transactions subcommands", + Use: "bep3", + Short: "bep3 transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, } bep3TxCmd.AddCommand(flags.PostCommands( @@ -41,23 +45,23 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command { // GetCmdCreateAtomicSwap cli command for creating atomic swaps func GetCmdCreateAtomicSwap(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "create [to] [recipient-other-chain] [sender-other-chain] [timestamp] [coins] [expected-income] [height-span] [cross-chain]", + Use: "create [to] [recipient-other-chain] [sender-other-chain] [timestamp] [coins] [height-span]", Short: "create a new atomic swap", - Example: fmt.Sprintf("%s tx %s create kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj bnb1urfermcg92dwq36572cx4xg84wpk3lfpksr5g7 bnb1uky3me9ggqypmrsvxk7ur6hqkzq7zmv4ed4ng7 now 100bnb 100bnb 360 true --from validator", + Example: fmt.Sprintf("%s tx %s create kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj bnb1urfermcg92dwq36572cx4xg84wpk3lfpksr5g7 bnb1uky3me9ggqypmrsvxk7ur6hqkzq7zmv4ed4ng7 now 100bnb 360 --from validator", version.ClientName, types.ModuleName), - Args: cobra.ExactArgs(8), + Args: cobra.ExactArgs(6), RunE: func(cmd *cobra.Command, args []string) error { inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - from := cliCtx.GetFromAddress() // same as KavaExecutor.DeputyAddress (for cross-chain) + from := cliCtx.GetFromAddress() // same as Kava executor's deputy address to, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - recipientOtherChain := args[1] // same as OtherExecutor.DeputyAddress + recipientOtherChain := args[1] // same as the other executor's deputy address senderOtherChain := args[2] // Timestamp defaults to time.Now() unless it's explicitly set @@ -77,10 +81,10 @@ func GetCmdCreateAtomicSwap(cdc *codec.Codec) *cobra.Command { return err } - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) // Print random number, timestamp, and hash to user's console - fmt.Printf("\nRandom number: %s\n", hex.EncodeToString(randomNumber.Bytes())) + fmt.Printf("\nRandom number: %s\n", string(randomNumber[:])) fmt.Printf("Timestamp: %d\n", timestamp) fmt.Printf("Random number hash: %s\n\n", hex.EncodeToString(randomNumberHash)) @@ -89,21 +93,14 @@ func GetCmdCreateAtomicSwap(cdc *codec.Codec) *cobra.Command { return err } - expectedIncome := args[5] - - heightSpan, err := strconv.ParseInt(args[6], 10, 64) - if err != nil { - return err - } - - crossChain, err := strconv.ParseBool(args[7]) + heightSpan, err := strconv.ParseUint(args[5], 10, 64) if err != nil { return err } msg := types.NewMsgCreateAtomicSwap( - from, to, recipientOtherChain, senderOtherChain, randomNumberHash, - timestamp, coins, expectedIncome, heightSpan, crossChain, + from, to, recipientOtherChain, senderOtherChain, + randomNumberHash, timestamp, coins, heightSpan, ) err = msg.ValidateBasic() @@ -130,7 +127,7 @@ func GetCmdClaimAtomicSwap(cdc *codec.Codec) *cobra.Command { from := cliCtx.GetFromAddress() - swapID, err := types.HexToBytes(args[0]) + swapID, err := hex.DecodeString(args[0]) if err != nil { return err } @@ -138,11 +135,7 @@ func GetCmdClaimAtomicSwap(cdc *codec.Codec) *cobra.Command { if len(strings.TrimSpace(args[1])) == 0 { return fmt.Errorf("random-number cannot be empty") } - - randomNumber, err := types.HexToBytes(args[1]) - if err != nil { - return err - } + randomNumber := []byte(args[1]) msg := types.NewMsgClaimAtomicSwap(from, swapID, randomNumber) @@ -170,7 +163,7 @@ func GetCmdRefundAtomicSwap(cdc *codec.Codec) *cobra.Command { from := cliCtx.GetFromAddress() - swapID, err := types.HexToBytes(args[0]) + swapID, err := hex.DecodeString(args[0]) if err != nil { return err } diff --git a/x/bep3/client/rest/query.go b/x/bep3/client/rest/query.go index 7ae9bc35..c3ccc0a1 100644 --- a/x/bep3/client/rest/query.go +++ b/x/bep3/client/rest/query.go @@ -1,13 +1,15 @@ package rest import ( + "encoding/hex" "fmt" "net/http" - - "github.com/gorilla/mux" + "strconv" "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/gorilla/mux" "github.com/kava-labs/kava/x/bep3/types" ) @@ -19,6 +21,7 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc(fmt.Sprintf("/%s/swap/{%s}", types.ModuleName, restSwapID), queryAtomicSwapHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/swaps", types.ModuleName), queryAtomicSwapsHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/supply/{%s}", types.ModuleName, restDenom), queryAssetSupplyHandlerFn(cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/%s/supplies", types.ModuleName), queryAssetSuppliesHandlerFn(cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/%s/parameters", types.ModuleName), queryParamsHandlerFn(cliCtx)).Methods("GET") } @@ -38,7 +41,7 @@ func queryAtomicSwapHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - swapID, err := types.HexToBytes(vars[restSwapID]) + swapID, err := hex.DecodeString(vars[restSwapID]) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -69,38 +72,75 @@ func queryAtomicSwapHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } } +// HTTP request handler to query list of atomic swaps filtered by optional params func queryAtomicSwapsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // Parse the query height + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) if !ok { return } - route := fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryGetAtomicSwaps) + var ( + involveAddr sdk.AccAddress + expiration uint64 + swapStatus types.SwapStatus + swapDirection types.SwapDirection + ) - res, height, err := cliCtx.QueryWithData(route, nil) + if x := r.URL.Query().Get(RestInvolve); len(x) != 0 { + involveAddr, err = sdk.AccAddressFromBech32(x) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + } + + if x := r.URL.Query().Get(RestExpiration); len(x) != 0 { + expiration, err = strconv.ParseUint(x, 10, 64) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + } + + if x := r.URL.Query().Get(RestStatus); len(x) != 0 { + swapStatus = types.NewSwapStatusFromString(x) + if !swapStatus.IsValid() { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("invalid swap status %s", swapStatus)) + return + } + } + + if x := r.URL.Query().Get(RestDirection); len(x) != 0 { + swapDirection = types.NewSwapDirectionFromString(x) + if !swapDirection.IsValid() { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("invalid swap direction %s", swapDirection)) + return + } + } + + params := types.NewQueryAtomicSwaps(page, limit, involveAddr, expiration, swapStatus, swapDirection) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryGetAtomicSwaps) + res, height, err := cliCtx.QueryWithData(route, bz) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - // Decode and return results cliCtx = cliCtx.WithHeight(height) - - var swaps types.AtomicSwaps - err = cliCtx.Codec.UnmarshalJSON(res, &swaps) - if err != nil { - rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) - return - } - - // using empty slice so json returns [] instead of null when there's no swaps - sliceSwaps := types.AtomicSwaps{} - for _, s := range swaps { - sliceSwaps = append(sliceSwaps, s) - } - rest.PostProcessResponse(w, cliCtx, cliCtx.Codec.MustMarshalJSON(sliceSwaps)) + rest.PostProcessResponse(w, cliCtx, res) } } @@ -143,6 +183,41 @@ func queryAssetSupplyHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } } +func queryAssetSuppliesHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Parse the query height + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + route := fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryGetAssetSupplies) + + res, height, err := cliCtx.QueryWithData(route, nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + // Decode and return results + cliCtx = cliCtx.WithHeight(height) + + var supplies types.AssetSupplies + err = cliCtx.Codec.UnmarshalJSON(res, &supplies) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + // using empty slice so json returns [] instead of null when there's no swaps + sliceSupplies := types.AssetSupplies{} + for _, s := range supplies { + sliceSupplies = append(sliceSupplies, s) + } + rest.PostProcessResponse(w, cliCtx, cliCtx.Codec.MustMarshalJSON(sliceSupplies)) + } +} + func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) diff --git a/x/bep3/client/rest/rest.go b/x/bep3/client/rest/rest.go index 1b5bfb21..7cb7219a 100644 --- a/x/bep3/client/rest/rest.go +++ b/x/bep3/client/rest/rest.go @@ -10,6 +10,15 @@ import ( "github.com/cosmos/cosmos-sdk/types/rest" ) +// REST Variable names +// nolint +const ( + RestExpiration = "expiration" + RestInvolve = "involve" + RestStatus = "status" + RestDirection = "direction" +) + // RegisterRoutes registers bep3-related REST handlers to a router func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { registerQueryRoutes(cliCtx, r) @@ -26,8 +35,7 @@ type PostCreateSwapReq struct { RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` Timestamp int64 `json:"timestamp" yaml:"timestamp"` Amount sdk.Coins `json:"amount" yaml:"amount"` - ExpectedIncome string `json:"expected_income" yaml:"expected_income"` - HeightSpan int64 `json:"height_span" yaml:"height_span"` + HeightSpan uint64 `json:"height_span" yaml:"height_span"` CrossChain bool `json:"cross_chain" yaml:"cross_chain"` } diff --git a/x/bep3/client/rest/tx.go b/x/bep3/client/rest/tx.go index 2f5a08ae..8d63db2c 100644 --- a/x/bep3/client/rest/tx.go +++ b/x/bep3/client/rest/tx.go @@ -41,9 +41,7 @@ func postCreateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { req.RandomNumberHash, req.Timestamp, req.Amount, - req.ExpectedIncome, req.HeightSpan, - req.CrossChain, ) if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) diff --git a/x/bep3/genesis.go b/x/bep3/genesis.go index cd7836b7..a8737e72 100644 --- a/x/bep3/genesis.go +++ b/x/bep3/genesis.go @@ -37,8 +37,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper if !found { panic(fmt.Sprintf("invalid asset supply: %s is not a supported asset", coin.Denom)) } - if !coin.Limit.Equal(supply.Limit.Amount) { - panic(fmt.Sprintf("supported asset limit %s does not equal asset supply %s", coin.Limit, supply.Limit.Amount)) + if !coin.Limit.Equal(supply.SupplyLimit.Amount) { + panic(fmt.Sprintf("supported asset limit %s does not equal asset supply %s", coin.Limit, supply.SupplyLimit.Amount)) } // Increment current, incoming, and outgoing asset supplies diff --git a/x/bep3/genesis_test.go b/x/bep3/genesis_test.go index a4499f67..7e2302b7 100644 --- a/x/bep3/genesis_test.go +++ b/x/bep3/genesis_test.go @@ -89,7 +89,7 @@ func (suite *GenesisTestSuite) TestGenesisState() { IncomingSupply: c("bnb", 0), OutgoingSupply: c("bnb", 0), CurrentSupply: c("bnb", assetParam.Limit.Add(i(1)).Int64()), - Limit: c("bnb", assetParam.Limit.Int64()), + SupplyLimit: c("bnb", assetParam.Limit.Int64()), }, } return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)} @@ -108,9 +108,9 @@ func (suite *GenesisTestSuite) TestGenesisState() { _, addrs := app.GeneratePrivKeyAddressPairs(2) timestamp := ts(0) randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) swap := bep3.NewAtomicSwap(cs(c("bnb", overLimitAmount.Int64())), randomNumberHash, - int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, + uint64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, TestRecipientOtherChain, 0, bep3.Open, true, bep3.Incoming) gs.AtomicSwaps = bep3.AtomicSwaps{swap} @@ -121,7 +121,7 @@ func (suite *GenesisTestSuite) TestGenesisState() { IncomingSupply: c("bnb", assetParam.Limit.Add(i(1)).Int64()), OutgoingSupply: c("bnb", 0), CurrentSupply: c("bnb", 0), - Limit: c("bnb", assetParam.Limit.Int64()), + SupplyLimit: c("bnb", assetParam.Limit.Int64()), }, } return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)} @@ -141,9 +141,9 @@ func (suite *GenesisTestSuite) TestGenesisState() { _, addrs := app.GeneratePrivKeyAddressPairs(2) timestamp := ts(0) randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) swap := bep3.NewAtomicSwap(cs(c("bnb", halfLimit)), randomNumberHash, - int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, + uint64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, TestRecipientOtherChain, 0, bep3.Open, true, bep3.Incoming) gs.AtomicSwaps = bep3.AtomicSwaps{swap} @@ -154,7 +154,7 @@ func (suite *GenesisTestSuite) TestGenesisState() { IncomingSupply: c("bnb", halfLimit), OutgoingSupply: c("bnb", 0), CurrentSupply: c("bnb", overHalfLimit), - Limit: c("bnb", assetParam.Limit.Int64()), + SupplyLimit: c("bnb", assetParam.Limit.Int64()), }, } return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)} @@ -171,7 +171,7 @@ func (suite *GenesisTestSuite) TestGenesisState() { IncomingSupply: c("fake", 0), OutgoingSupply: c("fake", 0), CurrentSupply: c("fake", 0), - Limit: c("fake", StandardSupplyLimit.Int64()), + SupplyLimit: c("fake", StandardSupplyLimit.Int64()), }, } return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)} @@ -185,9 +185,9 @@ func (suite *GenesisTestSuite) TestGenesisState() { _, addrs := app.GeneratePrivKeyAddressPairs(2) timestamp := ts(0) randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) swap := bep3.NewAtomicSwap(cs(c("fake", 500000)), randomNumberHash, - int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, + uint64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, TestRecipientOtherChain, 0, bep3.Open, true, bep3.Incoming) gs.AtomicSwaps = bep3.AtomicSwaps{swap} @@ -202,9 +202,9 @@ func (suite *GenesisTestSuite) TestGenesisState() { _, addrs := app.GeneratePrivKeyAddressPairs(2) timestamp := ts(0) randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) swap := bep3.NewAtomicSwap(cs(c("bnb", 5000)), randomNumberHash, - int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, + uint64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain, TestRecipientOtherChain, 0, bep3.NULL, true, bep3.Incoming) gs.AtomicSwaps = bep3.AtomicSwaps{swap} diff --git a/x/bep3/handler.go b/x/bep3/handler.go index fa92f055..074c7240 100644 --- a/x/bep3/handler.go +++ b/x/bep3/handler.go @@ -25,7 +25,7 @@ func NewHandler(k Keeper) sdk.Handler { // handleMsgCreateAtomicSwap handles requests to create a new AtomicSwap func handleMsgCreateAtomicSwap(ctx sdk.Context, k Keeper, msg MsgCreateAtomicSwap) (*sdk.Result, error) { err := k.CreateAtomicSwap(ctx, msg.RandomNumberHash, msg.Timestamp, msg.HeightSpan, msg.From, msg.To, - msg.SenderOtherChain, msg.RecipientOtherChain, msg.Amount, msg.ExpectedIncome, msg.CrossChain) + msg.SenderOtherChain, msg.RecipientOtherChain, msg.Amount, true) if err != nil { return nil, err diff --git a/x/bep3/handler_test.go b/x/bep3/handler_test.go index 888d819d..e501cf28 100644 --- a/x/bep3/handler_test.go +++ b/x/bep3/handler_test.go @@ -47,31 +47,32 @@ func (suite *HandlerTestSuite) SetupTest() { } func (suite *HandlerTestSuite) AddAtomicSwap() (tmbytes.HexBytes, tmbytes.HexBytes) { - expireHeight := int64(360) + expireHeight := uint64(360) amount := cs(c("bnb", int64(50000))) timestamp := ts(0) randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) // Create atomic swap and check err to confirm creation err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight, suite.addrs[0], suite.addrs[1], TestSenderOtherChain, TestRecipientOtherChain, - amount, amount.String(), true) + amount, true) suite.Nil(err) swapID := bep3.CalculateSwapID(randomNumberHash, suite.addrs[0], TestSenderOtherChain) - return swapID, randomNumber.Bytes() + return swapID, randomNumber[:] } func (suite *HandlerTestSuite) TestMsgCreateAtomicSwap() { amount := cs(c("bnb", int64(10000))) timestamp := ts(0) randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) msg := bep3.NewMsgCreateAtomicSwap( - suite.addrs[0], suite.addrs[2], TestRecipientOtherChain, TestSenderOtherChain, - randomNumberHash, timestamp, amount, amount.String(), int64(300), true) + suite.addrs[0], suite.addrs[2], TestRecipientOtherChain, + TestSenderOtherChain, randomNumberHash, timestamp, amount, + uint64(300)) res, err := suite.handler(suite.ctx, msg) suite.Require().NoError(err) @@ -81,9 +82,9 @@ func (suite *HandlerTestSuite) TestMsgCreateAtomicSwap() { func (suite *HandlerTestSuite) TestMsgClaimAtomicSwap() { // Attempt claim msg on fake atomic swap badRandomNumber, _ := bep3.GenerateSecureRandomNumber() - badRandomNumberHash := bep3.CalculateRandomHash(badRandomNumber.Bytes(), ts(0)) + badRandomNumberHash := bep3.CalculateRandomHash(badRandomNumber[:], ts(0)) badSwapID := bep3.CalculateSwapID(badRandomNumberHash, suite.addrs[0], TestSenderOtherChain) - badMsg := bep3.NewMsgClaimAtomicSwap(suite.addrs[0], badSwapID, badRandomNumber.Bytes()) + badMsg := bep3.NewMsgClaimAtomicSwap(suite.addrs[0], badSwapID, badRandomNumber[:]) badRes, err := suite.handler(suite.ctx, badMsg) suite.Require().Error(err) suite.Require().Nil(badRes) @@ -99,7 +100,7 @@ func (suite *HandlerTestSuite) TestMsgClaimAtomicSwap() { func (suite *HandlerTestSuite) TestMsgRefundAtomicSwap() { // Attempt refund msg on fake atomic swap badRandomNumber, _ := bep3.GenerateSecureRandomNumber() - badRandomNumberHash := bep3.CalculateRandomHash(badRandomNumber.Bytes(), ts(0)) + badRandomNumberHash := bep3.CalculateRandomHash(badRandomNumber[:], ts(0)) badSwapID := bep3.CalculateSwapID(badRandomNumberHash, suite.addrs[0], TestSenderOtherChain) badMsg := bep3.NewMsgRefundAtomicSwap(suite.addrs[0], badSwapID) badRes, err := suite.handler(suite.ctx, badMsg) diff --git a/x/bep3/integration_test.go b/x/bep3/integration_test.go index 390b13a4..f3dd42c5 100644 --- a/x/bep3/integration_test.go +++ b/x/bep3/integration_test.go @@ -73,10 +73,10 @@ func baseGenState(deputy sdk.AccAddress) bep3.GenesisState { func loadSwapAndSupply(addr sdk.AccAddress, index int) (bep3.AtomicSwap, bep3.AssetSupply) { coin := c(DenomMap[index], 50000) - expireOffset := int64((index * 15) + 360) // Default expire height + offet to match timestamp - timestamp := ts(index) // One minute apart + expireOffset := uint64((index * 15) + 360) // Default expire height + offet to match timestamp + timestamp := ts(index) // One minute apart randomNumber, _ := bep3.GenerateSecureRandomNumber() - randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp) swap := bep3.NewAtomicSwap(cs(coin), randomNumberHash, expireOffset, timestamp, addr, addr, TestSenderOtherChain, TestRecipientOtherChain, 1, bep3.Open, true, bep3.Incoming) diff --git a/x/bep3/keeper/asset.go b/x/bep3/keeper/asset.go index cf8f1ef7..adef5756 100644 --- a/x/bep3/keeper/asset.go +++ b/x/bep3/keeper/asset.go @@ -15,8 +15,8 @@ func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) erro } // Resulting current supply must be under asset's limit - if !supply.Limit.IsGTE(supply.CurrentSupply.Add(coin)) { - return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supply.Limit) + if !supply.SupplyLimit.IsGTE(supply.CurrentSupply.Add(coin)) { + return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supply.SupplyLimit) } supply.CurrentSupply = supply.CurrentSupply.Add(coin) @@ -51,8 +51,8 @@ func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) err // Result of (current + incoming + amount) must be under asset's limit totalSupply := supply.CurrentSupply.Add(supply.IncomingSupply) - if !supply.Limit.IsGTE(totalSupply.Add(coin)) { - return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supply.Limit) + if !supply.SupplyLimit.IsGTE(totalSupply.Add(coin)) { + return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supply.SupplyLimit) } supply.IncomingSupply = supply.IncomingSupply.Add(coin) diff --git a/x/bep3/keeper/asset_test.go b/x/bep3/keeper/asset_test.go index 67447c55..84206575 100644 --- a/x/bep3/keeper/asset_test.go +++ b/x/bep3/keeper/asset_test.go @@ -43,7 +43,7 @@ func (suite *AssetTestSuite) SetupTest() { IncomingSupply: c("bnb", 5), OutgoingSupply: c("bnb", 5), CurrentSupply: c("bnb", 40), - Limit: c("bnb", 50), + SupplyLimit: c("bnb", 50), } keeper.SetAssetSupply(ctx, supply, []byte(supply.Denom)) diff --git a/x/bep3/keeper/integration_test.go b/x/bep3/keeper/integration_test.go index 88c38d3f..1d910f0c 100644 --- a/x/bep3/keeper/integration_test.go +++ b/x/bep3/keeper/integration_test.go @@ -66,13 +66,13 @@ func atomicSwaps(ctx sdk.Context, count int) types.AtomicSwaps { } func atomicSwap(ctx sdk.Context, index int) types.AtomicSwap { - expireOffset := int64((index * 15) + 360) // Default expire height + offet to match timestamp - timestamp := ts(index) // One minute apart + expireOffset := uint64((index * 15) + 360) // Default expire height + offet to match timestamp + timestamp := ts(index) // One minute apart randomNumber, _ := types.GenerateSecureRandomNumber() - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) return types.NewAtomicSwap(cs(c("bnb", 50000)), randomNumberHash, - ctx.BlockHeight()+expireOffset, timestamp, TestUser1, TestUser2, + uint64(ctx.BlockHeight())+expireOffset, timestamp, TestUser1, TestUser2, TestSenderOtherChain, TestRecipientOtherChain, 0, types.Open, true, types.Incoming) } diff --git a/x/bep3/keeper/keeper.go b/x/bep3/keeper/keeper.go index 4657138b..c748c118 100644 --- a/x/bep3/keeper/keeper.go +++ b/x/bep3/keeper/keeper.go @@ -124,7 +124,7 @@ func (k Keeper) IterateAtomicSwapsByBlock(ctx sdk.Context, inclusiveCutoffTime u store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapByBlockPrefix) iterator := store.Iterator( nil, // start at the very start of the prefix store - sdk.PrefixEndBytes(types.Uint64ToBytes(inclusiveCutoffTime)), // end of range + sdk.PrefixEndBytes(sdk.Uint64ToBigEndian(inclusiveCutoffTime)), // end of range ) defer iterator.Close() @@ -146,15 +146,15 @@ func (k Keeper) IterateAtomicSwapsByBlock(ctx sdk.Context, inclusiveCutoffTime u // Completed swaps are stored for 1 week. func (k Keeper) InsertIntoLongtermStorage(ctx sdk.Context, atomicSwap types.AtomicSwap) { store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapLongtermStoragePrefix) - store.Set(types.GetAtomicSwapByHeightKey(atomicSwap.ClosedBlock+types.DefaultLongtermStorageDuration, - atomicSwap.GetSwapID()), atomicSwap.GetSwapID()) + deletionHeight := uint64(atomicSwap.ClosedBlock) + types.DefaultLongtermStorageDuration + store.Set(types.GetAtomicSwapByHeightKey(deletionHeight, atomicSwap.GetSwapID()), atomicSwap.GetSwapID()) } // RemoveFromLongtermStorage removes a swap from the into the longterm storage index func (k Keeper) RemoveFromLongtermStorage(ctx sdk.Context, atomicSwap types.AtomicSwap) { store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapLongtermStoragePrefix) - store.Delete(types.GetAtomicSwapByHeightKey(atomicSwap.ClosedBlock+types.DefaultLongtermStorageDuration, - atomicSwap.GetSwapID())) + deletionHeight := uint64(atomicSwap.ClosedBlock) + types.DefaultLongtermStorageDuration + store.Delete(types.GetAtomicSwapByHeightKey(deletionHeight, atomicSwap.GetSwapID())) } // IterateAtomicSwapsLongtermStorage provides an iterator over AtomicSwaps ordered by deletion height. @@ -164,7 +164,7 @@ func (k Keeper) IterateAtomicSwapsLongtermStorage(ctx sdk.Context, inclusiveCuto store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapLongtermStoragePrefix) iterator := store.Iterator( nil, // start at the very start of the prefix store - sdk.PrefixEndBytes(types.Uint64ToBytes(inclusiveCutoffTime)), // end of range + sdk.PrefixEndBytes(sdk.Uint64ToBigEndian(inclusiveCutoffTime)), // end of range ) defer iterator.Close() diff --git a/x/bep3/keeper/keeper_test.go b/x/bep3/keeper/keeper_test.go index 363a0ea3..37fe230e 100644 --- a/x/bep3/keeper/keeper_test.go +++ b/x/bep3/keeper/keeper_test.go @@ -122,7 +122,7 @@ func (suite *KeeperTestSuite) TestInsertIntoByBlockIndex() { // Block index lacks getter methods, must use iteration to get count of swaps in store var swapIDs [][]byte - suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, uint64(atomicSwap.ExpireHeight+1), func(id []byte) bool { + suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, atomicSwap.ExpireHeight+1, func(id []byte) bool { swapIDs = append(swapIDs, id) return false }) @@ -145,7 +145,7 @@ func (suite *KeeperTestSuite) TestRemoveFromByBlockIndex() { // Check stored data in block index var swapIDsPre [][]byte - suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, uint64(atomicSwap.ExpireHeight+1), func(id []byte) bool { + suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, atomicSwap.ExpireHeight+1, func(id []byte) bool { swapIDsPre = append(swapIDsPre, id) return false }) @@ -155,7 +155,7 @@ func (suite *KeeperTestSuite) TestRemoveFromByBlockIndex() { // Check stored data not in block index var swapIDsPost [][]byte - suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, uint64(atomicSwap.ExpireHeight+1), func(id []byte) bool { + suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, atomicSwap.ExpireHeight+1, func(id []byte) bool { swapIDsPost = append(swapIDsPost, id) return false }) @@ -178,10 +178,10 @@ func (suite *KeeperTestSuite) TestIterateAtomicSwapsByBlock() { // Initialize a new atomic swap (different randomNumberHash = different swap IDs) timestamp := tmtime.Now().Add(time.Duration(i) * time.Minute).Unix() randomNumber, _ := types.GenerateSecureRandomNumber() - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) atomicSwap := types.NewAtomicSwap(cs(c("bnb", 50000)), randomNumberHash, - blockCtx.BlockHeight(), timestamp, TestUser1, TestUser2, + uint64(blockCtx.BlockHeight()), timestamp, TestUser1, TestUser2, TestSenderOtherChain, TestRecipientOtherChain, 0, types.Open, true, types.Incoming) @@ -269,10 +269,10 @@ func (suite *KeeperTestSuite) TestIterateAtomicSwapsLongtermStorage() { for i := 0; i < 8; i++ { timestamp := tmtime.Now().Unix() randomNumber, _ := types.GenerateSecureRandomNumber() - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) atomicSwap := types.NewAtomicSwap(cs(c("bnb", 50000)), randomNumberHash, - suite.ctx.BlockHeight(), timestamp, TestUser1, TestUser2, + uint64(suite.ctx.BlockHeight()), timestamp, TestUser1, TestUser2, TestSenderOtherChain, TestRecipientOtherChain, 100, types.Open, true, types.Incoming) diff --git a/x/bep3/keeper/params.go b/x/bep3/keeper/params.go index f3dc1abf..825be35f 100644 --- a/x/bep3/keeper/params.go +++ b/x/bep3/keeper/params.go @@ -25,13 +25,13 @@ func (k Keeper) GetBnbDeputyAddress(ctx sdk.Context) sdk.AccAddress { } // GetMaxBlockLock returns the maximum block lock -func (k Keeper) GetMaxBlockLock(ctx sdk.Context) int64 { +func (k Keeper) GetMaxBlockLock(ctx sdk.Context) uint64 { params := k.GetParams(ctx) return params.MaxBlockLock } // GetMinBlockLock returns the minimum block lock -func (k Keeper) GetMinBlockLock(ctx sdk.Context) int64 { +func (k Keeper) GetMinBlockLock(ctx sdk.Context) uint64 { params := k.GetParams(ctx) return params.MinBlockLock } diff --git a/x/bep3/keeper/querier.go b/x/bep3/keeper/querier.go index 8cc573b6..3483a086 100644 --- a/x/bep3/keeper/querier.go +++ b/x/bep3/keeper/querier.go @@ -3,6 +3,7 @@ package keeper import ( abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -16,6 +17,8 @@ func NewQuerier(keeper Keeper) sdk.Querier { switch path[0] { case types.QueryGetAssetSupply: return queryAssetSupply(ctx, req, keeper) + case types.QueryGetAssetSupplies: + return queryAssetSupplies(ctx, req, keeper) case types.QueryGetAtomicSwap: return queryAtomicSwap(ctx, req, keeper) case types.QueryGetAtomicSwaps: @@ -50,6 +53,20 @@ func queryAssetSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([] return bz, nil } +func queryAssetSupplies(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err error) { + assets := keeper.GetAllAssetSupplies(ctx) + if assets == nil { + assets = types.AssetSupplies{} + } + + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, assets) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + func queryAtomicSwap(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Decode request var requestParams types.QueryAtomicSwapByID @@ -73,16 +90,21 @@ func queryAtomicSwap(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]b return bz, nil } -func queryAtomicSwaps(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err error) { - var swaps types.AtomicSwaps +func queryAtomicSwaps(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { + var params types.QueryAtomicSwaps + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } - keeper.IterateAtomicSwaps(ctx, func(s types.AtomicSwap) bool { - swaps = append(swaps, s) - return false - }) + unfilteredSwaps := keeper.GetAllAtomicSwaps(ctx) + swaps := filterAtomicSwaps(ctx, unfilteredSwaps, params) + if swaps == nil { + swaps = types.AtomicSwaps{} + } - bz, err2 := codec.MarshalJSONIndent(types.ModuleCdc, swaps) - if err2 != nil { + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, swaps) + if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -101,3 +123,46 @@ func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]by } return bz, nil } + +// getAtomicSwapsFiltered retrieves atomic swaps filtered by a given set of params. +// If no filters are provided, all atomic swaps will be returned in paginated form. +func filterAtomicSwaps(ctx sdk.Context, swaps types.AtomicSwaps, params types.QueryAtomicSwaps) types.AtomicSwaps { + filteredSwaps := make(types.AtomicSwaps, 0, len(swaps)) + + for _, s := range swaps { + matchInvolve, matchExpiration, matchStatus, matchDirection := true, true, true, true + + // match involved address (if supplied) + if len(params.Involve) > 0 { + matchInvolve = s.Sender.Equals(params.Involve) || s.Recipient.Equals(params.Involve) + } + + // match expiration block limit (if supplied) + if params.Expiration > 0 { + matchExpiration = s.ExpireHeight <= params.Expiration + } + + // match status (if supplied/valid) + if params.Status.IsValid() { + matchStatus = s.Status == params.Status + } + + // match direction (if supplied/valid) + if params.Direction.IsValid() { + matchDirection = s.Direction == params.Direction + } + + if matchInvolve && matchExpiration && matchStatus && matchDirection { + filteredSwaps = append(filteredSwaps, s) + } + } + + start, end := client.Paginate(len(filteredSwaps), params.Page, params.Limit, 100) + if start < 0 || end < 0 { + filteredSwaps = types.AtomicSwaps{} + } else { + filteredSwaps = filteredSwaps[start:end] + } + + return filteredSwaps +} diff --git a/x/bep3/keeper/querier_test.go b/x/bep3/keeper/querier_test.go index ddf705e5..f963f7eb 100644 --- a/x/bep3/keeper/querier_test.go +++ b/x/bep3/keeper/querier_test.go @@ -62,16 +62,15 @@ func (suite *QuerierTestSuite) SetupTest() { isSwapID := make(map[string]bool) for i := 0; i < 10; i++ { // Set up atomic swap variables - expireHeight := int64(360) + expireHeight := uint64(360) amount := cs(c("bnb", 100)) timestamp := ts(0) randomNumber, _ := types.GenerateSecureRandomNumber() - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) // Create atomic swap and check err err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight, - addrs[0], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain, amount, - amount.String(), true) + addrs[0], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain, amount, true) suite.Nil(err) // Calculate swap ID and save @@ -129,12 +128,33 @@ func (suite *QuerierTestSuite) TestQueryAtomicSwap() { suite.True(suite.isSwapID[hex.EncodeToString(swap.GetSwapID())]) } +func (suite *QuerierTestSuite) TestQueryAssetSupplies() { + ctx := suite.ctx.WithIsCheckTx(false) + // Set up request query + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAssetSupplies}, "/"), + Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAssetSupplies(1, 100)), + } + + bz, err := suite.querier(ctx, []string{types.QueryGetAssetSupplies}, query) + suite.Nil(err) + suite.NotNil(bz) + + var supplies types.AssetSupplies + suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &supplies)) + + // Check that returned value matches asset supplies in state + storeSupplies := suite.keeper.GetAllAssetSupplies(ctx) + suite.Equal(len(storeSupplies), len(supplies)) + suite.Equal(supplies, storeSupplies) +} + func (suite *QuerierTestSuite) TestQueryAtomicSwaps() { ctx := suite.ctx.WithIsCheckTx(false) // Set up request query query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAtomicSwaps}, "/"), - Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAtomicSwaps(1, 100)), + Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAtomicSwaps(1, 100, sdk.AccAddress{}, 0, types.Open, types.Incoming)), } bz, err := suite.querier(ctx, []string{types.QueryGetAtomicSwaps}, query) diff --git a/x/bep3/keeper/swap.go b/x/bep3/keeper/swap.go index ac58affd..b04f5430 100644 --- a/x/bep3/keeper/swap.go +++ b/x/bep3/keeper/swap.go @@ -12,10 +12,10 @@ import ( "github.com/kava-labs/kava/x/bep3/types" ) -// CreateAtomicSwap creates a new AtomicSwap -func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, timestamp int64, heightSpan int64, +// CreateAtomicSwap creates a new atomic swap. +func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, timestamp int64, heightSpan uint64, sender sdk.AccAddress, recipient sdk.AccAddress, senderOtherChain, recipientOtherChain string, - amount sdk.Coins, expectedIncome string, crossChain bool) error { + amount sdk.Coins, crossChain bool) error { // Confirm that this is not a duplicate swap swapID := types.CalculateSwapID(randomNumberHash, sender, senderOtherChain) _, found := k.GetAtomicSwap(ctx, swapID) @@ -80,10 +80,11 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times } // Store the details of the swap - atomicSwap := types.NewAtomicSwap(amount, randomNumberHash, ctx.BlockHeight()+heightSpan, - timestamp, sender, recipient, senderOtherChain, recipientOtherChain, 0, types.Open, - crossChain, direction) + expireHeight := uint64(ctx.BlockHeight()) + heightSpan + atomicSwap := types.NewAtomicSwap(amount, randomNumberHash, expireHeight, timestamp, sender, + recipient, senderOtherChain, recipientOtherChain, 0, types.Open, crossChain, direction) + // Insert the atomic swap under both keys k.SetAtomicSwap(ctx, atomicSwap) k.InsertIntoByBlockIndex(ctx, atomicSwap) @@ -98,8 +99,7 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times sdk.NewAttribute(types.AttributeKeyTimestamp, fmt.Sprintf("%d", atomicSwap.Timestamp)), sdk.NewAttribute(types.AttributeKeySenderOtherChain, atomicSwap.SenderOtherChain), sdk.NewAttribute(types.AttributeKeyExpireHeight, fmt.Sprintf("%d", atomicSwap.ExpireHeight)), - sdk.NewAttribute(types.AttributeKeyAmount, atomicSwap.Amount[0].String()), - sdk.NewAttribute(types.AttributeKeyExpectedIncome, expectedIncome), + sdk.NewAttribute(types.AttributeKeyAmount, atomicSwap.Amount.String()), sdk.NewAttribute(types.AttributeKeyDirection, atomicSwap.Direction.String()), ), ) @@ -107,7 +107,7 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times return nil } -// ClaimAtomicSwap validates a claim attempt, and if successful, sends the escrowed amount and closes the AtomicSwap +// ClaimAtomicSwap validates a claim attempt, and if successful, sends the escrowed amount and closes the AtomicSwap. func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []byte, randomNumber []byte) error { atomicSwap, found := k.GetAtomicSwap(ctx, swapID) if !found { @@ -116,7 +116,7 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b // Only open atomic swaps can be claimed if atomicSwap.Status != types.Open { - return types.ErrSwapNotClaimable + return sdkerrors.Wrapf(types.ErrSwapNotClaimable, "status %s", atomicSwap.Status.String()) } // Calculate hashed secret using submitted number @@ -125,13 +125,13 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b // Confirm that secret unlocks the atomic swap if !bytes.Equal(hashedSecret, atomicSwap.GetSwapID()) { - return sdkerrors.Wrapf(types.ErrInvalidClaimSecret, "%s ≠ %s", hex.EncodeToString(hashedSecret), hex.EncodeToString(atomicSwap.GetSwapID())) + return sdkerrors.Wrapf(types.ErrInvalidClaimSecret, "the submitted random number is incorrect") } var err error switch atomicSwap.Direction { case types.Incoming: - err := k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0]) + err = k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0]) if err != nil { return err } @@ -175,14 +175,14 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b sdk.NewAttribute(types.AttributeKeyRecipient, atomicSwap.Recipient.String()), sdk.NewAttribute(types.AttributeKeyAtomicSwapID, hex.EncodeToString(atomicSwap.GetSwapID())), sdk.NewAttribute(types.AttributeKeyRandomNumberHash, hex.EncodeToString(atomicSwap.RandomNumberHash)), - sdk.NewAttribute(types.AttributeKeyRandomNumber, hex.EncodeToString(randomNumber)), + sdk.NewAttribute(types.AttributeKeyRandomNumber, string(randomNumber)), ), ) return nil } -// RefundAtomicSwap refunds an AtomicSwap, sending assets to the original sender and closing the AtomicSwap +// RefundAtomicSwap refunds an AtomicSwap, sending assets to the original sender and closing the AtomicSwap. func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []byte) error { atomicSwap, found := k.GetAtomicSwap(ctx, swapID) if !found { @@ -190,7 +190,7 @@ func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID [] } // Only expired swaps may be refunded if atomicSwap.Status != types.Expired { - return types.ErrSwapNotRefundable + return sdkerrors.Wrapf(types.ErrSwapNotRefundable, "status %s", atomicSwap.Status.String()) } var err error @@ -225,10 +225,10 @@ func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID [] ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeRefundAtomicSwap, - sdk.NewAttribute(types.AttributeKeyRefundSender, fmt.Sprintf("%s", from)), - sdk.NewAttribute(types.AttributeKeySender, fmt.Sprintf("%s", atomicSwap.Sender)), - sdk.NewAttribute(types.AttributeKeyAtomicSwapID, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.GetSwapID()))), - sdk.NewAttribute(types.AttributeKeyRandomNumberHash, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.RandomNumberHash))), + sdk.NewAttribute(types.AttributeKeyRefundSender, from.String()), + sdk.NewAttribute(types.AttributeKeySender, atomicSwap.Sender.String()), + sdk.NewAttribute(types.AttributeKeyAtomicSwapID, hex.EncodeToString(atomicSwap.GetSwapID())), + sdk.NewAttribute(types.AttributeKeyRandomNumberHash, hex.EncodeToString(atomicSwap.RandomNumberHash)), ), ) @@ -236,23 +236,23 @@ func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID [] } // UpdateExpiredAtomicSwaps finds all AtomicSwaps that are past (or at) their ending times and expires them. -func (k Keeper) UpdateExpiredAtomicSwaps(ctx sdk.Context) error { - var expiredSwaps [][]byte +func (k Keeper) UpdateExpiredAtomicSwaps(ctx sdk.Context) { + var expiredSwapIDs []string k.IterateAtomicSwapsByBlock(ctx, uint64(ctx.BlockHeight()), func(id []byte) bool { - expiredSwaps = append(expiredSwaps, id) + atomicSwap, found := k.GetAtomicSwap(ctx, id) + if !found { + // NOTE: shouldn't happen. Continue to next item. + return false + } + // Expire the uncompleted swap and update both indexes + atomicSwap.Status = types.Expired + // Note: claimed swaps have already been removed from byBlock index. + k.RemoveFromByBlockIndex(ctx, atomicSwap) + k.SetAtomicSwap(ctx, atomicSwap) + expiredSwapIDs = append(expiredSwapIDs, hex.EncodeToString(atomicSwap.GetSwapID())) return false }) - // Expire incomplete swaps (claimed swaps have already been removed from byBlock index) - var expiredSwapIDs []string - for _, id := range expiredSwaps { - atomicSwap, _ := k.GetAtomicSwap(ctx, id) - atomicSwap.Status = types.Expired - k.SetAtomicSwap(ctx, atomicSwap) - k.RemoveFromByBlockIndex(ctx, atomicSwap) - expiredSwapIDs = append(expiredSwapIDs, hex.EncodeToString(atomicSwap.GetSwapID())) - } - // Emit 'swaps_expired' event ctx.EventManager().EmitEvent( sdk.NewEvent( @@ -261,23 +261,18 @@ func (k Keeper) UpdateExpiredAtomicSwaps(ctx sdk.Context) error { sdk.NewAttribute(types.AttributeExpirationBlock, fmt.Sprintf("%d", ctx.BlockHeight())), ), ) - - return nil } -// DeleteClosedAtomicSwapsFromLongtermStorage removes swaps one week after completion -func (k Keeper) DeleteClosedAtomicSwapsFromLongtermStorage(ctx sdk.Context) error { - var swapsToDelete [][]byte +// DeleteClosedAtomicSwapsFromLongtermStorage removes swaps one week after completion. +func (k Keeper) DeleteClosedAtomicSwapsFromLongtermStorage(ctx sdk.Context) { k.IterateAtomicSwapsLongtermStorage(ctx, uint64(ctx.BlockHeight()), func(id []byte) bool { - swapsToDelete = append(swapsToDelete, id) - return false - }) - - // Delete closed atomic swaps - for _, id := range swapsToDelete { - swap, _ := k.GetAtomicSwap(ctx, id) + swap, found := k.GetAtomicSwap(ctx, id) + if !found { + // NOTE: shouldn't happen. Continue to next item. + return false + } k.RemoveAtomicSwap(ctx, swap.GetSwapID()) k.RemoveFromLongtermStorage(ctx, swap) - } - return nil + return false + }) } diff --git a/x/bep3/keeper/swap_test.go b/x/bep3/keeper/swap_test.go index 3389cf58..adb36f25 100644 --- a/x/bep3/keeper/swap_test.go +++ b/x/bep3/keeper/swap_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "fmt" "testing" "time" @@ -74,11 +73,11 @@ func (suite *AtomicSwapTestSuite) GenerateSwapDetails() { // Set up atomic swap details timestamp := ts(i) randomNumber, _ := types.GenerateSecureRandomNumber() - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) timestamps = append(timestamps, timestamp) randomNumberHashes = append(randomNumberHashes, randomNumberHash) - randomNumbers = append(randomNumbers, randomNumber.Bytes()) + randomNumbers = append(randomNumbers, randomNumber[:]) } suite.timestamps = timestamps suite.randomNumberHashes = randomNumberHashes @@ -90,13 +89,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { type args struct { randomNumberHash []byte timestamp int64 - heightSpan int64 + heightSpan uint64 sender sdk.AccAddress recipient sdk.AccAddress senderOtherChain string recipientOtherChain string coins sdk.Coins - expectedIncome string crossChain bool direction types.SwapDirection } @@ -113,13 +111,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[0], timestamp: suite.timestamps[0], - heightSpan: int64(360), + heightSpan: uint64(360), sender: suite.deputy, recipient: suite.addrs[1], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), - expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM), crossChain: true, direction: types.Incoming, }, @@ -132,13 +129,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[0], timestamp: suite.timestamps[0], - heightSpan: int64(360), + heightSpan: uint64(360), sender: suite.addrs[1], recipient: suite.addrs[2], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), - expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM), crossChain: true, direction: types.Outgoing, }, @@ -151,13 +147,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[1], timestamp: suite.timestamps[1], - heightSpan: int64(360), + heightSpan: uint64(360), sender: suite.deputy, recipient: suite.addrs[2], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c("xyz", 50000)), - expectedIncome: "50000xyz", crossChain: true, direction: types.Incoming, }, @@ -170,13 +165,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[2], timestamp: suite.timestamps[2] - 2000, - heightSpan: int64(360), + heightSpan: uint64(360), sender: suite.deputy, recipient: suite.addrs[3], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), - expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM), crossChain: true, direction: types.Incoming, }, @@ -189,13 +183,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[3], timestamp: suite.timestamps[3] + 5000, - heightSpan: int64(360), + heightSpan: uint64(360), sender: suite.deputy, recipient: suite.addrs[4], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), - expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM), crossChain: true, direction: types.Incoming, }, @@ -208,13 +201,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[4], timestamp: suite.timestamps[4], - heightSpan: int64(5), + heightSpan: uint64(5), sender: suite.deputy, recipient: suite.addrs[5], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), - expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM), crossChain: true, direction: types.Incoming, }, @@ -227,13 +219,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[5], timestamp: suite.timestamps[5], - heightSpan: int64(1000000), + heightSpan: uint64(1000000), sender: suite.deputy, recipient: suite.addrs[6], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), - expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM), crossChain: true, direction: types.Incoming, }, @@ -246,13 +237,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[6], timestamp: suite.timestamps[6], - heightSpan: int64(360), + heightSpan: uint64(360), sender: suite.deputy, recipient: suite.addrs[7], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 0)), - expectedIncome: fmt.Sprintf("0%s", BNB_DENOM), crossChain: true, direction: types.Incoming, }, @@ -265,13 +255,12 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { args{ randomNumberHash: suite.randomNumberHashes[0], timestamp: suite.timestamps[0], - heightSpan: int64(360), + heightSpan: uint64(360), sender: suite.deputy, recipient: suite.addrs[1], senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), - expectedIncome: "50000bnb", crossChain: true, direction: types.Incoming, }, @@ -305,7 +294,7 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { // Create atomic swap err := suite.keeper.CreateAtomicSwap(suite.ctx, tc.args.randomNumberHash, tc.args.timestamp, tc.args.heightSpan, tc.args.sender, tc.args.recipient, tc.args.senderOtherChain, - tc.args.recipientOtherChain, tc.args.coins, tc.args.expectedIncome, tc.args.crossChain) + tc.args.recipientOtherChain, tc.args.coins, tc.args.crossChain) // Load sender's account after swap creation senderAccPost := ak.GetAccount(suite.ctx, tc.args.sender) @@ -341,7 +330,7 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { types.AtomicSwap{ Amount: tc.args.coins, RandomNumberHash: tc.args.randomNumberHash, - ExpireHeight: suite.ctx.BlockHeight() + tc.args.heightSpan, + ExpireHeight: uint64(suite.ctx.BlockHeight()) + tc.args.heightSpan, Timestamp: tc.args.timestamp, Sender: tc.args.sender, Recipient: tc.args.recipient, @@ -419,7 +408,7 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() { suite.ctx, args{ swapID: []byte{}, - randomNumber: invalidRandomNumber.Bytes(), + randomNumber: invalidRandomNumber[:], direction: types.Incoming, }, false, @@ -462,8 +451,8 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() { // Create atomic swap err := suite.keeper.CreateAtomicSwap(suite.ctx, suite.randomNumberHashes[i], suite.timestamps[i], - int64(360), sender, expectedRecipient, TestSenderOtherChain, TestRecipientOtherChain, - expectedClaimAmount, expectedClaimAmount.String(), true) + uint64(360), sender, expectedRecipient, TestSenderOtherChain, TestRecipientOtherChain, + expectedClaimAmount, true) suite.NoError(err) realSwapID := types.CalculateSwapID(suite.randomNumberHashes[i], sender, TestSenderOtherChain) @@ -616,8 +605,8 @@ func (suite *AtomicSwapTestSuite) TestRefundAtomicSwap() { } err := suite.keeper.CreateAtomicSwap(suite.ctx, suite.randomNumberHashes[i], suite.timestamps[i], - int64(360), sender, suite.addrs[8], TestSenderOtherChain, TestRecipientOtherChain, - expectedRefundAmount, expectedRefundAmount.String(), true) + uint64(360), sender, suite.addrs[8], TestSenderOtherChain, TestRecipientOtherChain, + expectedRefundAmount, true) suite.NoError(err) realSwapID := types.CalculateSwapID(suite.randomNumberHashes[i], sender, TestSenderOtherChain) diff --git a/x/bep3/simulation/decoder_test.go b/x/bep3/simulation/decoder_test.go index a5b6ce08..0aa27ab2 100644 --- a/x/bep3/simulation/decoder_test.go +++ b/x/bep3/simulation/decoder_test.go @@ -27,7 +27,7 @@ func TestDecodeDistributionStore(t *testing.T) { oneCoin := sdk.NewCoin("coin", sdk.OneInt()) swap := types.NewAtomicSwap(sdk.Coins{oneCoin}, nil, 10, 100, nil, nil, "otherChainSender", "otherChainRec", 200, types.Completed, true, types.Outgoing) - supply := types.AssetSupply{Denom: "coin", IncomingSupply: oneCoin, OutgoingSupply: oneCoin, CurrentSupply: oneCoin, Limit: oneCoin} + supply := types.AssetSupply{Denom: "coin", IncomingSupply: oneCoin, OutgoingSupply: oneCoin, CurrentSupply: oneCoin, SupplyLimit: oneCoin} bz := tmbytes.HexBytes([]byte{1, 2}) kvPairs := kv.Pairs{ diff --git a/x/bep3/simulation/genesis.go b/x/bep3/simulation/genesis.go index f2a8e34a..1e31120f 100644 --- a/x/bep3/simulation/genesis.go +++ b/x/bep3/simulation/genesis.go @@ -37,17 +37,17 @@ func GenRandBnbDeputy(r *rand.Rand) simulation.Account { } // GenMinBlockLock randomized MinBlockLock -func GenMinBlockLock(r *rand.Rand) int64 { +func GenMinBlockLock(r *rand.Rand) uint64 { min := int(types.AbsoluteMinimumBlockLock) max := int(types.AbsoluteMaximumBlockLock) - return int64(r.Intn(max-min) + min) + return uint64(r.Intn(max-min) + min) } // GenMaxBlockLock randomized MaxBlockLock -func GenMaxBlockLock(r *rand.Rand, minBlockLock int64) int64 { +func GenMaxBlockLock(r *rand.Rand, minBlockLock uint64) uint64 { min := int(minBlockLock) max := int(types.AbsoluteMaximumBlockLock) - return int64(r.Intn(max-min) + min) + return uint64(r.Intn(max-min) + min) } // GenSupportedAssets gets randomized SupportedAssets @@ -101,7 +101,7 @@ func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState bnbDeputy := GenRandBnbDeputy(simState.Rand) // min/max block lock are hardcoded to 50/100 for expected -NumBlocks=100 - minBlockLock := int64(types.AbsoluteMinimumBlockLock) + minBlockLock := types.AbsoluteMinimumBlockLock maxBlockLock := minBlockLock * 2 var supportedAssets types.AssetParams diff --git a/x/bep3/simulation/operations.go b/x/bep3/simulation/operations.go index 3c8bf155..2d521343 100644 --- a/x/bep3/simulation/operations.go +++ b/x/bep3/simulation/operations.go @@ -133,15 +133,13 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", denom), "", false, nil), nil, nil } coins := sdk.NewCoins(sdk.NewCoin(denom, amount)) - expectedIncome := coins.String() // We're assuming that sims are run with -NumBlocks=100 - heightSpan := int64(55) - crossChain := true + heightSpan := uint64(55) msg := types.NewMsgCreateAtomicSwap( sender.Address, recipient.Address, recipientOtherChain, senderOtherChain, - randomNumberHash, timestamp, coins, expectedIncome, heightSpan, crossChain, + randomNumberHash, timestamp, coins, heightSpan, ) tx := helpers.GenTx( @@ -164,14 +162,14 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain) if r.Intn(100) < 50 { // Claim future operation - executionBlock := ctx.BlockHeight() + (msg.HeightSpan / 2) + executionBlock := uint64(ctx.BlockHeight()) + msg.HeightSpan/2 futureOp = simulation.FutureOperation{ BlockHeight: int(executionBlock), Op: operationClaimAtomicSwap(ak, k, swapID, randomNumber.BigInt().Bytes()), } } else { // Refund future operation - executionBlock := ctx.BlockHeight() + msg.HeightSpan + executionBlock := uint64(ctx.BlockHeight()) + msg.HeightSpan futureOp = simulation.FutureOperation{ BlockHeight: int(executionBlock), Op: operationRefundAtomicSwap(ak, k, swapID), diff --git a/x/bep3/spec/02_state.md b/x/bep3/spec/02_state.md index 7ce2df13..45232c9c 100644 --- a/x/bep3/spec/02_state.md +++ b/x/bep3/spec/02_state.md @@ -48,7 +48,6 @@ type AtomicSwap struct { RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"` ClosedBlock int64 `json:"closed_block" yaml:"closed_block"` Status SwapStatus `json:"status" yaml:"status"` - CrossChain bool `json:"cross_chain" yaml:"cross_chain"` Direction SwapDirection `json:"direction" yaml:"direction"` } diff --git a/x/bep3/spec/03_messages.md b/x/bep3/spec/03_messages.md index 97ef3912..0b22692d 100644 --- a/x/bep3/spec/03_messages.md +++ b/x/bep3/spec/03_messages.md @@ -14,9 +14,7 @@ type MsgCreateAtomicSwap struct { RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` Timestamp int64 `json:"timestamp" yaml:"timestamp"` Amount sdk.Coins `json:"amount" yaml:"amount"` - ExpectedIncome string `json:"expected_income" yaml:"expected_income"` HeightSpan int64 `json:"height_span" yaml:"height_span"` - CrossChain bool `json:"cross_chain" yaml:"cross_chain"` } ``` diff --git a/x/bep3/spec/04_events.md b/x/bep3/spec/04_events.md index 059a2055..df4b7d66 100644 --- a/x/bep3/spec/04_events.md +++ b/x/bep3/spec/04_events.md @@ -16,7 +16,6 @@ The `x/bep3` module emits the following events: | create_atomic_swap | sender_other_chain | {sender other chain} | | create_atomic_swap | expire_height | {swap expiration block} | | create_atomic_swap | amount | {coin amount} | -| create_atomic_swap | expected_income | {expected value received}| | create_atomic_swap | direction | {incoming or outgoing} | | message | module | bep3 | | message | sender | {sender address} | diff --git a/x/bep3/spec/06_begin_block.md b/x/bep3/spec/06_begin_block.md index 44a8f328..ee5db461 100644 --- a/x/bep3/spec/06_begin_block.md +++ b/x/bep3/spec/06_begin_block.md @@ -1,19 +1,46 @@ # Begin Block -At the start of each block, atomic swaps that have reached `ExpireHeight` are expired. The logic to expire atomic swaps is as follows: +At the start of each block, atomic swaps that meet certain criteria are expired or deleted. ```go - var expiredSwaps [][]byte +func BeginBlocker(ctx sdk.Context, k Keeper) { + k.UpdateExpiredAtomicSwaps(ctx) + k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx) +} +``` + +## Expiration + +If an atomic swap's `ExpireHeight` is greater than the current block height, it will be expired. The logic to expire atomic swaps is as follows: + +```go + var expiredSwapIDs []string k.IterateAtomicSwapsByBlock(ctx, uint64(ctx.BlockHeight()), func(id []byte) bool { - expiredSwaps = append(expiredSwaps, id) + atomicSwap, found := k.GetAtomicSwap(ctx, id) + if !found { + return false + } + // Expire the uncompleted swap and update both indexes + atomicSwap.Status = types.Expired + k.RemoveFromByBlockIndex(ctx, atomicSwap) + k.SetAtomicSwap(ctx, atomicSwap) + expiredSwapIDs = append(expiredSwapIDs, hex.EncodeToString(atomicSwap.GetSwapID())) return false }) - - // Expire incomplete swaps (claimed swaps have already been removed from byBlock index) - for _, id := range expiredSwaps { - atomicSwap, _ := k.GetAtomicSwap(ctx, id) - atomicSwap.Status = types.Expired - k.SetAtomicSwap(ctx, atomicSwap) - k.RemoveFromByBlockIndex(ctx, atomicSwap) - } ``` + +## Deletion + +Atomic swaps are deleted 86400 blocks (one week, assuming a block time of 7 seconds) after being completed. The logic to delete atomic swaps is as follows: + +```go +k.IterateAtomicSwapsLongtermStorage(ctx, uint64(ctx.BlockHeight()), func(id []byte) bool { + swap, found := k.GetAtomicSwap(ctx, id) + if !found { + return false + } + k.RemoveAtomicSwap(ctx, swap.GetSwapID()) + k.RemoveFromLongtermStorage(ctx, swap) + return false +}) +``` \ No newline at end of file diff --git a/x/bep3/types/asset.go b/x/bep3/types/asset.go index f9ab7971..4d2b9d06 100644 --- a/x/bep3/types/asset.go +++ b/x/bep3/types/asset.go @@ -13,17 +13,17 @@ type AssetSupply struct { IncomingSupply sdk.Coin `json:"incoming_supply" yaml:"incoming_supply"` OutgoingSupply sdk.Coin `json:"outgoing_supply" yaml:"outgoing_supply"` CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"` - Limit sdk.Coin `json:"limit" yaml:"limit"` + SupplyLimit sdk.Coin `json:"supply_limit" yaml:"supply_limit"` } // NewAssetSupply initializes a new AssetSupply -func NewAssetSupply(denom string, incomingSupply, outgoingSupply, currentSupply, limit sdk.Coin) AssetSupply { +func NewAssetSupply(denom string, incomingSupply, outgoingSupply, currentSupply, supplyLimit sdk.Coin) AssetSupply { return AssetSupply{ Denom: denom, IncomingSupply: incomingSupply, OutgoingSupply: outgoingSupply, CurrentSupply: currentSupply, - Limit: limit, + SupplyLimit: supplyLimit, } } @@ -38,31 +38,23 @@ func (a AssetSupply) Validate() error { if !a.CurrentSupply.IsValid() { return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "current supply %s", a.CurrentSupply) } - if !a.Limit.IsValid() { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "limit %s", a.Limit) + if !a.SupplyLimit.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "supply limit %s", a.SupplyLimit) } return sdk.ValidateDenom(a.Denom) } // String implements stringer func (a AssetSupply) String() string { - return fmt.Sprintf("Asset Supply"+ - "\n Denom: %s"+ - "\n Incoming supply: %s"+ - "\n Outgoing supply: %s"+ - "\n Current supply: %s"+ - "\n Limit: %s"+ - a.Denom, a.IncomingSupply, a.OutgoingSupply, a.CurrentSupply, a.Limit) + return fmt.Sprintf(` + %s supply: + Incoming supply: %s + Outgoing supply: %s + Current supply: %s + Supply limit: %s + `, + a.Denom, a.IncomingSupply, a.OutgoingSupply, a.CurrentSupply, a.SupplyLimit) } // AssetSupplies is a slice of AssetSupply type AssetSupplies []AssetSupply - -// String implements stringer -func (supplies AssetSupplies) String() string { - out := "" - for _, supply := range supplies { - out += supply.String() + "\n" - } - return out -} diff --git a/x/bep3/types/asset_test.go b/x/bep3/types/asset_test.go index 186d34b6..1b25230f 100644 --- a/x/bep3/types/asset_test.go +++ b/x/bep3/types/asset_test.go @@ -43,12 +43,12 @@ func TestAssetSupplyValidate(t *testing.T) { false, }, { - "invalid limit", + "invalid supply limit", AssetSupply{ IncomingSupply: coin, OutgoingSupply: coin, CurrentSupply: coin, - Limit: invalidCoin, + SupplyLimit: invalidCoin, }, false, }, diff --git a/x/bep3/types/common_test.go b/x/bep3/types/common_test.go index bf481647..b94d8eab 100644 --- a/x/bep3/types/common_test.go +++ b/x/bep3/types/common_test.go @@ -25,10 +25,10 @@ func atomicSwaps(count int) types.AtomicSwaps { } func atomicSwap(index int) types.AtomicSwap { - expireOffset := int64((index * 15) + 360) // Default expire height + offet to match timestamp - timestamp := ts(index) // One minute apart + expireOffset := uint64((index * 15) + 360) // Default expire height + offet to match timestamp + timestamp := ts(index) // One minute apart randomNumber, _ := types.GenerateSecureRandomNumber() - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) swap := types.NewAtomicSwap(cs(c("bnb", 50000)), randomNumberHash, expireOffset, timestamp, kavaAddrs[0], kavaAddrs[1], binanceAddrs[0].String(), binanceAddrs[1].String(), 1, types.Open, true, types.Incoming) diff --git a/x/bep3/types/events.go b/x/bep3/types/events.go index 298f3be6..72743b41 100644 --- a/x/bep3/types/events.go +++ b/x/bep3/types/events.go @@ -16,7 +16,6 @@ const ( AttributeKeySenderOtherChain = "sender_other_chain" AttributeKeyExpireHeight = "expire_height" AttributeKeyAmount = "amount" - AttributeKeyExpectedIncome = "expected_income" AttributeKeyDirection = "direction" AttributeKeyClaimSender = "claim_sender" AttributeKeyRandomNumber = "random_number" diff --git a/x/bep3/types/hash.go b/x/bep3/types/hash.go index f5772eb0..1e347314 100644 --- a/x/bep3/types/hash.go +++ b/x/bep3/types/hash.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "encoding/binary" "errors" + "fmt" "math/big" "strings" @@ -13,22 +14,21 @@ import ( ) // GenerateSecureRandomNumber generates cryptographically strong pseudo-random number -func GenerateSecureRandomNumber() (*big.Int, error) { +func GenerateSecureRandomNumber() ([64]byte, error) { + // Max is a 256-bits integer i.e. 2^256 max := new(big.Int) - max.Exp(big.NewInt(2), big.NewInt(256), nil) // 256-bits integer i.e. 2^256 + max.Exp(big.NewInt(2), big.NewInt(256), nil) - // Generate number between 0 - max + // Generate number in the range [0, max] randomNumber, err := rand.Int(rand.Reader, max) if err != nil { - return big.NewInt(0), errors.New("random number generation error") + return [64]byte{}, errors.New("random number generation error") } - // Catch random numbers that encode to hexadecimal poorly - if len(randomNumber.Text(16)) != 64 { - return GenerateSecureRandomNumber() - } - - return randomNumber, nil + // Ensure length of 64 for hexadecimal encoding by padding with 0s + var paddedNumber [64]byte + copy(paddedNumber[:], fmt.Sprintf("%064x", randomNumber)) + return paddedNumber, nil } // CalculateRandomHash calculates the hash of a number and timestamp @@ -43,7 +43,7 @@ func CalculateRandomHash(randomNumber []byte, timestamp int64) []byte { func CalculateSwapID(randomNumberHash []byte, sender sdk.AccAddress, senderOtherChain string) []byte { senderOtherChain = strings.ToLower(senderOtherChain) data := randomNumberHash - data = append(data, []byte(sender)...) + data = append(data, sender.Bytes()...) data = append(data, []byte(senderOtherChain)...) return tmhash.Sum(data) } diff --git a/x/bep3/types/hash_test.go b/x/bep3/types/hash_test.go index ebfb0e46..a93df5ab 100644 --- a/x/bep3/types/hash_test.go +++ b/x/bep3/types/hash_test.go @@ -36,24 +36,24 @@ func (suite *HashTestSuite) TestGenerateSecureRandomNumber() { secureRandomNumber, err := types.GenerateSecureRandomNumber() suite.Nil(err) suite.NotNil(secureRandomNumber) - suite.Equal(64, len(secureRandomNumber.Text(16))) + suite.Equal(64, len(secureRandomNumber)) } func (suite *HashTestSuite) TestCalculateRandomHash() { randomNumber, _ := types.GenerateSecureRandomNumber() - hash := types.CalculateRandomHash(randomNumber.Bytes(), suite.timestamps[0]) + hash := types.CalculateRandomHash(randomNumber[:], suite.timestamps[0]) suite.NotNil(hash) suite.Equal(32, len(hash)) } func (suite *HashTestSuite) TestCalculateSwapID() { randomNumber, _ := types.GenerateSecureRandomNumber() - hash := types.CalculateRandomHash(randomNumber.Bytes(), suite.timestamps[3]) + hash := types.CalculateRandomHash(randomNumber[:], suite.timestamps[3]) swapID := types.CalculateSwapID(hash, suite.addrs[3], suite.addrs[5].String()) suite.NotNil(swapID) suite.Equal(32, len(swapID)) - diffHash := types.CalculateRandomHash(randomNumber.Bytes(), suite.timestamps[2]) + diffHash := types.CalculateRandomHash(randomNumber[:], suite.timestamps[2]) diffSwapID := types.CalculateSwapID(diffHash, suite.addrs[3], suite.addrs[5].String()) suite.NotEqual(swapID, diffSwapID) } diff --git a/x/bep3/types/keys.go b/x/bep3/types/keys.go index fa5316dc..1ebe4e7b 100644 --- a/x/bep3/types/keys.go +++ b/x/bep3/types/keys.go @@ -1,8 +1,7 @@ package types import ( - "encoding/binary" - "encoding/hex" + sdk "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -23,7 +22,7 @@ const ( ) // DefaultLongtermStorageDuration is 1 week (assuming a block time of 7 seconds) -const DefaultLongtermStorageDuration int64 = 86400 +const DefaultLongtermStorageDuration uint64 = 86400 // Key prefixes var ( @@ -34,34 +33,6 @@ var ( ) // GetAtomicSwapByHeightKey is used by the AtomicSwapByBlock index and AtomicSwapLongtermStorage index -func GetAtomicSwapByHeightKey(height int64, swapID []byte) []byte { - return append(Uint64ToBytes(uint64(height)), swapID...) -} - -// Uint64ToBytes converts a uint64 into fixed length bytes for use in store keys. -func Uint64ToBytes(id uint64) []byte { - bz := make([]byte, 8) - binary.BigEndian.PutUint64(bz, uint64(id)) - return bz -} - -// Uint64FromBytes converts some fixed length bytes back into a uint64. -func Uint64FromBytes(bz []byte) uint64 { - return binary.BigEndian.Uint64(bz) -} - -// BytesToHex converts data from []byte to a hex-encoded string -func BytesToHex(data []byte) string { - encodedData := make([]byte, hex.EncodedLen(len(data))) - hex.Encode(encodedData, data) - return string(encodedData) -} - -// HexToBytes converts data from a hex-encoded string to []bytes -func HexToBytes(data string) ([]byte, error) { - decodedData, err := hex.DecodeString(data) - if err != nil { - return []byte{}, err - } - return decodedData, nil +func GetAtomicSwapByHeightKey(height uint64, swapID []byte) []byte { + return append(sdk.Uint64ToBigEndian(height), swapID...) } diff --git a/x/bep3/types/msg.go b/x/bep3/types/msg.go index 92be18da..4f46a3ad 100644 --- a/x/bep3/types/msg.go +++ b/x/bep3/types/msg.go @@ -13,11 +13,10 @@ import ( ) const ( - CreateAtomicSwap = "createAtomicSwap" - DepositAtomicSwap = "depositAtomicSwap" - ClaimAtomicSwap = "claimAtomicSwap" - RefundAtomicSwap = "refundAtomicSwap" - CalcSwapID = "calcSwapID" + CreateAtomicSwap = "createAtomicSwap" + ClaimAtomicSwap = "claimAtomicSwap" + RefundAtomicSwap = "refundAtomicSwap" + CalcSwapID = "calcSwapID" Int64Size = 8 RandomNumberHashLength = 32 @@ -47,15 +46,13 @@ type MsgCreateAtomicSwap struct { RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` Timestamp int64 `json:"timestamp" yaml:"timestamp"` Amount sdk.Coins `json:"amount" yaml:"amount"` - ExpectedIncome string `json:"expected_income" yaml:"expected_income"` - HeightSpan int64 `json:"height_span" yaml:"height_span"` - CrossChain bool `json:"cross_chain" yaml:"cross_chain"` + HeightSpan uint64 `json:"height_span" yaml:"height_span"` } // NewMsgCreateAtomicSwap initializes a new MsgCreateAtomicSwap func NewMsgCreateAtomicSwap(from sdk.AccAddress, to sdk.AccAddress, recipientOtherChain, senderOtherChain string, randomNumberHash tmbytes.HexBytes, timestamp int64, - amount sdk.Coins, expectedIncome string, heightSpan int64, crossChain bool) MsgCreateAtomicSwap { + amount sdk.Coins, heightSpan uint64) MsgCreateAtomicSwap { return MsgCreateAtomicSwap{ From: from, To: to, @@ -64,9 +61,7 @@ func NewMsgCreateAtomicSwap(from sdk.AccAddress, to sdk.AccAddress, recipientOth RandomNumberHash: randomNumberHash, Timestamp: timestamp, Amount: amount, - ExpectedIncome: expectedIncome, HeightSpan: heightSpan, - CrossChain: crossChain, } } @@ -78,10 +73,9 @@ func (msg MsgCreateAtomicSwap) Type() string { return CreateAtomicSwap } // String prints the MsgCreateAtomicSwap func (msg MsgCreateAtomicSwap) String() string { - return fmt.Sprintf("AtomicSwap{%v#%v#%v#%v#%v#%v#%v#%v#%v#%v}", + return fmt.Sprintf("AtomicSwap{%v#%v#%v#%v#%v#%v#%v#%v}", msg.From, msg.To, msg.RecipientOtherChain, msg.SenderOtherChain, - msg.RandomNumberHash, msg.Timestamp, msg.Amount, msg.ExpectedIncome, - msg.HeightSpan, msg.CrossChain) + msg.RandomNumberHash, msg.Timestamp, msg.Amount, msg.HeightSpan) } // GetInvolvedAddresses gets the addresses involved in a MsgCreateAtomicSwap @@ -108,14 +102,8 @@ func (msg MsgCreateAtomicSwap) ValidateBasic() error { if len(msg.To) != AddrByteCount { return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.To)) } - if !msg.CrossChain && msg.RecipientOtherChain != "" { - return errors.New("must leave recipient address on other chain to empty for single chain swap") - } - if !msg.CrossChain && msg.SenderOtherChain != "" { - return errors.New("must leave sender address on other chain to empty for single chain swap") - } - if msg.CrossChain && strings.TrimSpace(msg.RecipientOtherChain) == "" { - return errors.New("missing recipient address on other chain for cross chain swap") + if strings.TrimSpace(msg.RecipientOtherChain) == "" { + return errors.New("missing recipient address on other chain") } if len(msg.RecipientOtherChain) > MaxOtherChainAddrLength { return fmt.Errorf("the length of recipient address on other chain should be less than %d", MaxOtherChainAddrLength) @@ -135,16 +123,6 @@ func (msg MsgCreateAtomicSwap) ValidateBasic() error { if !msg.Amount.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) } - if len(msg.ExpectedIncome) > MaxExpectedIncomeLength { - return fmt.Errorf("the length of expected income should be less than %d", MaxExpectedIncomeLength) - } - expectedIncomeCoins, err := sdk.ParseCoins(msg.ExpectedIncome) - if err != nil || expectedIncomeCoins == nil { - return fmt.Errorf("expected income %s must be in valid format e.g. 10000ukava", msg.ExpectedIncome) - } - if expectedIncomeCoins.IsAnyGT(msg.Amount) { - return fmt.Errorf("expected income %s cannot be greater than amount %s", msg.ExpectedIncome, msg.Amount.String()) - } if msg.HeightSpan <= 0 { return errors.New("height span must be positive") } diff --git a/x/bep3/types/msg_test.go b/x/bep3/types/msg_test.go index 3940e0b6..d0ce53d1 100644 --- a/x/bep3/types/msg_test.go +++ b/x/bep3/types/msg_test.go @@ -39,16 +39,12 @@ func TestMsgCreateAtomicSwap(t *testing.T) { randomNumberHash tmbytes.HexBytes timestamp int64 amount sdk.Coins - expectedIncome string - heightSpan int64 - crossChain bool + heightSpan uint64 expectPass bool }{ - {"normal", binanceAddrs[0], kavaAddrs[0], "", "", randomNumberHash, timestampInt64, coinsSingle, "50000bnb", 500, false, true}, - {"cross-chain", binanceAddrs[0], kavaAddrs[0], kavaAddrs[0].String(), binanceAddrs[0].String(), randomNumberHash, timestampInt64, coinsSingle, "50000bnb", 80000, true, true}, - {"with other chain fields", binanceAddrs[0], kavaAddrs[0], kavaAddrs[0].String(), binanceAddrs[0].String(), randomNumberHash, timestampInt64, coinsSingle, "50000bnb", 500, false, false}, - {"cross-cross no other chain fields", binanceAddrs[0], kavaAddrs[0], "", "", randomNumberHash, timestampInt64, coinsSingle, "50000bnb", 500, true, false}, - {"zero coins", binanceAddrs[0], kavaAddrs[0], "", "", randomNumberHash, timestampInt64, coinsZero, "50000bnb", 500, true, false}, + {"normal cross-chain", binanceAddrs[0], kavaAddrs[0], kavaAddrs[0].String(), binanceAddrs[0].String(), randomNumberHash, timestampInt64, coinsSingle, 500, true}, + {"without other chain fields", binanceAddrs[0], kavaAddrs[0], "", "", randomNumberHash, timestampInt64, coinsSingle, 500, false}, + {"invalid amount", binanceAddrs[0], kavaAddrs[0], "", "", randomNumberHash, timestampInt64, coinsZero, 500, false}, } for i, tc := range tests { @@ -60,9 +56,7 @@ func TestMsgCreateAtomicSwap(t *testing.T) { tc.randomNumberHash, tc.timestamp, tc.amount, - tc.expectedIncome, tc.heightSpan, - tc.crossChain, ) if tc.expectPass { require.NoError(t, msg.ValidateBasic(), "test: %v", i) diff --git a/x/bep3/types/params.go b/x/bep3/types/params.go index b7d04493..73b23ab3 100644 --- a/x/bep3/types/params.go +++ b/x/bep3/types/params.go @@ -20,11 +20,11 @@ var ( KeyMaxBlockLock = []byte("MaxBlockLock") KeySupportedAssets = []byte("SupportedAssets") - AbsoluteMaximumBlockLock int64 = 10000 - AbsoluteMinimumBlockLock int64 = 50 - DefaultMinBlockLock int64 = 80 - DefaultMaxBlockLock int64 = 600 - DefaultSupportedAssets = AssetParams{ + AbsoluteMaximumBlockLock uint64 = 10000 + AbsoluteMinimumBlockLock uint64 = 50 + DefaultMinBlockLock uint64 = 80 + DefaultMaxBlockLock uint64 = 600 + DefaultSupportedAssets = AssetParams{ AssetParam{ Denom: "bnb", CoinID: 714, @@ -37,8 +37,8 @@ var ( // Params governance parameters for bep3 module type Params struct { BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // Bnbchain deputy address - MinBlockLock int64 `json:"min_block_lock" yaml:"min_block_lock"` // AtomicSwap minimum block lock - MaxBlockLock int64 `json:"max_block_lock" yaml:"max_block_lock"` // AtomicSwap maximum block lock + MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // AtomicSwap minimum block lock + MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // AtomicSwap maximum block lock SupportedAssets AssetParams `json:"supported_assets" yaml:"supported_assets"` // Supported assets } @@ -53,7 +53,7 @@ func (p Params) String() string { } // NewParams returns a new params object -func NewParams(bnbDeputyAddress sdk.AccAddress, minBlockLock, maxBlockLock int64, supportedAssets AssetParams, +func NewParams(bnbDeputyAddress sdk.AccAddress, minBlockLock, maxBlockLock uint64, supportedAssets AssetParams, ) Params { return Params{ BnbDeputyAddress: bnbDeputyAddress, @@ -159,7 +159,7 @@ func validateBnbDeputyAddressParam(i interface{}) error { } func validateMinBlockLockParam(i interface{}) error { - minBlockLock, ok := i.(int64) + minBlockLock, ok := i.(uint64) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } @@ -172,7 +172,7 @@ func validateMinBlockLockParam(i interface{}) error { } func validateMaxBlockLockParam(i interface{}) error { - maxBlockLock, ok := i.(int64) + maxBlockLock, ok := i.(uint64) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } diff --git a/x/bep3/types/params_test.go b/x/bep3/types/params_test.go index 9532cbf5..f8bc1536 100644 --- a/x/bep3/types/params_test.go +++ b/x/bep3/types/params_test.go @@ -29,8 +29,8 @@ func (suite *ParamsTestSuite) TestParamValidation() { type args struct { bnbDeputyAddress sdk.AccAddress - minBlockLock int64 - maxBlockLock int64 + minBlockLock uint64 + maxBlockLock uint64 supportedAssets types.AssetParams } diff --git a/x/bep3/types/querier.go b/x/bep3/types/querier.go index 034a851b..28e2d3de 100644 --- a/x/bep3/types/querier.go +++ b/x/bep3/types/querier.go @@ -1,10 +1,15 @@ package types -import tmbytes "github.com/tendermint/tendermint/libs/bytes" +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" +) const ( // QueryGetAssetSupply command for getting info about an asset's supply QueryGetAssetSupply = "supply" + // QueryGetAssetSupplies command for getting a list of asset supplies + QueryGetAssetSupplies = "supplies" // QueryGetAtomicSwap command for getting info about an atomic swap QueryGetAtomicSwap = "swap" // QueryGetAtomicSwaps command for getting a list of atomic swaps @@ -25,6 +30,20 @@ func NewQueryAssetSupply(denom tmbytes.HexBytes) QueryAssetSupply { } } +// QueryAssetSupplies contains the params for an AssetSupplies query +type QueryAssetSupplies struct { + Page int `json:"page" yaml:"page"` + Limit int `json:"limit" yaml:"limit"` +} + +// NewQueryAssetSupplies creates a new QueryAssetSupplies +func NewQueryAssetSupplies(page int, limit int) QueryAssetSupplies { + return QueryAssetSupplies{ + Page: page, + Limit: limit, + } +} + // QueryAtomicSwapByID contains the params for query 'custom/bep3/swap' type QueryAtomicSwapByID struct { SwapID tmbytes.HexBytes `json:"swap_id" yaml:"swap_id"` @@ -39,14 +58,23 @@ func NewQueryAtomicSwapByID(swapBytes tmbytes.HexBytes) QueryAtomicSwapByID { // QueryAtomicSwaps contains the params for an AtomicSwaps query type QueryAtomicSwaps struct { - Page int `json:"page" yaml:"page"` - Limit int `json:"limit" yaml:"limit"` + Page int `json:"page" yaml:"page"` + Limit int `json:"limit" yaml:"limit"` + Involve sdk.AccAddress `json:"involve" yaml:"involve"` + Expiration uint64 `json:"expiration" yaml:"expiration"` + Status SwapStatus `json:"status" yaml:"status"` + Direction SwapDirection `json:"direction" yaml:"direction"` } -// NewQueryAtomicSwaps creates a new QueryAtomicSwaps -func NewQueryAtomicSwaps(page int, limit int) QueryAtomicSwaps { +// NewQueryAtomicSwaps creates a new instance of QueryAtomicSwaps +func NewQueryAtomicSwaps(page, limit int, involve sdk.AccAddress, expiration uint64, + status SwapStatus, direction SwapDirection) QueryAtomicSwaps { return QueryAtomicSwaps{ - Page: page, - Limit: limit, + Page: page, + Limit: limit, + Involve: involve, + Expiration: expiration, + Status: status, + Direction: direction, } } diff --git a/x/bep3/types/swap.go b/x/bep3/types/swap.go index 8ac98170..5747a449 100644 --- a/x/bep3/types/swap.go +++ b/x/bep3/types/swap.go @@ -17,7 +17,7 @@ import ( type AtomicSwap struct { Amount sdk.Coins `json:"amount" yaml:"amount"` RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` - ExpireHeight int64 `json:"expire_height" yaml:"expire_height"` + ExpireHeight uint64 `json:"expire_height" yaml:"expire_height"` Timestamp int64 `json:"timestamp" yaml:"timestamp"` Sender sdk.AccAddress `json:"sender" yaml:"sender"` Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"` @@ -30,8 +30,8 @@ type AtomicSwap struct { } // NewAtomicSwap returns a new AtomicSwap -func NewAtomicSwap(amount sdk.Coins, randomNumberHash tmbytes.HexBytes, expireHeight, timestamp int64, sender, - recipient sdk.AccAddress, senderOtherChain string, recipientOtherChain string, closedBlock int64, +func NewAtomicSwap(amount sdk.Coins, randomNumberHash tmbytes.HexBytes, expireHeight uint64, timestamp int64, + sender, recipient sdk.AccAddress, senderOtherChain string, recipientOtherChain string, closedBlock int64, status SwapStatus, crossChain bool, direction SwapDirection) AtomicSwap { return AtomicSwap{ Amount: amount, @@ -197,6 +197,16 @@ func (status *SwapStatus) UnmarshalJSON(data []byte) error { return nil } +// IsValid returns true if the swap status is valid and false otherwise. +func (status SwapStatus) IsValid() bool { + if status == Open || + status == Completed || + status == Expired { + return true + } + return false +} + // SwapDirection is the direction of an AtomicSwap type SwapDirection byte @@ -245,3 +255,12 @@ func (direction *SwapDirection) UnmarshalJSON(data []byte) error { *direction = NewSwapDirectionFromString(s) return nil } + +// IsValid returns true if the swap direction is valid and false otherwise. +func (direction SwapDirection) IsValid() bool { + if direction == Incoming || + direction == Outgoing { + return true + } + return false +} diff --git a/x/bep3/types/swap_test.go b/x/bep3/types/swap_test.go index 0f06ee18..b59dc6dc 100644 --- a/x/bep3/types/swap_test.go +++ b/x/bep3/types/swap_test.go @@ -32,7 +32,7 @@ func (suite *AtomicSwapTestSuite) SetupTest() { for i := 0; i < 10; i++ { timestamp := ts(i) randomNumber, _ := types.GenerateSecureRandomNumber() - randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp) + randomNumberHash := types.CalculateRandomHash(randomNumber[:], timestamp) timestamps = append(timestamps, timestamp) randomNumberHashes = append(randomNumberHashes, randomNumberHash) }