Skip to content

Commit

Permalink
check route is ready or not and deactivate the tunnel
Browse files Browse the repository at this point in the history
  • Loading branch information
nkitlabs committed Dec 20, 2024
1 parent 1accdef commit e9fa0b8
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 21 deletions.
8 changes: 8 additions & 0 deletions x/bandtss/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,11 @@ func (k Keeper) GetCurrentGroup(ctx sdk.Context) types.CurrentGroup {
k.cdc.MustUnmarshal(bz, &currentGroup)
return currentGroup
}

// IsReady returns whether the module is ready to produce a tss signing or not.
func (k Keeper) IsReady(ctx sdk.Context) bool {
isCurrentGroupReady := k.GetCurrentGroup(ctx).GroupID != 0
isIncomingGroupReady := k.GetIncomingGroupID(ctx) != 0

return isCurrentGroupReady || isIncomingGroupReady
}
18 changes: 17 additions & 1 deletion x/tunnel/keeper/keeper_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,25 @@ func (k Keeper) ProduceActiveTunnelPacket(
tunnelID uint64,
pricesMap map[string]feedstypes.Price,
) (err error) {
// get route information
tunnel, err := k.GetTunnel(ctx, tunnelID)
if err != nil {
return err
}

route, err := tunnel.GetRouteValue()
if err != nil {
return err
}

// Check if the route is ready for receiving a new packet. If not, deactivate the tunnel.
if !k.IsRouteReady(ctx, route, tunnelID) {
return k.DeactivateTunnel(ctx, tunnelID)
}

// Check if the tunnel has enough fund to create a packet and deactivate the tunnel if not
// enough fund. Error should not happen here since the tunnel is already validated.
ok, err := k.HasEnoughFundToCreatePacket(ctx, tunnelID)
ok, err := k.HasEnoughFundToCreatePacket(ctx, route, sdk.MustAccAddressFromBech32(tunnel.FeePayer))
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions x/tunnel/keeper/keeper_packet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ func (s *KeeperTestSuite) TestProducePacket() {

k.SetTunnel(ctx, tunnel)

s.bandtssKeeper.EXPECT().IsReady(gomock.Any()).Return(true)
err = k.ActivateTunnel(ctx, tunnelID)
s.Require().NoError(err)

Expand Down Expand Up @@ -189,6 +190,7 @@ func (s *KeeperTestSuite) TestProduceActiveTunnelPackets() {
DestinationContractAddress: "0x",
}

s.bandtssKeeper.EXPECT().IsReady(gomock.Any()).Return(true).AnyTimes()
s.bandtssKeeper.EXPECT().GetSigningFee(gomock.Any()).Return(
sdk.NewCoins(sdk.NewCoin("uband", sdkmath.NewInt(20))), nil,
).Times(2)
Expand Down Expand Up @@ -259,6 +261,7 @@ func (s *KeeperTestSuite) TestProduceActiveTunnelPacketsNotEnoughMoney() {
DestinationContractAddress: "0x",
}

s.bandtssKeeper.EXPECT().IsReady(gomock.Any()).Return(true).AnyTimes()
s.bandtssKeeper.EXPECT().GetSigningFee(gomock.Any()).Return(
sdk.NewCoins(sdk.NewCoin("uband", sdkmath.NewInt(20))), nil,
)
Expand Down
7 changes: 7 additions & 0 deletions x/tunnel/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ func (s *KeeperTestSuite) AddSampleTunnel(isActive bool) *types.Tunnel {
tunnel.TotalDeposit = append(tunnel.TotalDeposit, k.GetParams(ctx).MinDeposit...)
k.SetTunnel(ctx, tunnel)

route, err := tunnel.GetRouteValue()
s.Require().NoError(err)

if _, ok := route.(*types.TSSRoute); ok {
s.bandtssKeeper.EXPECT().IsReady(gomock.Any()).Return(true)
}

err = k.ActivateTunnel(ctx, tunnel.ID)
s.Require().NoError(err)
}
Expand Down
52 changes: 36 additions & 16 deletions x/tunnel/keeper/keeper_tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package keeper
import (
"fmt"

host "github.com/cosmos/ibc-go/v8/modules/core/24-host"

storetypes "cosmossdk.io/store/types"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -202,6 +204,16 @@ func (k Keeper) ActivateTunnel(ctx sdk.Context, tunnelID uint64) error {
// add the tunnel ID to the active tunnel IDs
k.SetActiveTunnelID(ctx, tunnelID)

route, err := tunnel.GetRouteValue()
if err != nil {
return err
}

// check whether the router is ready or not
if !k.IsRouteReady(ctx, route, tunnelID) {
return types.ErrRouteNotReady.Wrapf("tunnelID: %d", tunnelID)
}

// set the last interval timestamp to the current block time
tunnel.IsActive = true
k.SetTunnel(ctx, tunnel)
Expand Down Expand Up @@ -256,17 +268,11 @@ func (k Keeper) GetTotalFees(ctx sdk.Context) types.TotalFees {
}

// HasEnoughFundToCreatePacket checks if the fee payer has enough balance to create a packet
func (k Keeper) HasEnoughFundToCreatePacket(ctx sdk.Context, tunnelID uint64) (bool, error) {
tunnel, err := k.GetTunnel(ctx, tunnelID)
if err != nil {
return false, err
}

// get the route fee from the tunnel
route, err := tunnel.GetRouteValue()
if err != nil {
return false, err
}
func (k Keeper) HasEnoughFundToCreatePacket(
ctx sdk.Context,
route types.RouteI,
feePayer sdk.AccAddress,
) (bool, error) {
routeFee, err := k.GetRouteFee(ctx, route)
if err != nil {
return false, err
Expand All @@ -276,15 +282,29 @@ func (k Keeper) HasEnoughFundToCreatePacket(ctx sdk.Context, tunnelID uint64) (b
basePacketFee := k.GetParams(ctx).BasePacketFee
totalFee := basePacketFee.Add(routeFee...)

// compare the fee payer's balance with the total fee
feePayer, err := sdk.AccAddressFromBech32(tunnel.FeePayer)
if err != nil {
return false, err
}
balances := k.bankKeeper.SpendableCoins(ctx, feePayer)
return balances.IsAllGTE(totalFee), nil
}

// IsRouteReady checks if the given route is ready for receiving a new packet.
func (k Keeper) IsRouteReady(ctx sdk.Context, routeI types.RouteI, tunnelID uint64) bool {
switch route := routeI.(type) {
case *types.TSSRoute:
return k.bandtssKeeper.IsReady(ctx)
case *types.IBCRoute:
portID := PortIDForTunnel(tunnelID)

// retrieve the dynamic capability for this channel
_, found := k.scopedKeeper.GetCapability(
ctx,
host.ChannelCapabilityPath(portID, route.ChannelID),
)
return found
default:
return false
}
}

func (k Keeper) GenerateTunnelAccount(ctx sdk.Context, key string) (sdk.AccAddress, error) {
header := ctx.BlockHeader()

Expand Down
50 changes: 48 additions & 2 deletions x/tunnel/keeper/keeper_tunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import (

"go.uber.org/mock/gomock"

host "github.com/cosmos/ibc-go/v8/modules/core/24-host"

sdkmath "cosmossdk.io/math"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"

feedstypes "github.com/bandprotocol/chain/v3/x/feeds/types"
"github.com/bandprotocol/chain/v3/x/tunnel/keeper"
"github.com/bandprotocol/chain/v3/x/tunnel/types"
)

Expand Down Expand Up @@ -167,7 +170,9 @@ func (s *KeeperTestSuite) TestActivateTunnel() {
ctx, k := s.ctx, s.keeper

tunnelID := uint64(1)
route := &codectypes.Any{}
route, err := codectypes.NewAnyWithValue(&types.IBCRoute{ChannelID: "test"})
s.Require().NoError(err)

signalDeviations := []types.SignalDeviation{
{SignalID: "CS:BAND-USD"},
{SignalID: "CS:ETH-USD"},
Expand All @@ -188,7 +193,12 @@ func (s *KeeperTestSuite) TestActivateTunnel() {

k.SetTunnel(ctx, tunnel)

err := k.ActivateTunnel(ctx, tunnelID)
// mock the GetCapability function to return true
portID := keeper.PortIDForTunnel(tunnelID)
name := host.ChannelCapabilityPath(portID, "test")
s.scopedKeeper.EXPECT().GetCapability(gomock.Any(), name).Return(nil, true)

err = k.ActivateTunnel(ctx, tunnelID)
s.Require().NoError(err)

// validate the tunnel is activated
Expand All @@ -201,6 +211,42 @@ func (s *KeeperTestSuite) TestActivateTunnel() {
s.Require().Contains(activeTunnelIDs, tunnelID)
}

func (s *KeeperTestSuite) TestActivateTunnelInactiveRoute() {
ctx, k := s.ctx, s.keeper

tunnelID := uint64(1)
route, err := codectypes.NewAnyWithValue(&types.IBCRoute{ChannelID: "test"})
s.Require().NoError(err)

signalDeviations := []types.SignalDeviation{
{SignalID: "CS:BAND-USD"},
{SignalID: "CS:ETH-USD"},
}
interval := uint64(10)
creator := sdk.AccAddress([]byte("creator_address")).String()

tunnel := types.Tunnel{
ID: tunnelID,
Route: route,
SignalDeviations: signalDeviations,
Interval: interval,
TotalDeposit: k.GetParams(ctx).MinDeposit,
Creator: creator,
IsActive: false,
CreatedAt: ctx.BlockTime().Unix(),
}

k.SetTunnel(ctx, tunnel)

// mock the GetCapability function to return false
portID := keeper.PortIDForTunnel(tunnelID)
name := host.ChannelCapabilityPath(portID, "test")
s.scopedKeeper.EXPECT().GetCapability(gomock.Any(), name).Return(nil, false)

err = k.ActivateTunnel(ctx, tunnelID)
s.Require().ErrorIs(err, types.ErrRouteNotReady)
}

func (s *KeeperTestSuite) TestDeactivateTunnel() {
ctx, k := s.ctx, s.keeper

Expand Down
15 changes: 13 additions & 2 deletions x/tunnel/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (k msgServer) CreateTunnel(

// Bind ibc port for the new tunnel
if isIBCRoute {
_, err = k.ensureIBCPort(ctx, tunnel.ID)
_, err = k.Keeper.ensureIBCPort(ctx, tunnel.ID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -289,11 +289,22 @@ func (k msgServer) TriggerTunnel(
return nil, types.ErrInactiveTunnel.Wrapf("tunnelID %d", msg.TunnelID)
}

ok, err := k.Keeper.HasEnoughFundToCreatePacket(ctx, tunnel.ID)
route, err := tunnel.GetRouteValue()
if err != nil {
return nil, err
}

// Check if the route is ready for receiving a new packet.
if ok := k.IsRouteReady(ctx, route, tunnel.ID); !ok {
return nil, types.ErrRouteNotReady.Wrapf("tunnelID %d", msg.TunnelID)
}

// Check if the fee payer of the tunnel has enough fund to create a packet.
feePayer := sdk.MustAccAddressFromBech32(tunnel.FeePayer)
ok, err := k.Keeper.HasEnoughFundToCreatePacket(ctx, route, feePayer)
if err != nil {
return nil, err
}
if !ok {
return nil, types.ErrInsufficientFund.Wrapf("tunnelID %d", msg.TunnelID)
}
Expand Down
3 changes: 3 additions & 0 deletions x/tunnel/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,8 @@ func (s *KeeperTestSuite) TestMsgActivate() {

s.AddSampleTunnel(false)

s.bandtssKeeper.EXPECT().IsReady(gomock.Any()).Return(true)

return types.NewMsgActivate(1, sdk.AccAddress([]byte("creator_address")).String())
},
expErr: false,
Expand Down Expand Up @@ -692,6 +694,7 @@ func (s *KeeperTestSuite) TestMsgTriggerTunnel() {
feePayer := sdk.MustAccAddressFromBech32(tunnel.FeePayer)
s.Require().NoError(err)

s.bandtssKeeper.EXPECT().IsReady(gomock.Any()).Return(true)
s.bandtssKeeper.EXPECT().GetSigningFee(gomock.Any()).Return(
sdk.NewCoins(sdk.NewCoin("uband", sdkmath.NewInt(20))), nil,
).Times(2)
Expand Down
22 changes: 22 additions & 0 deletions x/tunnel/testutil/expected_keepers_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions x/tunnel/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ var (
ErrSendPacketPanic = errorsmod.Register(ModuleName, 25, "panic in sending packet")
ErrInvalidChannelID = errorsmod.Register(ModuleName, 26, "invalid channel id")
ErrInvalidPortID = errorsmod.Register(ModuleName, 27, "invalid port id")
ErrRouteNotReady = errorsmod.Register(ModuleName, 28, "route is not ready")
)
1 change: 1 addition & 0 deletions x/tunnel/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ type BandtssKeeper interface {
feeLimit sdk.Coins,
) (bandtsstypes.SigningID, error)
GetSigningFee(ctx sdk.Context) (sdk.Coins, error)
IsReady(ctx sdk.Context) bool
}

0 comments on commit e9fa0b8

Please sign in to comment.