-
Notifications
You must be signed in to change notification settings - Fork 586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: ica app version negotiation #410
Changes from 5 commits
18990b4
49e2901
1f73b16
b967f08
52fe314
bd66f33
62e5c39
4ee74bf
b2f3e3b
c7d7804
001313c
d6725e1
32240ff
e4af997
18e9601
c691632
3d5baab
f400bdc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
package keeper | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" | ||
|
@@ -36,8 +39,9 @@ func (k Keeper) OnChanOpenInit( | |
if counterparty.PortId != types.PortID { | ||
return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "counterparty port-id must be '%s', (%s != %s)", types.PortID, counterparty.PortId, types.PortID) | ||
} | ||
if version != types.Version { | ||
return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelVersion, "channel version must be '%s' (%s != %s)", types.Version, version, types.Version) | ||
|
||
if !strings.Contains(version, types.Version) { | ||
return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid version: %s, expected %s", version, types.Version) | ||
} | ||
|
||
existingChannelID, found := k.GetActiveChannel(ctx, portID) | ||
|
@@ -71,10 +75,12 @@ func (k Keeper) OnChanOpenTry( | |
if order != channeltypes.ORDERED { | ||
return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "invalid channel ordering: %s, expected %s", order.String(), channeltypes.ORDERED.String()) | ||
} | ||
if version != types.Version { | ||
return sdkerrors.Wrapf(types.ErrInvalidVersion, "got: %s, expected %s", version, types.Version) | ||
|
||
if !strings.Contains(version, types.Version) { | ||
return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid version: %s, expected %s", version, types.Version) | ||
} | ||
if counterpartyVersion != types.Version { | ||
|
||
if !strings.Contains(counterpartyVersion, types.Version) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the inclusion of the account address in the version string we now have to deviate from the spec as we cannot explicitly check for equality. I've updated these checks to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@colin-axner would like to hear your thoughts There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this would be a nicer option. Just basic assertion of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I'd definitely like stronger validation. The two areas we do validation now is in 24-host and identifier parsing I think the address validation should be light weight. I'm not sure it needs to be a valid bech 32 address since this can be address on a different chain. Maybe just a string which doesn't contain a I'm also okay doing some arbitrary address length validation and increasing the maximum allowed length if someone runs into an issue Adding a validation function also increases the readability of the code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated! See here |
||
return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) | ||
} | ||
|
||
|
@@ -85,22 +91,33 @@ func (k Keeper) OnChanOpenTry( | |
} | ||
|
||
// Register interchain account if it does not already exist | ||
k.RegisterInterchainAccount(ctx, counterparty.PortId) | ||
accAddr := strings.TrimPrefix(version, fmt.Sprintf("%s-", types.Version)) | ||
if err := k.RegisterInterchainAccount(ctx, accAddr, counterparty.PortId); err != nil { | ||
return err | ||
} | ||
|
||
colin-axner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil | ||
} | ||
|
||
// OnChanOpenAck sets the active channel for the interchain account/owner pair | ||
// and stores the associated interchain account address in state keyed by it's corresponding port identifier | ||
// | ||
// Controller Chain | ||
func (k Keeper) OnChanOpenAck( | ||
ctx sdk.Context, | ||
portID, | ||
channelID string, | ||
counterpartyVersion string, | ||
) error { | ||
if counterpartyVersion != types.Version { | ||
if !strings.Contains(counterpartyVersion, types.Version) { | ||
return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) | ||
} | ||
|
||
k.SetActiveChannel(ctx, portID, channelID) | ||
|
||
accAddr := strings.TrimPrefix(counterpartyVersion, fmt.Sprintf("%s-", types.Version)) | ||
damiannolan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
k.SetInterchainAccountAddress(ctx, portID, accAddr) | ||
|
||
return nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package interchain_accounts_test | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/suite" | ||
|
||
"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types" | ||
channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types" | ||
ibctesting "github.com/cosmos/ibc-go/testing" | ||
) | ||
|
||
type InterchainAccountsTestSuite struct { | ||
suite.Suite | ||
|
||
coordinator *ibctesting.Coordinator | ||
|
||
// testing chains used for convenience and readability | ||
chainA *ibctesting.TestChain | ||
chainB *ibctesting.TestChain | ||
chainC *ibctesting.TestChain | ||
} | ||
|
||
func (suite *InterchainAccountsTestSuite) SetupTest() { | ||
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) | ||
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) | ||
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) | ||
suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(2)) | ||
} | ||
|
||
func NewICAPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { | ||
path := ibctesting.NewPath(chainA, chainB) | ||
path.EndpointA.ChannelConfig.PortID = types.PortID | ||
path.EndpointB.ChannelConfig.PortID = types.PortID | ||
path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED | ||
path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED | ||
path.EndpointA.ChannelConfig.Version = types.Version | ||
path.EndpointB.ChannelConfig.Version = fmt.Sprintf("%s-%s", types.Version, types.GenerateAddress("ics-27-0-0-testing")) | ||
|
||
return path | ||
} | ||
|
||
func TestICATestSuite(t *testing.T) { | ||
suite.Run(t, new(InterchainAccountsTestSuite)) | ||
} | ||
|
||
func (suite *InterchainAccountsTestSuite) TestNegotiateAppVersion() { | ||
suite.SetupTest() | ||
path := NewICAPath(suite.chainA, suite.chainB) | ||
suite.coordinator.SetupConnections(path) | ||
|
||
module, _, err := suite.chainA.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), types.PortID) | ||
suite.Require().NoError(err) | ||
|
||
cbs, ok := suite.chainA.GetSimApp().GetIBCKeeper().Router.GetRoute(module) | ||
suite.Require().True(ok) | ||
|
||
counterpartyPortID, err := types.GeneratePortID("testing", path.EndpointA.ConnectionID, path.EndpointB.ConnectionID) | ||
suite.Require().NoError(err) | ||
|
||
counterparty := &channeltypes.Counterparty{ | ||
PortId: counterpartyPortID, | ||
ChannelId: path.EndpointB.ChannelID, | ||
} | ||
|
||
version, err := cbs.NegotiateAppVersion(suite.chainA.GetContext(), channeltypes.ORDERED, path.EndpointA.ConnectionID, types.PortID, *counterparty, types.Version) | ||
damiannolan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
suite.Require().NoError(err) | ||
suite.Require().True(strings.Contains(version, types.Version)) | ||
suite.Require().True(strings.Contains(version, types.GenerateAddress(counterpartyPortID).String())) | ||
damiannolan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,9 +9,9 @@ import ( | |
|
||
crypto "github.com/cosmos/cosmos-sdk/crypto/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/address" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" | ||
"github.com/tendermint/tendermint/crypto/tmhash" | ||
|
||
connectiontypes "github.com/cosmos/ibc-go/modules/core/03-connection/types" | ||
) | ||
|
@@ -20,9 +20,10 @@ const ( | |
ICAPrefix string = "ics-27" | ||
colin-axner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
|
||
// GenerateAddress returns a truncated SHA256 hash using the provided port string | ||
func GenerateAddress(port string) []byte { | ||
return tmhash.SumTruncated([]byte(port)) | ||
// GenerateAddress returns an sdk.AccAddress using the provided port identifier and derived from the IBC interchainaccounts module account | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @colin-axner I've included this as per #303 after checking out the comment you had linked! I'm assuming this is a new approach which has yet to be adopted across the sdk. This seems to me like the correct usage but I'm not 100% as other modules use a different pattern. For example There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also updated this return sig from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! Deriving the address looks correct, but we probably need to create the module account in the ICA keeper (in NewKeeper function) and create the sub module account when the address is being generated. I may be mistaken, but typically you have to create a new account in the account keeper since it needs to track account information like sequence It might be worth doing this in a separate pr. Trying to use this account should test whether everything was setup correctly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure I can do this in a separate PR 👍 In the
From what I can tell the module accounts are being created here |
||
func GenerateAddress(portID string) sdk.AccAddress { | ||
damiannolan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
moduleAcc := address.Module(ModuleName, ModuleAccountKey) | ||
return sdk.AccAddress(address.Derive(moduleAcc, []byte(portID))) | ||
} | ||
|
||
// GeneratePortID generates the portID for a specific owner | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably return a more descriptive error here. wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, wondering if this check to get an account needs to change at all given we are using the new module acc feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the point is to do a no-op if the account already exists since it could create an attack vector if an error is returned. Or at least that's what comes to mind. It'd be great to have an inline comment explaining the reasoning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea here that there may be an account that already exists and this would return nil (no-op) in the event that a new channel was being established between chains that had already registered an interchain account?