diff --git a/CHANGELOG.md b/CHANGELOG.md index 0489f3c..f561b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ "Bug Fixes" for any bug fixes. "Client Breaking" for breaking CLI commands and REST routes used by end-users. "API Breaking" for breaking exported APIs used by developers building on SDK. -"State Machine Breaking" for any changes that result in a different AppState given same genesisState and txList. +"State Machine Breaking" for any changes that result in a different AppState given the same genesisState and txList. --> @@ -18,6 +18,7 @@ An '!' indicates a state machine breaking change. ### Features +- ! [#154](https://github.com/bcp-innovations/hyperlane-cosmos/pull/154) Charge gas for signature verification. - ! [#135](https://github.com/bcp-innovations/hyperlane-cosmos/pull/135) Allow authority to create native synthetic tokens. ### Bug Fixes diff --git a/x/core/01_interchain_security/types/merkle_root_multisig.go b/x/core/01_interchain_security/types/merkle_root_multisig.go index 4cbcd9c..f9cf5e9 100644 --- a/x/core/01_interchain_security/types/merkle_root_multisig.go +++ b/x/core/01_interchain_security/types/merkle_root_multisig.go @@ -6,7 +6,9 @@ import ( "fmt" "slices" + storetypes "cosmossdk.io/store/types" "github.com/bcp-innovations/hyperlane-cosmos/util" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/crypto" ) @@ -20,7 +22,7 @@ func (m *MerkleRootMultisigISM) ModuleType() uint8 { return INTERCHAIN_SECURITY_MODULE_TYPE_MERKLE_ROOT_MULTISIG } -func (m *MerkleRootMultisigISM) Verify(_ context.Context, rawMetadata []byte, message util.HyperlaneMessage) (bool, error) { +func (m *MerkleRootMultisigISM) Verify(ctx context.Context, rawMetadata []byte, message util.HyperlaneMessage) (bool, error) { metadata, err := NewMerkleRootMultisigMetadata(rawMetadata) if err != nil { return false, err @@ -32,6 +34,9 @@ func (m *MerkleRootMultisigISM) Verify(_ context.Context, rawMetadata []byte, me digest := metadata.Digest(&message) + // Charge Cosmos gas for each signature verification. + sdk.UnwrapSDKContext(ctx).GasMeter().ConsumeGas(storetypes.Gas(1000*len(metadata.Signatures)), "ism signature verification") + return VerifyMultisig(m.Validators, m.Threshold, metadata.Signatures, digest) } diff --git a/x/core/01_interchain_security/types/merkle_root_multisig_test.go b/x/core/01_interchain_security/types/merkle_root_multisig_test.go index 2cccd8d..b6897f0 100644 --- a/x/core/01_interchain_security/types/merkle_root_multisig_test.go +++ b/x/core/01_interchain_security/types/merkle_root_multisig_test.go @@ -3,9 +3,9 @@ package types_test import ( "fmt" + i "github.com/bcp-innovations/hyperlane-cosmos/tests/integration" "github.com/bcp-innovations/hyperlane-cosmos/util" "github.com/bcp-innovations/hyperlane-cosmos/x/core/01_interchain_security/types" - sdk "github.com/cosmos/cosmos-sdk/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -33,6 +33,12 @@ TEST CASES - merkle_root_multisig.go */ var _ = Describe("merkle_root_multisig.go", Ordered, func() { + var s *i.KeeperTestSuite + + BeforeEach(func() { + s = i.NewCleanChain() + }) + It("Validate (invalid) invalid validators", func() { // Arrange validators := []string{ @@ -158,7 +164,7 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata := bytesFromHexString("") // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata, util.HyperlaneMessage{}) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata, util.HyperlaneMessage{}) // Assert Expect(err.Error()).To(Equal("invalid metadata length: got 0, expected at least 1096 bytes")) @@ -202,7 +208,7 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err.Error()).To(Equal("invalid signatures length in metadata")) @@ -245,7 +251,7 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err.Error()).To(Equal("invalid signed index")) @@ -288,7 +294,7 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err.Error()).To(Equal("threshold can not be reached")) @@ -329,7 +335,7 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err.Error()).To(Equal("failed to recover validator signature: invalid signature recovery id")) @@ -372,7 +378,7 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err).To(BeNil()) @@ -422,7 +428,7 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata.Signatures = duplicatedSignatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err).To(BeNil()) @@ -473,11 +479,12 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err).To(BeNil()) Expect(verify).To(BeTrue()) + Expect(int(s.Ctx().GasMeter().GasConsumed())).To(Equal(3000)) }) // Verifies metadata and messages submitted by a Hyperlane Relayer. @@ -497,9 +504,10 @@ var _ = Describe("merkle_root_multisig.go", Ordered, func() { } // Act - verify, err := merkleRootMultiSig.Verify(sdk.Context{}, validMetadata, validMessage) + verify, err := merkleRootMultiSig.Verify(s.Ctx(), validMetadata, validMessage) // Assert + Expect(int(s.Ctx().GasMeter().GasConsumed())).To(Equal(1000)) Expect(err).To(BeNil()) Expect(verify).To(BeTrue()) }) diff --git a/x/core/01_interchain_security/types/message_id_multisig.go b/x/core/01_interchain_security/types/message_id_multisig.go index 89d8607..5e32d07 100644 --- a/x/core/01_interchain_security/types/message_id_multisig.go +++ b/x/core/01_interchain_security/types/message_id_multisig.go @@ -6,7 +6,9 @@ import ( "fmt" "slices" + storetypes "cosmossdk.io/store/types" "github.com/bcp-innovations/hyperlane-cosmos/util" + sdk "github.com/cosmos/cosmos-sdk/types" ) var _ HyperlaneInterchainSecurityModule = &MessageIdMultisigISM{} @@ -19,7 +21,7 @@ func (m *MessageIdMultisigISM) ModuleType() uint8 { return INTERCHAIN_SECURITY_MODULE_TYPE_MESSAGE_ID_MULTISIG } -func (m *MessageIdMultisigISM) Verify(_ context.Context, rawMetadata []byte, message util.HyperlaneMessage) (bool, error) { +func (m *MessageIdMultisigISM) Verify(ctx context.Context, rawMetadata []byte, message util.HyperlaneMessage) (bool, error) { metadata, err := NewMessageIdMultisigMetadata(rawMetadata) if err != nil { return false, err @@ -27,6 +29,9 @@ func (m *MessageIdMultisigISM) Verify(_ context.Context, rawMetadata []byte, mes digest := metadata.Digest(&message) + // Charge Cosmos gas for each signature verification. + sdk.UnwrapSDKContext(ctx).GasMeter().ConsumeGas(storetypes.Gas(1000*len(metadata.Signatures)), "ism signature verification") + return VerifyMultisig(m.Validators, m.Threshold, metadata.Signatures, digest) } diff --git a/x/core/01_interchain_security/types/message_id_multisig_test.go b/x/core/01_interchain_security/types/message_id_multisig_test.go index d1f33c2..ce8c2a6 100644 --- a/x/core/01_interchain_security/types/message_id_multisig_test.go +++ b/x/core/01_interchain_security/types/message_id_multisig_test.go @@ -3,9 +3,9 @@ package types_test import ( "fmt" + i "github.com/bcp-innovations/hyperlane-cosmos/tests/integration" "github.com/bcp-innovations/hyperlane-cosmos/util" "github.com/bcp-innovations/hyperlane-cosmos/x/core/01_interchain_security/types" - sdk "github.com/cosmos/cosmos-sdk/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -31,6 +31,12 @@ TEST CASES - message_id_multisig.go */ var _ = Describe("message_id_multisig.go", Ordered, func() { + var s *i.KeeperTestSuite + + BeforeEach(func() { + s = i.NewCleanChain() + }) + It("Validate (invalid) invalid validators", func() { // Arrange validators := []string{ @@ -155,7 +161,7 @@ var _ = Describe("message_id_multisig.go", Ordered, func() { metadata := bytesFromHexString("") // Act - verify, err := messageIdMultisigIsm.Verify(sdk.Context{}, metadata, util.HyperlaneMessage{}) + verify, err := messageIdMultisigIsm.Verify(s.Ctx(), metadata, util.HyperlaneMessage{}) // Assert Expect(err.Error()).To(Equal("invalid metadata length: got 0, expected at least 68 bytes")) @@ -197,7 +203,7 @@ var _ = Describe("message_id_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := merkleRootMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := merkleRootMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err.Error()).To(Equal("invalid signatures length in metadata")) @@ -237,7 +243,7 @@ var _ = Describe("message_id_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := messageIdMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := messageIdMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err.Error()).To(Equal("threshold can not be reached")) @@ -275,7 +281,7 @@ var _ = Describe("message_id_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := messageIdMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := messageIdMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err.Error()).To(Equal("failed to recover validator signature: invalid signature recovery id")) @@ -315,7 +321,7 @@ var _ = Describe("message_id_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := messageIdMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := messageIdMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err).To(BeNil()) @@ -362,7 +368,7 @@ var _ = Describe("message_id_multisig.go", Ordered, func() { metadata.Signatures = duplicatedSignatures // Act - verify, err := messageIdMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := messageIdMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert Expect(err).To(BeNil()) @@ -409,9 +415,10 @@ var _ = Describe("message_id_multisig.go", Ordered, func() { metadata.Signatures = signatures // Act - verify, err := messageIdMultisigIsm.Verify(sdk.Context{}, metadata.Bytes(), message) + verify, err := messageIdMultisigIsm.Verify(s.Ctx(), metadata.Bytes(), message) // Assert + Expect(int(s.Ctx().GasMeter().GasConsumed())).To(Equal(3000)) Expect(err).To(BeNil()) Expect(verify).To(BeTrue()) })