diff --git a/x/bep3/abci.go b/x/bep3/abci.go index 8675da52..97b14027 100644 --- a/x/bep3/abci.go +++ b/x/bep3/abci.go @@ -9,4 +9,7 @@ import ( func BeginBlocker(ctx sdk.Context, k Keeper) { k.UpdateExpiredAtomicSwaps(ctx) k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx) + if ctx.BlockTime().After(SupplyLimitUpgradeTime) { + k.UpdateAssetSupplies(ctx) + } } diff --git a/x/bep3/abci_test.go b/x/bep3/abci_test.go index 700ddcb3..e4687c35 100644 --- a/x/bep3/abci_test.go +++ b/x/bep3/abci_test.go @@ -2,6 +2,7 @@ package bep3_test import ( "testing" + "time" "github.com/stretchr/testify/suite" @@ -226,7 +227,7 @@ func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongterm // Run the second begin blocker bep3.BeginBlocker(tc.secondCtx, suite.keeper) - // Check each swap's availibility and status + // Check each swap's availability and status for _, swapID := range suite.swapIDs { _, found := suite.keeper.GetAtomicSwap(tc.secondCtx, swapID) if tc.expectInStorage { @@ -239,6 +240,61 @@ func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongterm } } +func (suite *ABCITestSuite) TestBeginBlocker_UpdateAssetSupplies() { + // set new asset limit in the params + newBnbLimit := c("bnb", 100) + params := suite.keeper.GetParams(suite.ctx) + for i := range params.SupportedAssets { + if params.SupportedAssets[i].Denom != newBnbLimit.Denom { + continue + } + params.SupportedAssets[i].Limit = newBnbLimit.Amount + } + suite.keeper.SetParams(suite.ctx, params) + + // store the old limit for future reference + supply, found := suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom)) + suite.True(found) + oldBnbLimit := supply.SupplyLimit + + // run before the upgrade time, check limit was not changed + bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime.Add(-time.Hour)), suite.keeper) + + supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom)) + suite.True(found) + suite.True(supply.SupplyLimit.IsEqual(oldBnbLimit)) + + // run at precise upgrade time, check limit was not changed + bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime), suite.keeper) + + supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom)) + suite.True(found) + suite.True(supply.SupplyLimit.IsEqual(oldBnbLimit)) + + // run after upgrade time, check limit was changed + bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime.Add(time.Nanosecond)), suite.keeper) + + supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom)) + suite.True(found) + suite.True(supply.SupplyLimit.IsEqual(newBnbLimit)) + + // run again with new params, check limit was updated to new param limit + finalBnbLimit := c("bnb", 5000000000000) + params = suite.keeper.GetParams(suite.ctx) + for i := range params.SupportedAssets { + if params.SupportedAssets[i].Denom != finalBnbLimit.Denom { + continue + } + params.SupportedAssets[i].Limit = finalBnbLimit.Amount + } + suite.keeper.SetParams(suite.ctx, params) + bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime.Add(time.Hour)), suite.keeper) + + supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom)) + suite.True(found) + suite.True(supply.SupplyLimit.IsEqual(finalBnbLimit)) +} + func TestABCITestSuite(t *testing.T) { suite.Run(t, new(ABCITestSuite)) } diff --git a/x/bep3/alias.go b/x/bep3/alias.go index a0950f0c..cec60425 100644 --- a/x/bep3/alias.go +++ b/x/bep3/alias.go @@ -116,6 +116,7 @@ var ( KeyMinBlockLock = types.KeyMinBlockLock KeySupportedAssets = types.KeySupportedAssets ModuleCdc = types.ModuleCdc + SupplyLimitUpgradeTime = types.SupplyLimitUpgradeTime ) type ( diff --git a/x/bep3/keeper/asset.go b/x/bep3/keeper/asset.go index a5c65b6c..75b11786 100644 --- a/x/bep3/keeper/asset.go +++ b/x/bep3/keeper/asset.go @@ -113,3 +113,18 @@ func (k Keeper) DecrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) err k.SetAssetSupply(ctx, supply, []byte(coin.Denom)) return nil } + +// UpdateAssetSupplies applies updates to the asset limit from parameters to the asset supplies +func (k Keeper) UpdateAssetSupplies(ctx sdk.Context) { + params := k.GetParams(ctx) + for _, supportedAsset := range params.SupportedAssets { + asset, found := k.GetAssetSupply(ctx, []byte(supportedAsset.Denom)) + if !found { + continue + } + if !asset.SupplyLimit.Amount.Equal(supportedAsset.Limit) { + asset.SupplyLimit = sdk.NewCoin(supportedAsset.Denom, supportedAsset.Limit) + k.SetAssetSupply(ctx, asset, []byte(supportedAsset.Denom)) + } + } +} diff --git a/x/bep3/keeper/asset_test.go b/x/bep3/keeper/asset_test.go index 84206575..4b8bc58a 100644 --- a/x/bep3/keeper/asset_test.go +++ b/x/bep3/keeper/asset_test.go @@ -413,6 +413,25 @@ func (suite *AssetTestSuite) TestDecrementOutgoingAssetSupply() { } } +func (suite *AssetTestSuite) TestUpdateAssetSupplies() { + // set new asset limit in the params + newBnbLimit := c("bnb", 100) + params := suite.keeper.GetParams(suite.ctx) + for i := range params.SupportedAssets { + if params.SupportedAssets[i].Denom != newBnbLimit.Denom { + continue + } + params.SupportedAssets[i].Limit = newBnbLimit.Amount + } + suite.keeper.SetParams(suite.ctx, params) + + suite.keeper.UpdateAssetSupplies(suite.ctx) + + supply, found := suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom)) + suite.True(found) + suite.True(supply.SupplyLimit.IsEqual(newBnbLimit)) +} + func TestAssetTestSuite(t *testing.T) { suite.Run(t, new(AssetTestSuite)) } diff --git a/x/bep3/keeper/swap.go b/x/bep3/keeper/swap.go index 7a3f9f6e..3b1d8e13 100644 --- a/x/bep3/keeper/swap.go +++ b/x/bep3/keeper/swap.go @@ -69,6 +69,12 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times // Incoming swaps have already had their fees collected by the deputy during the relay process. err = k.IncrementIncomingAssetSupply(ctx, amount[0]) case types.Outgoing: + if ctx.BlockTime().After(types.SupplyLimitUpgradeTime) { + if !recipient.Equals(deputy) { + return sdkerrors.Wrapf(types.ErrInvalidOutgoingAccount, "%s", recipient) + } + } + // Outoing swaps must have a height span within the accepted range if heightSpan < k.GetMinBlockLock(ctx) || heightSpan > k.GetMaxBlockLock(ctx) { return sdkerrors.Wrapf(types.ErrInvalidHeightSpan, "height span %d outside range [%d, %d]", heightSpan, k.GetMinBlockLock(ctx), k.GetMaxBlockLock(ctx)) diff --git a/x/bep3/keeper/swap_test.go b/x/bep3/keeper/swap_test.go index 90efc5f3..1ae2648e 100644 --- a/x/bep3/keeper/swap_test.go +++ b/x/bep3/keeper/swap_test.go @@ -146,7 +146,7 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { timestamp: suite.timestamps[0], heightSpan: types.DefaultMinBlockLock, sender: suite.addrs[1], - recipient: suite.addrs[2], + recipient: suite.deputy, senderOtherChain: TestSenderOtherChain, recipientOtherChain: TestRecipientOtherChain, coins: cs(c(BNB_DENOM, 50000)), @@ -156,6 +156,60 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { true, true, }, + { + "outgoing swap recipient before update", + types.SupplyLimitUpgradeTime.Add(-time.Hour), + args{ + randomNumberHash: suite.randomNumberHashes[12], + timestamp: types.SupplyLimitUpgradeTime.Add(-time.Hour).Unix(), + heightSpan: types.DefaultMinBlockLock, + sender: suite.addrs[1], + recipient: suite.addrs[2], + senderOtherChain: TestUser1.String(), + recipientOtherChain: TestUser2.String(), + coins: cs(c(BNB_DENOM, 50000)), + crossChain: true, + direction: types.Outgoing, + }, + true, + true, + }, + { + "outgoing swap recipient at update time", + types.SupplyLimitUpgradeTime, + args{ + randomNumberHash: suite.randomNumberHashes[13], + timestamp: types.SupplyLimitUpgradeTime.Unix(), + heightSpan: types.DefaultMinBlockLock, + sender: suite.addrs[1], + recipient: suite.addrs[2], + senderOtherChain: TestUser1.String(), + recipientOtherChain: TestUser2.String(), + coins: cs(c(BNB_DENOM, 50000)), + crossChain: true, + direction: types.Outgoing, + }, + true, + true, + }, + { + "outgoing swap recipient after update", + types.SupplyLimitUpgradeTime.Add(time.Nanosecond), + args{ + randomNumberHash: suite.randomNumberHashes[14], + timestamp: types.SupplyLimitUpgradeTime.Add(time.Nanosecond).Unix(), + heightSpan: types.DefaultMinBlockLock, + sender: suite.addrs[1], + recipient: suite.addrs[2], + senderOtherChain: TestUser1.String(), + recipientOtherChain: TestUser2.String(), + coins: cs(c(BNB_DENOM, 50000)), + crossChain: true, + direction: types.Outgoing, + }, + false, + false, + }, { "outgoing swap amount not greater than fixed fee", @@ -360,6 +414,7 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() { for _, tc := range testCases { suite.Run(tc.name, func() { // Increment current asset supply to support outgoing swaps + suite.ctx = suite.ctx.WithBlockTime(tc.blockTime) if tc.args.direction == types.Outgoing { err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, tc.args.coins[0]) suite.Nil(err) diff --git a/x/bep3/types/errors.go b/x/bep3/types/errors.go index 1a1bc080..a07560cd 100644 --- a/x/bep3/types/errors.go +++ b/x/bep3/types/errors.go @@ -41,4 +41,6 @@ var ( ErrSwapNotClaimable = sdkerrors.Register(ModuleName, 17, "atomic swap is not claimable") // ErrInvalidAmount error for when a swap's amount is outside acceptable range ErrInvalidAmount = sdkerrors.Register(ModuleName, 18, "amount is outside acceptable range") + // ErrInvalidOutgoingAccount error for when an outgoing swap has a recipient value that is not the deputy address + ErrInvalidOutgoingAccount = sdkerrors.Register(ModuleName, 19, "invalid recipient address for outgoing swap") ) diff --git a/x/bep3/types/keys.go b/x/bep3/types/keys.go index 1ebe4e7b..1f39f531 100644 --- a/x/bep3/types/keys.go +++ b/x/bep3/types/keys.go @@ -1,6 +1,8 @@ package types import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -19,13 +21,16 @@ const ( // DefaultParamspace default namestore DefaultParamspace = ModuleName -) -// DefaultLongtermStorageDuration is 1 week (assuming a block time of 7 seconds) -const DefaultLongtermStorageDuration uint64 = 86400 + // DefaultLongtermStorageDuration is 1 week (assuming a block time of 7 seconds) + DefaultLongtermStorageDuration uint64 = 86400 +) // Key prefixes var ( + // SupplyLimitUpgradeTime is the block time after which the asset supply limits are updated from params + SupplyLimitUpgradeTime time.Time = time.Date(2020, 7, 8, 14, 0, 0, 0, time.UTC) + AtomicSwapKeyPrefix = []byte{0x00} // prefix for keys that store AtomicSwaps AtomicSwapByBlockPrefix = []byte{0x01} // prefix for keys of the AtomicSwapsByBlock index AssetSupplyKeyPrefix = []byte{0x02} // prefix for keys that store global asset supply counts