From 84b9c544c742e30d999ccbcfc24b91efa43fe91d Mon Sep 17 00:00:00 2001
From: Milap Sheth <milap@axelar.network>
Date: Fri, 17 Nov 2023 11:37:14 -0500
Subject: [PATCH] fix: allow hex strings to be prefixed by 0x optionally in
 queries (#2028) (#2029)

* fix: allow hex strings to be prefixed by 0x optionally in queries

* lint
---
 cmd/axelard/cmd/helpers.go    |  3 +-
 utils/encoding.go             | 11 +++++++
 utils/encoding_test.go        | 59 +++++++++++++++++++++++++++++++++++
 vald/sign.go                  |  5 +--
 x/axelarnet/client/cli/tx.go  |  5 ++-
 x/evm/keeper/grpc_query.go    |  3 +-
 x/evm/keeper/migrate.go       |  5 ++-
 x/evm/types/params.go         |  5 ++-
 x/evm/types/testutils/rand.go |  5 ++-
 x/evm/types/types.go          |  2 +-
 10 files changed, 85 insertions(+), 18 deletions(-)
 create mode 100644 utils/encoding.go
 create mode 100644 utils/encoding_test.go

diff --git a/cmd/axelard/cmd/helpers.go b/cmd/axelard/cmd/helpers.go
index 2d60bc87d..f9ea27d31 100644
--- a/cmd/axelard/cmd/helpers.go
+++ b/cmd/axelard/cmd/helpers.go
@@ -1,7 +1,6 @@
 package cmd
 
 import (
-	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
@@ -45,5 +44,5 @@ func getByteCodes(file string) ([]byte, error) {
 		return nil, fmt.Errorf("could not retrieve bytecode from file")
 	}
 
-	return hex.DecodeString(str)
+	return utils.HexDecode(str)
 }
diff --git a/utils/encoding.go b/utils/encoding.go
new file mode 100644
index 000000000..a6329fc32
--- /dev/null
+++ b/utils/encoding.go
@@ -0,0 +1,11 @@
+package utils
+
+import (
+	"encoding/hex"
+	"strings"
+)
+
+// Decode a hex string. Hex string can be optionally prefixed with 0x.
+func HexDecode(input string) ([]byte, error) {
+	return hex.DecodeString(strings.TrimPrefix(input, "0x"))
+}
diff --git a/utils/encoding_test.go b/utils/encoding_test.go
new file mode 100644
index 000000000..752b39019
--- /dev/null
+++ b/utils/encoding_test.go
@@ -0,0 +1,59 @@
+package utils
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestHexDecode(t *testing.T) {
+	tests := []struct {
+		name    string
+		input   string
+		want    []byte
+		wantErr bool
+	}{
+		{
+			name:    "empty input",
+			input:   "",
+			want:    []byte{},
+			wantErr: false,
+		},
+		{
+			name:    "valid input with 0x prefix",
+			input:   "0x68656c6c6f",
+			want:    []byte("hello"),
+			wantErr: false,
+		},
+		{
+			name:    "valid input without 0x prefix",
+			input:   "68656c6c6f",
+			want:    []byte("hello"),
+			wantErr: false,
+		},
+		{
+			name:    "invalid input with odd number of characters",
+			input:   "68656c6c6",
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name:    "invalid input with non-hex characters",
+			input:   "68656c6c6z",
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			got, err := HexDecode(test.input)
+			if test.wantErr {
+				assert.Error(t, err)
+				return
+			}
+
+			assert.Equal(t, test.want, got)
+		})
+	}
+}
diff --git a/vald/sign.go b/vald/sign.go
index a628a13e3..e29f927c0 100644
--- a/vald/sign.go
+++ b/vald/sign.go
@@ -16,6 +16,7 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/spf13/cobra"
 
+	"github.com/axelarnetwork/axelar-core/utils"
 	"github.com/axelarnetwork/axelar-core/vald/config"
 	"github.com/axelarnetwork/axelar-core/vald/tss"
 	evm "github.com/axelarnetwork/axelar-core/x/evm/types"
@@ -61,7 +62,7 @@ func GetSignCommand() *cobra.Command {
 				}
 			}
 
-			pubKeyRaw, err := hex.DecodeString(pubKeyHex)
+			pubKeyRaw, err := utils.HexDecode(pubKeyHex)
 			if err != nil {
 				return err
 			}
@@ -71,7 +72,7 @@ func GetSignCommand() *cobra.Command {
 				return err
 			}
 
-			hashRaw, err := hex.DecodeString(args[2])
+			hashRaw, err := utils.HexDecode(args[2])
 			if err != nil {
 				return err
 			}
diff --git a/x/axelarnet/client/cli/tx.go b/x/axelarnet/client/cli/tx.go
index 740ae7774..797b59b1a 100644
--- a/x/axelarnet/client/cli/tx.go
+++ b/x/axelarnet/client/cli/tx.go
@@ -1,7 +1,6 @@
 package cli
 
 import (
-	"encoding/hex"
 	"fmt"
 	"os"
 	"strconv"
@@ -316,7 +315,7 @@ func getGeneralMessage() *cobra.Command {
 			}
 
 			id := utils.NormalizeString(args[0])
-			payload, err := hex.DecodeString(args[1])
+			payload, err := utils.HexDecode(args[1])
 			if err != nil {
 				return err
 			}
@@ -357,7 +356,7 @@ func getCmdCallContract() *cobra.Command {
 				return err
 			}
 
-			payload, err := hex.DecodeString(strings.TrimPrefix(args[2], "0x"))
+			payload, err := utils.HexDecode(args[2])
 			if err != nil {
 				return err
 			}
diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go
index 45feeaec4..52e2a2cfc 100644
--- a/x/evm/keeper/grpc_query.go
+++ b/x/evm/keeper/grpc_query.go
@@ -15,6 +15,7 @@ import (
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 
+	"github.com/axelarnetwork/axelar-core/utils"
 	"github.com/axelarnetwork/axelar-core/x/evm/types"
 	multisig "github.com/axelarnetwork/axelar-core/x/multisig/exported"
 	nexustypes "github.com/axelarnetwork/axelar-core/x/nexus/exported"
@@ -259,7 +260,7 @@ func (q Querier) BatchedCommands(c context.Context, req *types.BatchedCommandsRe
 			return nil, status.Error(codes.NotFound, sdkerrors.Wrap(types.ErrEVM, fmt.Sprintf("could not get the latest batched commands for chain %s", req.Chain)).Error())
 		}
 	default:
-		commandBatchID, err := hex.DecodeString(req.Id)
+		commandBatchID, err := utils.HexDecode(req.Id)
 		if err != nil {
 			return nil, status.Error(codes.InvalidArgument, sdkerrors.Wrap(types.ErrEVM, fmt.Sprintf("invalid batched commands ID: %v", err)).Error())
 		}
diff --git a/x/evm/keeper/migrate.go b/x/evm/keeper/migrate.go
index f485c323d..eaa05998f 100644
--- a/x/evm/keeper/migrate.go
+++ b/x/evm/keeper/migrate.go
@@ -1,7 +1,6 @@
 package keeper
 
 import (
-	"encoding/hex"
 	"fmt"
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -140,12 +139,12 @@ func AlwaysMigrateBytecode(k *BaseKeeper, n types.Nexus, otherMigrations func(ct
 // EVM chain. It's crucial whenever contracts are changed between versions.
 // DO NOT DELETE
 func migrateContractsBytecode(ctx sdk.Context, ck chainKeeper) error {
-	bzToken, err := hex.DecodeString(types.Token)
+	bzToken, err := utils.HexDecode(types.Token)
 	if err != nil {
 		return err
 	}
 
-	bzBurnable, err := hex.DecodeString(types.Burnable)
+	bzBurnable, err := utils.HexDecode(types.Burnable)
 	if err != nil {
 		return err
 	}
diff --git a/x/evm/types/params.go b/x/evm/types/params.go
index 45e29bf7f..9b25f2cde 100644
--- a/x/evm/types/params.go
+++ b/x/evm/types/params.go
@@ -1,7 +1,6 @@
 package types
 
 import (
-	"encoding/hex"
 	"fmt"
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -39,12 +38,12 @@ func KeyTable() params.KeyTable {
 
 // DefaultParams returns the module's parameter set initialized with default values
 func DefaultParams() []Params {
-	bzToken, err := hex.DecodeString(Token)
+	bzToken, err := utils.HexDecode(Token)
 	if err != nil {
 		panic(err)
 	}
 
-	bzBurnable, err := hex.DecodeString(Burnable)
+	bzBurnable, err := utils.HexDecode(Burnable)
 	if err != nil {
 		panic(err)
 	}
diff --git a/x/evm/types/testutils/rand.go b/x/evm/types/testutils/rand.go
index 939e9de2d..0356d4107 100644
--- a/x/evm/types/testutils/rand.go
+++ b/x/evm/types/testutils/rand.go
@@ -1,7 +1,6 @@
 package testutils
 
 import (
-	"encoding/hex"
 	"fmt"
 	"math/big"
 	"strings"
@@ -323,7 +322,7 @@ func RandomTokens() []types.ERC20TokenMetadata {
 
 // RandomToken returns a random (valid) token for testing
 func RandomToken() types.ERC20TokenMetadata {
-	bzBurnable, err := hex.DecodeString(types.Burnable)
+	bzBurnable, err := utils.HexDecode(types.Burnable)
 	if err != nil {
 		panic(err)
 	}
@@ -390,7 +389,7 @@ func RandomBurnerInfo() types.BurnerInfo {
 
 // RandomParams returns a random (valid) params instance for testing
 func RandomParams() types.Params {
-	bzBurnable, err := hex.DecodeString(types.Burnable)
+	bzBurnable, err := utils.HexDecode(types.Burnable)
 	if err != nil {
 		panic(err)
 	}
diff --git a/x/evm/types/types.go b/x/evm/types/types.go
index a24929d66..5e50b8e3b 100644
--- a/x/evm/types/types.go
+++ b/x/evm/types/types.go
@@ -703,7 +703,7 @@ func CommandIDFromTransferID(id nexus.TransferID) CommandID {
 
 // HexToCommandID decodes a hex representation of a CommandID
 func HexToCommandID(id string) (CommandID, error) {
-	bz, err := hex.DecodeString(id)
+	bz, err := utils.HexDecode(id)
 	if err != nil {
 		return CommandID{}, err
 	}