[R4R] bep3 module audit revisions (#505)

* most audit revisions

* remove expected income

* update begin block spec

* filter queryAtomicSwaps, add queryAssetSupplies

* update old address

* update test

* Remove legacy method

* remove legacy comment

* address PR comments

* IsValid for SwapDirection, SwapStatus

* fix rng logging

* query asset supplies

* return [64]byte from rng

* remove cross chain field from MsgCreateAtomicSwap

* move swap filtering to querier

* rename Limit field to SupplyLimit
This commit is contained in:
Denali Marsh 2020-05-12 13:15:38 -07:00 committed by GitHub
parent 04cb414593
commit 6c68e41758
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 648 additions and 407 deletions

View File

@ -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)
}

View File

@ -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))
}
}

View File

@ -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

View File

@ -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",
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{
cmd := &cobra.Command{
Use: "swaps",
Short: "get a list of active atomic swaps",
Example: "bep3 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

View File

@ -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"
@ -27,6 +28,9 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command {
bep3TxCmd := &cobra.Command{
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
}

View File

@ -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)

View File

@ -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"`
}

View File

@ -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())

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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)

View File

@ -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
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)

View File

@ -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)

View File

@ -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))

View File

@ -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
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)
}

View File

@ -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()

View File

@ -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)

View File

@ -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
}

View File

@ -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, &params)
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
}

View File

@ -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)

View File

@ -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)
swap, found := k.GetAtomicSwap(ctx, id)
if !found {
// NOTE: shouldn't happen. Continue to next item.
return false
})
// Delete closed atomic swaps
for _, id := range swapsToDelete {
swap, _ := k.GetAtomicSwap(ctx, id)
}
k.RemoveAtomicSwap(ctx, swap.GetSwapID())
k.RemoveFromLongtermStorage(ctx, swap)
}
return nil
return false
})
}

View File

@ -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)

View File

@ -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{

View File

@ -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

View File

@ -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),

View File

@ -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"`
}

View File

@ -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"`
}
```

View File

@ -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} |

View File

@ -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
k.IterateAtomicSwapsByBlock(ctx, uint64(ctx.BlockHeight()), func(id []byte) bool {
expiredSwaps = append(expiredSwaps, id)
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)
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 {
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
})
```
## 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
})
```

View File

@ -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
}

View File

@ -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,
},

View File

@ -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
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)

View File

@ -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"

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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...)
}

View File

@ -14,7 +14,6 @@ import (
const (
CreateAtomicSwap = "createAtomicSwap"
DepositAtomicSwap = "depositAtomicSwap"
ClaimAtomicSwap = "claimAtomicSwap"
RefundAtomicSwap = "refundAtomicSwap"
CalcSwapID = "calcSwapID"
@ -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")
}

View File

@ -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)

View File

@ -20,10 +20,10 @@ var (
KeyMaxBlockLock = []byte("MaxBlockLock")
KeySupportedAssets = []byte("SupportedAssets")
AbsoluteMaximumBlockLock int64 = 10000
AbsoluteMinimumBlockLock int64 = 50
DefaultMinBlockLock int64 = 80
DefaultMaxBlockLock int64 = 600
AbsoluteMaximumBlockLock uint64 = 10000
AbsoluteMinimumBlockLock uint64 = 50
DefaultMinBlockLock uint64 = 80
DefaultMaxBlockLock uint64 = 600
DefaultSupportedAssets = AssetParams{
AssetParam{
Denom: "bnb",
@ -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)
}

View File

@ -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
}

View File

@ -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"`
@ -41,12 +60,21 @@ func NewQueryAtomicSwapByID(swapBytes tmbytes.HexBytes) QueryAtomicSwapByID {
type QueryAtomicSwaps struct {
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,
Involve: involve,
Expiration: expiration,
Status: status,
Direction: direction,
}
}

View File

@ -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
}

View File

@ -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)
}