From 799082a04545ccde01d8487b6899ef7f413a06bf Mon Sep 17 00:00:00 2001 From: Rootul Patel <rootulp@gmail.com> Date: Wed, 7 Jun 2023 15:19:16 -0400 Subject: [PATCH] fix: left pad namespace IDs --- cmd/celestia/rpc.go | 68 ++++++++++++++++++++++++---------------- cmd/celestia/rpc_test.go | 53 ++++++++++++------------------- share/nid.go | 5 ++- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/cmd/celestia/rpc.go b/cmd/celestia/rpc.go index 080b43dfc3..b6ec60dcd6 100644 --- a/cmd/celestia/rpc.go +++ b/cmd/celestia/rpc.go @@ -18,13 +18,12 @@ import ( "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" + "github.com/celestiaorg/celestia-app/pkg/appconsts" appns "github.com/celestiaorg/celestia-app/pkg/namespace" apptypes "github.com/celestiaorg/celestia-app/x/blob/types" - "github.com/celestiaorg/nmt/namespace" "github.com/celestiaorg/celestia-node/api/rpc/client" "github.com/celestiaorg/celestia-node/blob" - "github.com/celestiaorg/celestia-node/share" "github.com/celestiaorg/celestia-node/state" ) @@ -33,6 +32,8 @@ const ( // TODO: remove this once we add support for user specified namespace version. namespaceVersion = appns.NamespaceVersionZero + // TODO: remove this once we add support for user specified share version. + shareVersion = appconsts.ShareVersionZero ) var requestURL string @@ -74,7 +75,7 @@ func init() { } var rpcCmd = &cobra.Command{ - Use: "rpc [namespace] [method] [params...]", + Use: "rpc [namespaceID] [method] [params...]", Short: "Send JSON-RPC request", Args: cobra.MinimumNArgs(2), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -150,7 +151,7 @@ func parseParams(method string, params []string) []interface{} { panic("Error decoding blob data: base64 string could not be decoded.") } } - parsedBlob, err := blob.NewBlob(0, namespaceVersion, nID, blobData) + parsedBlob, err := blob.NewBlob(shareVersion, namespaceVersion, nID, blobData) if err != nil { panic(fmt.Sprintf("Error creating blob: %v", err)) } @@ -191,10 +192,10 @@ func parseParams(method string, params []string) []interface{} { } } parsedParams[2] = []*apptypes.Blob{{ - NamespaceId: nID[1:], + NamespaceId: nID, Data: blobData, - ShareVersion: 0, - NamespaceVersion: 0, + ShareVersion: uint32(shareVersion), + NamespaceVersion: uint32(namespaceVersion), }} return parsedParams[:3] case "Get": @@ -229,7 +230,7 @@ func parseParams(method string, params []string) []interface{} { if err != nil { panic(fmt.Sprintf("Error parsing namespace ID: %v", err)) } - parsedParams[1] = []namespace.ID{nID} + parsedParams[1] = nID return parsedParams case "QueryDelegation", "QueryUnbonding", "BalanceForAddress": var err error @@ -428,32 +429,45 @@ func parseSignatureForHelpstring(methodSig reflect.StructField) string { return simplifiedSignature } -func parseNamespaceID(param string) (namespace.ID, error) { - var nID []byte - var err error +// parseNamespaceID parses a namespace ID from a base64 or hex string. The param +// is expected to be the user-specified portion of a v0 namespace ID (i.e. the +// last 10 bytes) of a namespace ID. +func parseNamespaceID(param string) (namespaceID []byte, err error) { + userBytes, err := decodeNamespaceID(param) + if err != nil { + return nil, err + } + if len(userBytes) > appns.NamespaceVersionZeroIDSize { + return nil, fmt.Errorf("namespace ID %v is too large to be a v0 namespace, want <= %v bytes", userBytes, appns.NamespaceVersionZeroIDSize) + } + // if the namespace ID is <= 10 bytes, left pad it with 0s + return leftPad(userBytes, appns.NamespaceVersionZeroIDSize), nil +} + +// decodeNamespaceID decodes a namespace ID from a base64 or hex string +func decodeNamespaceID(param string) (namespaceID []byte, err error) { if strings.HasPrefix(param, "0x") { decoded, err := hex.DecodeString(param[2:]) if err != nil { return nil, fmt.Errorf("error decoding namespace ID: %w", err) } - nID = decoded - } else { - // otherwise, it's just a base64 string - nID, err = base64.StdEncoding.DecodeString(param) - if err != nil { - return nil, fmt.Errorf("error decoding namespace ID: %w", err) - } + return decoded, nil } - // if the namespace ID is <= 10 bytes, add v0 share + namespace prefix and zero pad - if len(nID) <= appns.NamespaceVersionZeroIDSize { - nID, err = share.NewNamespaceV0(nID) - if err != nil { - return nil, err - } - } else if len(nID) < appns.NamespaceSize { - return nil, fmt.Errorf("passed namespace is too large") + // otherwise, it's just a base64 string + decoded, err := base64.StdEncoding.DecodeString(param) + if err != nil { + return nil, fmt.Errorf("error decoding namespace ID: %w", err) + } + return decoded, nil +} + +func leftPad(data []byte, size int) []byte { + if len(data) >= size { + return data } - return nID, nil + padded := make([]byte, size) + copy(padded[size-len(data):], data) + return padded } func parseJSON(param string) (json.RawMessage, error) { diff --git a/cmd/celestia/rpc_test.go b/cmd/celestia/rpc_test.go index f16042c80d..5b8b146f21 100644 --- a/cmd/celestia/rpc_test.go +++ b/cmd/celestia/rpc_test.go @@ -12,56 +12,43 @@ func Test_parseNamespaceID(t *testing.T) { type testCase struct { name string param string - want namespace.ID + want []byte wantErr bool } testCases := []testCase{ { - name: "8 byte hex encoded namespace ID gets right padded", - param: "0x0c204d39600fddd3", - want: namespace.ID{ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3, 0x0, 0x0, - }, + name: "8 byte hex encoded namespace ID gets left padded with 0s", + param: "0x0c204d39600fddd3", + want: []byte{0x0, 0x0, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3}, wantErr: false, }, { - name: "10 byte hex encoded namespace ID", - param: "0x42690c204d39600fddd3", - want: namespace.ID{ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x42, 0x69, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3, - }, - wantErr: false, - }, - { - name: "29 byte hex encoded namespace ID", - param: "0x0000000000000000000000000000000000000001010101010101010101", - want: namespace.ID{ - 0x0, // namespace version - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // v0 ID prefix - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // namespace ID - }, + name: "10 byte hex encoded namespace ID remains unchanged", + param: "0x42690c204d39600fddd3", + want: []byte{0x42, 0x69, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3}, wantErr: false, }, { name: "11 byte hex encoded namespace ID returns error", - param: "0x42690c204d39600fddd3a3", - want: namespace.ID{}, + param: "0x1142690c204d39600fddd3", + want: []byte{}, wantErr: true, }, { - name: "10 byte base64 encoded namespace ID", - param: "QmkMIE05YA/d0w==", - want: namespace.ID{ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x42, 0x69, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3, - }, + name: "8 byte base64 encoded namespace ID gets left padded with 0s", + param: "QmkMIE05YA/d0w==", + want: namespace.ID{0x42, 0x69, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3}, + wantErr: false, + }, + { + name: "10 byte base64 encoded namespace ID remains unchanged", + param: "QmkMIE05YA/d0w==", + want: namespace.ID{0x42, 0x69, 0xc, 0x20, 0x4d, 0x39, 0x60, 0xf, 0xdd, 0xd3}, wantErr: false, }, { - name: "not base64 or hex encoded namespace ID returns error", - param: "5748493939429", + name: "11 byte base64 encoded namespace ID returns error", + param: "QQmkMIE05YA/d0w==", want: namespace.ID{}, wantErr: true, }, diff --git a/share/nid.go b/share/nid.go index dfd6a5de7e..9709e3bd2c 100644 --- a/share/nid.go +++ b/share/nid.go @@ -4,12 +4,11 @@ import ( "fmt" appns "github.com/celestiaorg/celestia-app/pkg/namespace" - "github.com/celestiaorg/nmt/namespace" ) -// NewNamespaceV0 takes variable size byte slice anc creates version 0 Namespace ID. +// NewNamespaceV0 takes variable size byte slice and creates a version 0 namespace. // The bytes slice must be <= 10 bytes. -func NewNamespaceV0(subNId []byte) (namespace.ID, error) { +func NewNamespaceV0(subNId []byte) ([]byte, error) { if lnid := len(subNId); lnid > appns.NamespaceVersionZeroIDSize { return nil, fmt.Errorf("namespace id must be <= %v, but it was %v bytes", appns.NamespaceVersionZeroIDSize, lnid) }