-
Notifications
You must be signed in to change notification settings - Fork 201
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: base64 decoder for cw3 wasm messages #1731
Changes from all commits
de4dc1e
e1c6414
ef2d615
ff84d95
73206f5
a98a3e0
4da8ec7
505ed58
145fb80
76f48bc
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 |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
sdkcodec "github.com/cosmos/cosmos-sdk/codec" | ||
|
||
wasmvm "github.com/CosmWasm/wasmvm/types" | ||
) | ||
|
||
// YieldStargateMsgs parses the JSON and sends wasmvm.StargateMsg objects to a channel | ||
func YieldStargateMsgs(jsonBz []byte) ([]wasmvm.StargateMsg, error) { | ||
var data interface{} | ||
if err := json.Unmarshal(jsonBz, &data); err != nil { | ||
return nil, err | ||
} | ||
|
||
var msgs []wasmvm.StargateMsg | ||
parseStargateMsgs(data, &msgs) | ||
return msgs, nil | ||
} | ||
|
||
func parseStargateMsgs(jsonData any, msgs *[]wasmvm.StargateMsg) { | ||
switch v := jsonData.(type) { | ||
case map[string]interface{}: | ||
if typeURL, ok := v["type_url"].(string); ok { | ||
if value, ok := v["value"].(string); ok { | ||
*msgs = append(*msgs, wasmvm.StargateMsg{ | ||
TypeURL: typeURL, | ||
Value: []byte(value), | ||
}) | ||
} | ||
} | ||
for _, value := range v { | ||
parseStargateMsgs(value, msgs) | ||
} | ||
case []interface{}: | ||
for _, value := range v { | ||
parseStargateMsgs(value, msgs) | ||
} | ||
} | ||
} | ||
Comment on lines
+30
to
+49
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. The recursive function |
||
|
||
type StargateMsgDecoded struct { | ||
TypeURL string `json:"type_url"` | ||
Value string `json:"value"` | ||
} | ||
|
||
func DecodeBase64StargateMsgs( | ||
jsonBz []byte, context client.Context, | ||
) (newSgMsgs []StargateMsgDecoded, err error) { | ||
codec := context.Codec | ||
|
||
var data interface{} | ||
if err := json.Unmarshal(jsonBz, &data); err != nil { | ||
return []StargateMsgDecoded{}, err | ||
} | ||
|
||
sgMsgs, err := YieldStargateMsgs(jsonBz) | ||
if err != nil { | ||
return | ||
} | ||
for _, sgMsg := range sgMsgs { | ||
valueStr := string(sgMsg.Value) | ||
value := strings.Replace(string(sgMsg.Value), `\"`, `"`, -1) | ||
value = strings.Replace(value, `"{`, `{`, -1) | ||
value = strings.Replace(value, `}"`, `}`, -1) | ||
|
||
if _, err := base64.StdEncoding.DecodeString(valueStr); err == nil { | ||
protoMsg, err := context.InterfaceRegistry.Resolve(sgMsg.TypeURL) | ||
if err != nil { | ||
return newSgMsgs, err | ||
} | ||
|
||
decodedBz, _ := base64.StdEncoding.Strict().DecodeString(string(sgMsg.Value)) | ||
concrete := protoMsg.(sdkcodec.ProtoMarshaler) | ||
|
||
err = codec.Unmarshal(decodedBz, concrete) | ||
if err != nil { | ||
return newSgMsgs, err | ||
} | ||
|
||
outBytes, err := codec.MarshalJSON(concrete) | ||
if err != nil { | ||
return newSgMsgs, err | ||
} | ||
|
||
newSgMsgs = append(newSgMsgs, StargateMsgDecoded{sgMsg.TypeURL, string(outBytes)}) | ||
} else if _, err := json.Marshal(value); err == nil { | ||
newSgMsgs = append(newSgMsgs, StargateMsgDecoded{sgMsg.TypeURL, string(sgMsg.Value)}) | ||
} else { | ||
return newSgMsgs, fmt.Errorf( | ||
"parse error: encountered wasmvm.StargateMsg with unexpected format: %s", sgMsg) | ||
} | ||
} | ||
return newSgMsgs, nil | ||
} | ||
|
||
// DecodeBase64Cmd creates a cobra command for base64 decoding. | ||
func DecodeBase64Cmd(defaultNodeHome string) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "base64-decode", | ||
Short: "Decode a base64-encoded protobuf message", | ||
Long: `Decode a base64-encoded protobuf message from JSON input. | ||
The input should be a JSON object with 'type_url' and 'value' fields.`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
clientCtx := client.GetClientContextFromCmd(cmd) | ||
|
||
outMessage, err := DecodeBase64StargateMsgs([]byte(args[0]), clientCtx) | ||
fmt.Println(outMessage) | ||
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. Would put if err == nil, print so that it only runs on success |
||
|
||
return err | ||
}, | ||
} | ||
|
||
cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") | ||
|
||
return cmd | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package cmd_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/NibiruChain/nibiru/app" | ||
|
||
"github.com/cometbft/cometbft/libs/log" | ||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/server" | ||
genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" | ||
"github.com/spf13/viper" | ||
"github.com/stretchr/testify/require" | ||
|
||
nibid "github.com/NibiruChain/nibiru/cmd/nibid/cmd" | ||
) | ||
|
||
func TestBase64Decode(t *testing.T) { | ||
type TestCase struct { | ||
name string | ||
json_message string | ||
expectError bool | ||
} | ||
Comment on lines
+19
to
+24
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. The |
||
|
||
executeTest := func(t *testing.T, testCase TestCase) { | ||
tc := testCase | ||
t.Run(tc.name, func(t *testing.T) { | ||
home := t.TempDir() | ||
logger := log.NewNopLogger() | ||
cfg, err := genutiltest.CreateDefaultTendermintConfig(home) | ||
require.NoError(t, err) | ||
|
||
appCodec := app.MakeEncodingConfig().Marshaler | ||
err = genutiltest.ExecInitCmd( | ||
testModuleBasicManager, home, appCodec) | ||
require.NoError(t, err) | ||
|
||
serverCtx := server.NewContext(viper.New(), cfg, logger) | ||
clientCtx := (client.Context{}. | ||
WithCodec(appCodec). | ||
WithHomeDir(home). | ||
WithInterfaceRegistry(app.MakeEncodingConfig().InterfaceRegistry)) | ||
|
||
ctx := context.Background() | ||
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) | ||
ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) | ||
|
||
cmd := nibid.DecodeBase64Cmd(home) | ||
cmd.SetArgs([]string{ | ||
tc.json_message, | ||
}) | ||
|
||
if tc.expectError { | ||
require.Error(t, cmd.ExecuteContext(ctx)) | ||
} else { | ||
require.NoError(t, cmd.ExecuteContext(ctx)) | ||
} | ||
}) | ||
} | ||
Comment on lines
+26
to
+60
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. The |
||
|
||
testCases := []TestCase{ | ||
{ | ||
name: "valid message", | ||
json_message: ` | ||
{ | ||
"stargate": { | ||
"type_url": "/cosmos.staking.v1beta1.MsgUndelegate", | ||
"value": "Cj9uaWJpMTdwOXJ6d25uZnhjanAzMnVuOXVnN3loaHpndGtodmw5amZrc3p0Z3c1dWg2OXdhYzJwZ3N5bjcwbmoSMm5pYml2YWxvcGVyMXdqNWtma25qa3BjNmpkMzByeHRtOHRweGZqZjd4cWx3eDM4YzdwGgwKBXVuaWJpEgMxMTE=" | ||
} | ||
}`, | ||
expectError: false, | ||
}, | ||
{ | ||
name: "valid message", | ||
json_message: ` | ||
{ | ||
"stargate": { | ||
"type_url": "/cosmos.staking.v1beta1.MsgUndelegate", | ||
"value": "Cj9uaWJpMTdwOXJ6d25uZnhjanAzMnVuOXVnN3loaHpndGtodmw5amZrc3p0Z3c1dWg2OXdhYzJwZ3N5bjcwbmoSMm5pYml2YWxvcGVyMXdqNWtma25qa3BjNmpkMzByeHRtOHRweGZqZjd4cWx3eDM4YzdwGgwKBXVuaWJpEgMxMTE=" | ||
}, | ||
"another": { | ||
"type_url": "/cosmos.staking.v1beta1.MsgDelegate", | ||
"value": {"delegator_address":"cosmos1eckjje8r8s48kv0pndgtwvehveedlzlnnshl3e", "validator_address":"cosmos1n6ndsc04xh2hqf506nhvhcggj0qwguf8ks06jj", "amount":{"denom":"unibi","amount":"42"} } | ||
} | ||
}`, | ||
expectError: false, | ||
}, | ||
{ | ||
name: "valid message", | ||
json_message: ` | ||
{ | ||
"another": { | ||
"type_url": "/cosmos.staking.v1beta1.MsgDelegate", | ||
"value": "{\"delegator_address\":\"cosmos1eckjje8r8s48kv0pndgtwvehveedlzlnnshl3e\", \"validator_address\":\"cosmos1n6ndsc04xh2hqf506nhvhcggj0qwguf8ks06jj\", \"amount\":{\"denom\":\"unibi\",\"amount\":\"42\"} }" | ||
} | ||
}`, | ||
expectError: false, | ||
}, | ||
{ | ||
name: "empty message", | ||
json_message: ` | ||
{ | ||
|
||
}`, | ||
expectError: false, | ||
}, | ||
{ | ||
name: "invalid json", | ||
json_message: ` | ||
|
||
}`, | ||
expectError: true, | ||
}, | ||
} | ||
|
||
for _, testCase := range testCases { | ||
executeTest(t, testCase) | ||
} | ||
} |
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.
The
YieldStargateMsgs
function correctly unmarshals JSON bytes into an interface and then parses the messages. However, there is no error handling for theparseStargateMsgs
function call. Ensure that any errors during parsing are properly handled.