Skip to content

Commit

Permalink
feat!: support for metaprotocol types (#2960)
Browse files Browse the repository at this point in the history
* experimental: add message types for metaprotocols

* experimental: refine multiprotocols support

* Update x/metaprotocols/types/keys.go

Co-authored-by: Simon Noetzlin <[email protected]>

* appease linter

* add docs and rebuild protos

* add docs, tests, readme

* update docs

* update e2e - enable all

* appease linter

* Update tests/e2e/e2e_bank_test.go

Co-authored-by: Philip Offtermatt <[email protected]>

* Update x/metaprotocols/README.md

Co-authored-by: Simon Noetzlin <[email protected]>

* docs: update changelog files

---------

Co-authored-by: Simon Noetzlin <[email protected]>
Co-authored-by: Philip Offtermatt <[email protected]>
  • Loading branch information
3 people authored Feb 27, 2024
1 parent 53f695a commit 3ae75ed
Show file tree
Hide file tree
Showing 16 changed files with 912 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for metaprotocols using Tx extension options ([\#2960](https://github.com/cosmos/gaia/pull/2960))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for metaprotocols using Tx extension options ([\#2960](https://github.com/cosmos/gaia/pull/2960))
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
build
.github
.vscode
7 changes: 7 additions & 0 deletions app/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -111,6 +113,7 @@ var ModuleBasics = module.NewBasicManager(
globalfee.AppModule{},
icsprovider.AppModuleBasic{},
consensus.AppModuleBasic{},
metaprotocols.AppModuleBasic{},
)

func appModules(
Expand Down Expand Up @@ -149,6 +152,7 @@ func appModules(
app.ICAModule,
app.PFMRouterModule,
app.ProviderModule,
metaprotocols.NewAppModule(),
}
}

Expand Down Expand Up @@ -219,6 +223,7 @@ func orderBeginBlockers() []string {
globalfee.ModuleName,
providertypes.ModuleName,
consensusparamtypes.ModuleName,
metaprotocolstypes.ModuleName,
}
}

Expand Down Expand Up @@ -255,6 +260,7 @@ func orderEndBlockers() []string {
globalfee.ModuleName,
providertypes.ModuleName,
consensusparamtypes.ModuleName,
metaprotocolstypes.ModuleName,
}
}

Expand Down Expand Up @@ -299,5 +305,6 @@ func orderInitBlockers() []string {
globalfee.ModuleName,
providertypes.ModuleName,
consensusparamtypes.ModuleName,
metaprotocolstypes.ModuleName,
}
}
71 changes: 71 additions & 0 deletions docs/docs/metaprotocols/README.md
Original file line number Diff line number Diff line change
@@ -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": "<base64 encoded bytes>"
}
```

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": "<base64 encoded bytes>"
}
]
},
"auth_info": {
"signer_infos": [],
"fee": {
"amount": [],
"gas_limit": "200000",
"payer": "",
"granter": ""
},
"tip": null
},
"signatures": []
}
```
5 changes: 5 additions & 0 deletions docs/docs/metaprotocols/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "Metaprotocol support",
"position": 15,
"link": { "type": "doc", "id": "metaprotocols/README" }
}
19 changes: 19 additions & 0 deletions proto/gaia/metaprotocols/extensions.proto
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 4 additions & 0 deletions tests/e2e/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 (
Expand All @@ -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)
Expand All @@ -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
Expand Down
122 changes: 121 additions & 1 deletion tests/e2e/e2e_bank_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
})
}
Loading

0 comments on commit 3ae75ed

Please sign in to comment.