diff --git a/.changelog/unreleased/features/2960-add-metaprotocols-support.md b/.changelog/unreleased/features/2960-add-metaprotocols-support.md new file mode 100644 index 00000000000..365d6b6589c --- /dev/null +++ b/.changelog/unreleased/features/2960-add-metaprotocols-support.md @@ -0,0 +1 @@ +Add support for metaprotocols using Tx extension options ([\#2960](https://github.com/cosmos/gaia/pull/2960)) \ No newline at end of file diff --git a/.changelog/unreleased/state-breaking/2960-add-metaprotocols-support.md b/.changelog/unreleased/state-breaking/2960-add-metaprotocols-support.md new file mode 100644 index 00000000000..365d6b6589c --- /dev/null +++ b/.changelog/unreleased/state-breaking/2960-add-metaprotocols-support.md @@ -0,0 +1 @@ +Add support for metaprotocols using Tx extension options ([\#2960](https://github.com/cosmos/gaia/pull/2960)) \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000000..4a11244b79b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules +build +.github +.vscode \ No newline at end of file diff --git a/app/modules.go b/app/modules.go index 661e4eae6a2..cecdf1a7505 100644 --- a/app/modules.go +++ b/app/modules.go @@ -57,6 +57,8 @@ import ( gaiaappparams "github.com/cosmos/gaia/v15/app/params" "github.com/cosmos/gaia/v15/x/globalfee" + "github.com/cosmos/gaia/v15/x/metaprotocols" + metaprotocolstypes "github.com/cosmos/gaia/v15/x/metaprotocols/types" ) var maccPerms = map[string][]string{ @@ -111,6 +113,7 @@ var ModuleBasics = module.NewBasicManager( globalfee.AppModule{}, icsprovider.AppModuleBasic{}, consensus.AppModuleBasic{}, + metaprotocols.AppModuleBasic{}, ) func appModules( @@ -149,6 +152,7 @@ func appModules( app.ICAModule, app.PFMRouterModule, app.ProviderModule, + metaprotocols.NewAppModule(), } } @@ -219,6 +223,7 @@ func orderBeginBlockers() []string { globalfee.ModuleName, providertypes.ModuleName, consensusparamtypes.ModuleName, + metaprotocolstypes.ModuleName, } } @@ -255,6 +260,7 @@ func orderEndBlockers() []string { globalfee.ModuleName, providertypes.ModuleName, consensusparamtypes.ModuleName, + metaprotocolstypes.ModuleName, } } @@ -299,5 +305,6 @@ func orderInitBlockers() []string { globalfee.ModuleName, providertypes.ModuleName, consensusparamtypes.ModuleName, + metaprotocolstypes.ModuleName, } } diff --git a/docs/docs/metaprotocols/README.md b/docs/docs/metaprotocols/README.md new file mode 100644 index 00000000000..1b4e241fe33 --- /dev/null +++ b/docs/docs/metaprotocols/README.md @@ -0,0 +1,71 @@ +--- +title: Metaprotocol Support +order: false +parent: + order: 2 +--- + +The `x/metaprotocol` module adds support for encoding and decoding additional fields attached to transactions. + +`extension_options` and `non_critical_extension_options` are optional fields that can be used to attach data to valid transactions. The fields are validated by the blockchain, but they are not used in any way. The fields pass validation if they are provided as empty lists (`[ ]`) or they use a list of `ExtensionData` types. + +The application does not use the attached data but it does ensure that the correct type is provided and that it can be successfully unmarshalled. The attached data will be part of a block. + +:::tip +Txs where `extension_options` or `non_critical_extension_options` are populated with a type other than `/gaia.metaprotocols.ExtensionData` are considered invalid and will be rejected. +::: + +Here is an example of a correctly formed `non_critical_extension_options` field: + +```json +{ + "@type": "/gaia.metaprotocols.ExtensionData", // must be this exact string + "protocol_id": "some-protocol", + "protocol_version": "1", + "data": "" +} +``` + +Here is an example of a correctly populated `non_critical_extension_options` on a `bank.MsgSend` transaction: + +```json +{ + "body": { + "messages": [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + "from_address": "cosmos1ehpqg9sj09037uhe56sqktk30asn47asthyr22", + "to_address": "cosmos1ehpqg9sj09037uhe56sqktk30asn47asthyr22", + "amount": [ + { + "denom": "uatom", + "amount": "100" + } + ] + } + ], + "memo": "memo_smaller_than_512_bytes", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [ + { + "@type": "/gaia.metaprotocols.ExtensionData", + "protocol_id": "some-protocol", + "protocol_version": "1", + "data": "" + } + ] + }, + "auth_info": { + "signer_infos": [], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [] +} +``` diff --git a/docs/docs/metaprotocols/_category_.json b/docs/docs/metaprotocols/_category_.json new file mode 100644 index 00000000000..1a5075eb193 --- /dev/null +++ b/docs/docs/metaprotocols/_category_.json @@ -0,0 +1,5 @@ +{ + "label": "Metaprotocol support", + "position": 15, + "link": { "type": "doc", "id": "metaprotocols/README" } +} \ No newline at end of file diff --git a/proto/gaia/metaprotocols/extensions.proto b/proto/gaia/metaprotocols/extensions.proto new file mode 100644 index 00000000000..868ee0b7a1a --- /dev/null +++ b/proto/gaia/metaprotocols/extensions.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package gaia.metaprotocols; + +option go_package = "github.com/cosmos/gaia/x/metaprotocols/types"; + +// ExtensionData is a data structure that can be used in transaction extensions. +message ExtensionData { + // protocol_id is the identifier of the protocol + // the field is not used internally but it is validated for correctness + string protocol_id = 1; + + // protocol_version is the identifier of the protocol version + // the field is not used internally but it is validated for correctness + string protocol_version = 2; + + // arbitrary bytes data that can be used to store any data + // the field is not used internally but it is validated and must be provided + bytes data = 3; +} diff --git a/tests/e2e/chain.go b/tests/e2e/chain.go index 774784694e6..8d897e9b111 100644 --- a/tests/e2e/chain.go +++ b/tests/e2e/chain.go @@ -14,6 +14,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" distribtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" govv1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1" @@ -23,6 +24,7 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" gaiaparams "github.com/cosmos/gaia/v15/app/params" + metaprotocoltypes "github.com/cosmos/gaia/v15/x/metaprotocols/types" ) const ( @@ -38,6 +40,7 @@ var ( func init() { encodingConfig = gaiaparams.MakeEncodingConfig() + banktypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) authtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) authvesting.RegisterInterfaces(encodingConfig.InterfaceRegistry) stakingtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) @@ -51,6 +54,7 @@ func init() { upgradetypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) distribtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) providertypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + metaprotocoltypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) cdc = encodingConfig.Marshaler txConfig = encodingConfig.TxConfig diff --git a/tests/e2e/e2e_bank_test.go b/tests/e2e/e2e_bank_test.go index 2abae83599d..c128ff5f0ea 100644 --- a/tests/e2e/e2e_bank_test.go +++ b/tests/e2e/e2e_bank_test.go @@ -2,13 +2,19 @@ package e2e import ( "fmt" + "path/filepath" "time" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + authTx "github.com/cosmos/cosmos-sdk/x/auth/tx" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + extensiontypes "github.com/cosmos/gaia/v15/x/metaprotocols/types" ) func (s *IntegrationTestSuite) testBankTokenTransfer() { - s.Run("send_photon_between_accounts", func() { + s.Run("send_tokens_between_accounts", func() { var ( err error valIdx = 0 @@ -96,3 +102,117 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() { ) }) } + +// tests the bank send command with populated non_critical_extension_options field +// the Tx should succeed if the data can be properly encoded and decoded +// the tx is signed and broadcast using gaiad tx sign and broadcast commands +func (s *IntegrationTestSuite) bankSendWithNonCriticalExtensionOptions() { + s.Run("transfer_with_non_critical_extension_options", func() { + c := s.chainA + + submitterAccount := c.genesisAccounts[1] + submitterAddress, err := submitterAccount.keyInfo.GetAddress() + s.Require().NoError(err) + sendMsg := banktypes.NewMsgSend(submitterAddress, submitterAddress, sdk.NewCoins(sdk.NewCoin(uatomDenom, sdk.NewInt(100)))) + + // valid non-critical extension options + ext := &extensiontypes.ExtensionData{ + ProtocolId: "test-protocol", + ProtocolVersion: "1", + Data: []byte("Hello Cosmos"), + } + + extAny, err := codectypes.NewAnyWithValue(ext) + s.Require().NoError(err) + s.Require().NotNil(extAny) + + txBuilder := encodingConfig.TxConfig.NewTxBuilder() + + s.Require().NoError(txBuilder.SetMsgs(sendMsg)) + + txBuilder.SetMemo("non-critical-ext-message-test") + txBuilder.SetFeeAmount(sdk.NewCoins(standardFees)) + txBuilder.SetGasLimit(200000) + + // add extension options + tx := txBuilder.GetTx() + if etx, ok := tx.(authTx.ExtensionOptionsTxBuilder); ok { + etx.SetNonCriticalExtensionOptions(extAny) + } + + bz, err := encodingConfig.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) + s.Require().NotNil(bz) + + txWithExt, err := decodeTx(bz) + s.Require().NoError(err) + s.Require().NotNil(txWithExt) + + rawTx, err := cdc.MarshalJSON(txWithExt) + s.Require().NoError(err) + s.Require().NotNil(rawTx) + + unsignedFname := "unsigned_non_critical_extension_option_tx.json" + unsignedJSONFile := filepath.Join(c.validators[0].configDir(), unsignedFname) + err = writeFile(unsignedJSONFile, rawTx) + s.Require().NoError(err) + + signedTx, err := s.signTxFileOnline(c, 0, submitterAddress.String(), unsignedFname) + s.Require().NoError(err) + s.Require().NotNil(signedTx) + + signedFname := "signed_non_critical_extension_option_tx.json" + signedJSONFile := filepath.Join(c.validators[0].configDir(), signedFname) + err = writeFile(signedJSONFile, signedTx) + s.Require().NoError(err) + + // if there's no errors the non_critical_extension_options field was properly encoded and decoded + out, err := s.broadcastTxFile(c, 0, submitterAddress.String(), signedFname) + s.Require().NoError(err) + s.Require().NotNil(out) + }) +} + +// tests the bank send command with invalid non_critical_extension_options field +// the tx should always fail to decode the extension options since no concrete type is registered for the provided extension field +func (s *IntegrationTestSuite) failedBankSendWithNonCriticalExtensionOptions() { + s.Run("fail_encoding_invalid_non_critical_extension_options", func() { + c := s.chainA + + submitterAccount := c.genesisAccounts[1] + submitterAddress, err := submitterAccount.keyInfo.GetAddress() + s.Require().NoError(err) + sendMsg := banktypes.NewMsgSend(submitterAddress, submitterAddress, sdk.NewCoins(sdk.NewCoin(uatomDenom, sdk.NewInt(100)))) + + // the message does not matter, as long as it is in the interface registry + ext := &banktypes.MsgMultiSend{} + + extAny, err := codectypes.NewAnyWithValue(ext) + s.Require().NoError(err) + s.Require().NotNil(extAny) + + txBuilder := encodingConfig.TxConfig.NewTxBuilder() + + s.Require().NoError(txBuilder.SetMsgs(sendMsg)) + + txBuilder.SetMemo("fail-non-critical-ext-message") + txBuilder.SetFeeAmount(sdk.NewCoins(standardFees)) + txBuilder.SetGasLimit(200000) + + // add extension options + tx := txBuilder.GetTx() + if etx, ok := tx.(authTx.ExtensionOptionsTxBuilder); ok { + etx.SetNonCriticalExtensionOptions(extAny) + } + + bz, err := encodingConfig.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) + s.Require().NotNil(bz) + + // decode fails because the provided extension option does not implement the correct TxExtensionOptionI interface + txWithExt, err := decodeTx(bz) + s.Require().Error(err) + s.Require().ErrorContains(err, "failed to decode tx: no concrete type registered for type URL /cosmos.bank.v1beta1.MsgMultiSend against interface *tx.TxExtensionOptionI") + s.Require().Nil(txWithExt) + }) +} diff --git a/tests/e2e/e2e_exec_test.go b/tests/e2e/e2e_exec_test.go index 70d772e636b..47df93077e7 100644 --- a/tests/e2e/e2e_exec_test.go +++ b/tests/e2e/e2e_exec_test.go @@ -6,6 +6,7 @@ import ( "context" "encoding/json" "fmt" + "path/filepath" "strconv" "strings" "time" @@ -908,3 +909,73 @@ func (s *IntegrationTestSuite) executeTransferTokenizeShareRecord(c *chain, valI s.executeGaiaTxCommand(ctx, c, gaiaCommand, valIdx, s.defaultExecValidation(c, valIdx)) s.T().Logf("%s successfully executed transfer tokenize share record for %s", owner, recordID) } + +// signTxFileOnline signs a transaction file using the gaiacli tx sign command +// the from flag is used to specify the keyring account to sign the transaction +// the from account must be registered in the keyring and exist on chain (have a balance or be a genesis account) +func (s *IntegrationTestSuite) signTxFileOnline(chain *chain, valIdx int, from string, txFilePath string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + gaiaCommand := []string{ + gaiadBinary, + txCommand, + "sign", + filepath.Join(gaiaHomePath, txFilePath), + fmt.Sprintf("--%s=%s", flags.FlagChainID, chain.id), + fmt.Sprintf("--%s=%s", flags.FlagHome, gaiaHomePath), + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + "--keyring-backend=test", + "--output=json", + "-y", + } + + var output []byte + var erroutput []byte + captureOutput := func(stdout []byte, stderr []byte) bool { + output = stdout + erroutput = stderr + return true + } + + s.executeGaiaTxCommand(ctx, chain, gaiaCommand, valIdx, captureOutput) + if len(erroutput) > 0 { + return nil, fmt.Errorf("failed to sign tx: %s", string(erroutput)) + } + return output, nil +} + +// broadcastTxFile broadcasts a signed transaction file using the gaiacli tx broadcast command +// the from flag is used to specify the keyring account to sign the transaction +// the from account must be registered in the keyring and exist on chain (have a balance or be a genesis account) +func (s *IntegrationTestSuite) broadcastTxFile(chain *chain, valIdx int, from string, txFilePath string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + broadcastTxCmd := []string{ + gaiadBinary, + txCommand, + "broadcast", + filepath.Join(gaiaHomePath, txFilePath), + fmt.Sprintf("--%s=%s", flags.FlagChainID, chain.id), + fmt.Sprintf("--%s=%s", flags.FlagHome, gaiaHomePath), + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + "--keyring-backend=test", + "--output=json", + "-y", + } + + var output []byte + var erroutput []byte + captureOutput := func(stdout []byte, stderr []byte) bool { + output = stdout + erroutput = stderr + return true + } + + s.executeGaiaTxCommand(ctx, chain, broadcastTxCmd, valIdx, captureOutput) + if len(erroutput) > 0 { + return nil, fmt.Errorf("failed to sign tx: %s", string(erroutput)) + } + return output, nil +} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index ea25c73a9cf..2c09d61c6c5 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -30,6 +30,8 @@ func (s *IntegrationTestSuite) TestBank() { s.T().Skip() } s.testBankTokenTransfer() + s.bankSendWithNonCriticalExtensionOptions() + s.failedBankSendWithNonCriticalExtensionOptions() } func (s *IntegrationTestSuite) TestByPassMinFee() { diff --git a/x/metaprotocols/README.md b/x/metaprotocols/README.md new file mode 100644 index 00000000000..e91c2b6127f --- /dev/null +++ b/x/metaprotocols/README.md @@ -0,0 +1,62 @@ +# x/metaprotocols module + +The `x/metaprotocols` module adds support for encoding and decoding additional fields attached to transactions. + +`extension_options` and `non_critical_extension_options` are optional fields that can be used to attach data to valid transactions. The fields are validated by the blockchain, but they are not used in any way. The fields pass validation if they are provided as empty lists (`[ ]`) or they use a list of `ExtensionData` types. + +The application does not use the attached data but it does ensure that the correct type is provided and that it can be successfully unmarshalled. The attached data will be part of a block. + +Here is an example of a correctly formed `non_critical_extension_options` field: + +```json +{ + "@type": "/gaia.metaprotocols.ExtensionData", // must be this exact string + "protocol_id": "some-protocol", + "protocol_version": "1", + "data": "" +} +``` + +Here is an example of a correctly populated `non_critical_extension_options` on a `bank.MsgSend` transaction: + +```json +{ + "body": { + "messages": [ + { + "@type": "/cosmos.bank.v1beta1.MsgSend", + "from_address": "cosmos1ehpqg9sj09037uhe56sqktk30asn47asthyr22", + "to_address": "cosmos1ehpqg9sj09037uhe56sqktk30asn47asthyr22", + "amount": [ + { + "denom": "uatom", + "amount": "100" + } + ] + } + ], + "memo": "memo_smaller_than_512_bytes", + "timeout_height": "0", + "extension_options": [], + "non_critical_extension_options": [ + { + "@type": "/gaia.metaprotocols.ExtensionData", + "protocol_id": "some-protocol", + "protocol_version": "1", + "data": "" + } + ] + }, + "auth_info": { + "signer_infos": [], + "fee": { + "amount": [], + "gas_limit": "200000", + "payer": "", + "granter": "" + }, + "tip": null + }, + "signatures": [] +} +``` diff --git a/x/metaprotocols/module.go b/x/metaprotocols/module.go new file mode 100644 index 00000000000..62cdbd238f9 --- /dev/null +++ b/x/metaprotocols/module.go @@ -0,0 +1,94 @@ +package metaprotocols + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/cosmos/gaia/v15/x/metaprotocols/types" +) + +const consensusVersion uint64 = 1 + +var ( + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleGenesis = AppModule{} + _ module.AppModule = AppModule{} +) + +type AppModuleBasic struct{} + +func (a AppModuleBasic) Name() string { + return types.ModuleName +} + +// DefaultGenesis is an empty object +func (AppModuleBasic) DefaultGenesis(_ codec.JSONCodec) json.RawMessage { + return []byte("{}") +} + +func (AppModuleBasic) ValidateGenesis(_ codec.JSONCodec, config client.TxEncodingConfig, _ json.RawMessage) error { + return nil +} + +func (a AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONCodec) json.RawMessage { + return a.DefaultGenesis(cdc) +} + +func (a AppModule) InitGenesis(ctx sdk.Context, marshaler codec.JSONCodec, message json.RawMessage) []abci.ValidatorUpdate { + return nil +} + +func (AppModule) ConsensusVersion() uint64 { return consensusVersion } + +func (a AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +func (a AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) { +} + +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { +} + +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return nil +} + +func (a AppModuleBasic) GetQueryCmd() *cobra.Command { + return nil +} + +func (a AppModuleBasic) RegisterLegacyAminoCodec(_ *codec.LegacyAmino) { +} + +type AppModule struct { + AppModuleBasic +} + +func NewAppModule() *AppModule { + return &AppModule{} +} + +func (a AppModule) RegisterInvariants(_ sdk.InvariantRegistry) { +} + +func (a AppModule) RegisterServices(_ module.Configurator) { +} + +func (a AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) { +} + +func (a AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return nil +} diff --git a/x/metaprotocols/types/codec.go b/x/metaprotocols/types/codec.go new file mode 100644 index 00000000000..9cf58026bd6 --- /dev/null +++ b/x/metaprotocols/types/codec.go @@ -0,0 +1,15 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec/types" + tx "github.com/cosmos/cosmos-sdk/types/tx" +) + +// RegisterInterfaces adds the x/metaprotocols module's interfaces to the provided InterfaceRegistry +// The ExtendedData interface is registered so that the TxExtensionOptionsI can be properly encoded and decoded +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations( + (*tx.TxExtensionOptionI)(nil), + &ExtensionData{}, + ) +} diff --git a/x/metaprotocols/types/extensions.pb.go b/x/metaprotocols/types/extensions.pb.go new file mode 100644 index 00000000000..74b5a83d60b --- /dev/null +++ b/x/metaprotocols/types/extensions.pb.go @@ -0,0 +1,430 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: gaia/metaprotocols/extensions.proto + +package types + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ExtensionData is a data structure that can be used in transaction extensions. +type ExtensionData struct { + // protocol_id is the identifier of the protocol + // the field is not used internally but it is validated for correctness + ProtocolId string `protobuf:"bytes,1,opt,name=protocol_id,json=protocolId,proto3" json:"protocol_id,omitempty"` + // protocol_version is the identifier of the protocol version + // the field is not used internally but it is validated for correctness + ProtocolVersion string `protobuf:"bytes,2,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"` + // arbitrary bytes data that can be used to store any data + // the field is not used internally but it is validated and must be provided + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *ExtensionData) Reset() { *m = ExtensionData{} } +func (m *ExtensionData) String() string { return proto.CompactTextString(m) } +func (*ExtensionData) ProtoMessage() {} +func (*ExtensionData) Descriptor() ([]byte, []int) { + return fileDescriptor_2f741ff82d908382, []int{0} +} +func (m *ExtensionData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExtensionData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExtensionData.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExtensionData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExtensionData.Merge(m, src) +} +func (m *ExtensionData) XXX_Size() int { + return m.Size() +} +func (m *ExtensionData) XXX_DiscardUnknown() { + xxx_messageInfo_ExtensionData.DiscardUnknown(m) +} + +var xxx_messageInfo_ExtensionData proto.InternalMessageInfo + +func (m *ExtensionData) GetProtocolId() string { + if m != nil { + return m.ProtocolId + } + return "" +} + +func (m *ExtensionData) GetProtocolVersion() string { + if m != nil { + return m.ProtocolVersion + } + return "" +} + +func (m *ExtensionData) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func init() { + proto.RegisterType((*ExtensionData)(nil), "gaia.metaprotocols.ExtensionData") +} + +func init() { + proto.RegisterFile("gaia/metaprotocols/extensions.proto", fileDescriptor_2f741ff82d908382) +} + +var fileDescriptor_2f741ff82d908382 = []byte{ + // 197 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4e, 0x4f, 0xcc, 0x4c, + 0xd4, 0xcf, 0x4d, 0x2d, 0x49, 0x2c, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0x29, 0xd6, 0x4f, + 0xad, 0x28, 0x49, 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0x2b, 0xd6, 0x03, 0x0b, 0x0a, 0x09, 0x81, 0x14, + 0xe9, 0xa1, 0x28, 0x52, 0xca, 0xe7, 0xe2, 0x75, 0x85, 0xa9, 0x73, 0x49, 0x2c, 0x49, 0x14, 0x92, + 0xe7, 0xe2, 0x86, 0xc9, 0xc6, 0x67, 0xa6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x71, 0xc1, + 0x84, 0x3c, 0x53, 0x84, 0x34, 0xb9, 0x04, 0xe0, 0x0a, 0xca, 0x52, 0x8b, 0x40, 0x1a, 0x25, 0x98, + 0xc0, 0xaa, 0xf8, 0x61, 0xe2, 0x61, 0x10, 0x61, 0x21, 0x21, 0x2e, 0x96, 0x94, 0xc4, 0x92, 0x44, + 0x09, 0x66, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x30, 0xdb, 0xc9, 0xed, 0xc4, 0x23, 0x39, 0xc6, 0x0b, + 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, + 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x74, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, + 0xf5, 0x93, 0xf3, 0x8b, 0x73, 0xf3, 0x8b, 0xf5, 0xc1, 0xbe, 0xaa, 0x40, 0xf3, 0x57, 0x49, 0x65, + 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x58, 0xc0, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x06, 0x68, + 0x90, 0xfa, 0x00, 0x00, 0x00, +} + +func (m *ExtensionData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExtensionData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExtensionData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintExtensions(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if len(m.ProtocolVersion) > 0 { + i -= len(m.ProtocolVersion) + copy(dAtA[i:], m.ProtocolVersion) + i = encodeVarintExtensions(dAtA, i, uint64(len(m.ProtocolVersion))) + i-- + dAtA[i] = 0x12 + } + if len(m.ProtocolId) > 0 { + i -= len(m.ProtocolId) + copy(dAtA[i:], m.ProtocolId) + i = encodeVarintExtensions(dAtA, i, uint64(len(m.ProtocolId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintExtensions(dAtA []byte, offset int, v uint64) int { + offset -= sovExtensions(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ExtensionData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ProtocolId) + if l > 0 { + n += 1 + l + sovExtensions(uint64(l)) + } + l = len(m.ProtocolVersion) + if l > 0 { + n += 1 + l + sovExtensions(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovExtensions(uint64(l)) + } + return n +} + +func sovExtensions(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozExtensions(x uint64) (n int) { + return sovExtensions(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ExtensionData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExtensions + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExtensionData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExtensionData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProtocolId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExtensions + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthExtensions + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthExtensions + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProtocolId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProtocolVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExtensions + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthExtensions + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthExtensions + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProtocolVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExtensions + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthExtensions + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthExtensions + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipExtensions(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthExtensions + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipExtensions(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowExtensions + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowExtensions + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowExtensions + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthExtensions + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupExtensions + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthExtensions + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthExtensions = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowExtensions = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupExtensions = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/metaprotocols/types/keys.go b/x/metaprotocols/types/keys.go new file mode 100644 index 00000000000..52df9320cce --- /dev/null +++ b/x/metaprotocols/types/keys.go @@ -0,0 +1,5 @@ +package types + +const ( + ModuleName = "metaprotocols" +)