Skip to content

Commit 3ae75ed

Browse files
MSalopeksainoep-offtermatt
authored
feat!: support for metaprotocol types (#2960)
* 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]>
1 parent 53f695a commit 3ae75ed

File tree

16 files changed

+912
-1
lines changed

16 files changed

+912
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for metaprotocols using Tx extension options ([\#2960](https://github.com/cosmos/gaia/pull/2960))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for metaprotocols using Tx extension options ([\#2960](https://github.com/cosmos/gaia/pull/2960))

.dockerignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
build
3+
.github
4+
.vscode

app/modules.go

+7
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ import (
5757

5858
gaiaappparams "github.com/cosmos/gaia/v15/app/params"
5959
"github.com/cosmos/gaia/v15/x/globalfee"
60+
"github.com/cosmos/gaia/v15/x/metaprotocols"
61+
metaprotocolstypes "github.com/cosmos/gaia/v15/x/metaprotocols/types"
6062
)
6163

6264
var maccPerms = map[string][]string{
@@ -111,6 +113,7 @@ var ModuleBasics = module.NewBasicManager(
111113
globalfee.AppModule{},
112114
icsprovider.AppModuleBasic{},
113115
consensus.AppModuleBasic{},
116+
metaprotocols.AppModuleBasic{},
114117
)
115118

116119
func appModules(
@@ -149,6 +152,7 @@ func appModules(
149152
app.ICAModule,
150153
app.PFMRouterModule,
151154
app.ProviderModule,
155+
metaprotocols.NewAppModule(),
152156
}
153157
}
154158

@@ -219,6 +223,7 @@ func orderBeginBlockers() []string {
219223
globalfee.ModuleName,
220224
providertypes.ModuleName,
221225
consensusparamtypes.ModuleName,
226+
metaprotocolstypes.ModuleName,
222227
}
223228
}
224229

@@ -255,6 +260,7 @@ func orderEndBlockers() []string {
255260
globalfee.ModuleName,
256261
providertypes.ModuleName,
257262
consensusparamtypes.ModuleName,
263+
metaprotocolstypes.ModuleName,
258264
}
259265
}
260266

@@ -299,5 +305,6 @@ func orderInitBlockers() []string {
299305
globalfee.ModuleName,
300306
providertypes.ModuleName,
301307
consensusparamtypes.ModuleName,
308+
metaprotocolstypes.ModuleName,
302309
}
303310
}

docs/docs/metaprotocols/README.md

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: Metaprotocol Support
3+
order: false
4+
parent:
5+
order: 2
6+
---
7+
8+
The `x/metaprotocol` module adds support for encoding and decoding additional fields attached to transactions.
9+
10+
`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.
11+
12+
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.
13+
14+
:::tip
15+
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.
16+
:::
17+
18+
Here is an example of a correctly formed `non_critical_extension_options` field:
19+
20+
```json
21+
{
22+
"@type": "/gaia.metaprotocols.ExtensionData", // must be this exact string
23+
"protocol_id": "some-protocol",
24+
"protocol_version": "1",
25+
"data": "<base64 encoded bytes>"
26+
}
27+
```
28+
29+
Here is an example of a correctly populated `non_critical_extension_options` on a `bank.MsgSend` transaction:
30+
31+
```json
32+
{
33+
"body": {
34+
"messages": [
35+
{
36+
"@type": "/cosmos.bank.v1beta1.MsgSend",
37+
"from_address": "cosmos1ehpqg9sj09037uhe56sqktk30asn47asthyr22",
38+
"to_address": "cosmos1ehpqg9sj09037uhe56sqktk30asn47asthyr22",
39+
"amount": [
40+
{
41+
"denom": "uatom",
42+
"amount": "100"
43+
}
44+
]
45+
}
46+
],
47+
"memo": "memo_smaller_than_512_bytes",
48+
"timeout_height": "0",
49+
"extension_options": [],
50+
"non_critical_extension_options": [
51+
{
52+
"@type": "/gaia.metaprotocols.ExtensionData",
53+
"protocol_id": "some-protocol",
54+
"protocol_version": "1",
55+
"data": "<base64 encoded bytes>"
56+
}
57+
]
58+
},
59+
"auth_info": {
60+
"signer_infos": [],
61+
"fee": {
62+
"amount": [],
63+
"gas_limit": "200000",
64+
"payer": "",
65+
"granter": ""
66+
},
67+
"tip": null
68+
},
69+
"signatures": []
70+
}
71+
```
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"label": "Metaprotocol support",
3+
"position": 15,
4+
"link": { "type": "doc", "id": "metaprotocols/README" }
5+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
syntax = "proto3";
2+
package gaia.metaprotocols;
3+
4+
option go_package = "github.com/cosmos/gaia/x/metaprotocols/types";
5+
6+
// ExtensionData is a data structure that can be used in transaction extensions.
7+
message ExtensionData {
8+
// protocol_id is the identifier of the protocol
9+
// the field is not used internally but it is validated for correctness
10+
string protocol_id = 1;
11+
12+
// protocol_version is the identifier of the protocol version
13+
// the field is not used internally but it is validated for correctness
14+
string protocol_version = 2;
15+
16+
// arbitrary bytes data that can be used to store any data
17+
// the field is not used internally but it is validated and must be provided
18+
bytes data = 3;
19+
}

tests/e2e/chain.go

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
sdk "github.com/cosmos/cosmos-sdk/types"
1515
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
1616
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
17+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
1718
distribtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
1819
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
1920
govv1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
@@ -23,6 +24,7 @@ import (
2324
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
2425

2526
gaiaparams "github.com/cosmos/gaia/v15/app/params"
27+
metaprotocoltypes "github.com/cosmos/gaia/v15/x/metaprotocols/types"
2628
)
2729

2830
const (
@@ -38,6 +40,7 @@ var (
3840

3941
func init() {
4042
encodingConfig = gaiaparams.MakeEncodingConfig()
43+
banktypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
4144
authtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
4245
authvesting.RegisterInterfaces(encodingConfig.InterfaceRegistry)
4346
stakingtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
@@ -51,6 +54,7 @@ func init() {
5154
upgradetypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
5255
distribtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
5356
providertypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
57+
metaprotocoltypes.RegisterInterfaces(encodingConfig.InterfaceRegistry)
5458

5559
cdc = encodingConfig.Marshaler
5660
txConfig = encodingConfig.TxConfig

tests/e2e/e2e_bank_test.go

+121-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@ package e2e
22

33
import (
44
"fmt"
5+
"path/filepath"
56
"time"
67

8+
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
79
sdk "github.com/cosmos/cosmos-sdk/types"
10+
authTx "github.com/cosmos/cosmos-sdk/x/auth/tx"
11+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
12+
13+
extensiontypes "github.com/cosmos/gaia/v15/x/metaprotocols/types"
814
)
915

1016
func (s *IntegrationTestSuite) testBankTokenTransfer() {
11-
s.Run("send_photon_between_accounts", func() {
17+
s.Run("send_tokens_between_accounts", func() {
1218
var (
1319
err error
1420
valIdx = 0
@@ -96,3 +102,117 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() {
96102
)
97103
})
98104
}
105+
106+
// tests the bank send command with populated non_critical_extension_options field
107+
// the Tx should succeed if the data can be properly encoded and decoded
108+
// the tx is signed and broadcast using gaiad tx sign and broadcast commands
109+
func (s *IntegrationTestSuite) bankSendWithNonCriticalExtensionOptions() {
110+
s.Run("transfer_with_non_critical_extension_options", func() {
111+
c := s.chainA
112+
113+
submitterAccount := c.genesisAccounts[1]
114+
submitterAddress, err := submitterAccount.keyInfo.GetAddress()
115+
s.Require().NoError(err)
116+
sendMsg := banktypes.NewMsgSend(submitterAddress, submitterAddress, sdk.NewCoins(sdk.NewCoin(uatomDenom, sdk.NewInt(100))))
117+
118+
// valid non-critical extension options
119+
ext := &extensiontypes.ExtensionData{
120+
ProtocolId: "test-protocol",
121+
ProtocolVersion: "1",
122+
Data: []byte("Hello Cosmos"),
123+
}
124+
125+
extAny, err := codectypes.NewAnyWithValue(ext)
126+
s.Require().NoError(err)
127+
s.Require().NotNil(extAny)
128+
129+
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
130+
131+
s.Require().NoError(txBuilder.SetMsgs(sendMsg))
132+
133+
txBuilder.SetMemo("non-critical-ext-message-test")
134+
txBuilder.SetFeeAmount(sdk.NewCoins(standardFees))
135+
txBuilder.SetGasLimit(200000)
136+
137+
// add extension options
138+
tx := txBuilder.GetTx()
139+
if etx, ok := tx.(authTx.ExtensionOptionsTxBuilder); ok {
140+
etx.SetNonCriticalExtensionOptions(extAny)
141+
}
142+
143+
bz, err := encodingConfig.TxConfig.TxEncoder()(tx)
144+
s.Require().NoError(err)
145+
s.Require().NotNil(bz)
146+
147+
txWithExt, err := decodeTx(bz)
148+
s.Require().NoError(err)
149+
s.Require().NotNil(txWithExt)
150+
151+
rawTx, err := cdc.MarshalJSON(txWithExt)
152+
s.Require().NoError(err)
153+
s.Require().NotNil(rawTx)
154+
155+
unsignedFname := "unsigned_non_critical_extension_option_tx.json"
156+
unsignedJSONFile := filepath.Join(c.validators[0].configDir(), unsignedFname)
157+
err = writeFile(unsignedJSONFile, rawTx)
158+
s.Require().NoError(err)
159+
160+
signedTx, err := s.signTxFileOnline(c, 0, submitterAddress.String(), unsignedFname)
161+
s.Require().NoError(err)
162+
s.Require().NotNil(signedTx)
163+
164+
signedFname := "signed_non_critical_extension_option_tx.json"
165+
signedJSONFile := filepath.Join(c.validators[0].configDir(), signedFname)
166+
err = writeFile(signedJSONFile, signedTx)
167+
s.Require().NoError(err)
168+
169+
// if there's no errors the non_critical_extension_options field was properly encoded and decoded
170+
out, err := s.broadcastTxFile(c, 0, submitterAddress.String(), signedFname)
171+
s.Require().NoError(err)
172+
s.Require().NotNil(out)
173+
})
174+
}
175+
176+
// tests the bank send command with invalid non_critical_extension_options field
177+
// the tx should always fail to decode the extension options since no concrete type is registered for the provided extension field
178+
func (s *IntegrationTestSuite) failedBankSendWithNonCriticalExtensionOptions() {
179+
s.Run("fail_encoding_invalid_non_critical_extension_options", func() {
180+
c := s.chainA
181+
182+
submitterAccount := c.genesisAccounts[1]
183+
submitterAddress, err := submitterAccount.keyInfo.GetAddress()
184+
s.Require().NoError(err)
185+
sendMsg := banktypes.NewMsgSend(submitterAddress, submitterAddress, sdk.NewCoins(sdk.NewCoin(uatomDenom, sdk.NewInt(100))))
186+
187+
// the message does not matter, as long as it is in the interface registry
188+
ext := &banktypes.MsgMultiSend{}
189+
190+
extAny, err := codectypes.NewAnyWithValue(ext)
191+
s.Require().NoError(err)
192+
s.Require().NotNil(extAny)
193+
194+
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
195+
196+
s.Require().NoError(txBuilder.SetMsgs(sendMsg))
197+
198+
txBuilder.SetMemo("fail-non-critical-ext-message")
199+
txBuilder.SetFeeAmount(sdk.NewCoins(standardFees))
200+
txBuilder.SetGasLimit(200000)
201+
202+
// add extension options
203+
tx := txBuilder.GetTx()
204+
if etx, ok := tx.(authTx.ExtensionOptionsTxBuilder); ok {
205+
etx.SetNonCriticalExtensionOptions(extAny)
206+
}
207+
208+
bz, err := encodingConfig.TxConfig.TxEncoder()(tx)
209+
s.Require().NoError(err)
210+
s.Require().NotNil(bz)
211+
212+
// decode fails because the provided extension option does not implement the correct TxExtensionOptionI interface
213+
txWithExt, err := decodeTx(bz)
214+
s.Require().Error(err)
215+
s.Require().ErrorContains(err, "failed to decode tx: no concrete type registered for type URL /cosmos.bank.v1beta1.MsgMultiSend against interface *tx.TxExtensionOptionI")
216+
s.Require().Nil(txWithExt)
217+
})
218+
}

0 commit comments

Comments
 (0)