Repository: ethereum/go-ethereum (Go)

Issue:
Incorrect recursive encoding of nested bytes arrays (`bytes[]`) in apitypes
#### System information

Geth version: v1.14.12
CL client & version: Not related
OS & Version: OSX
Commit hash : (if `develop`)

#### Expected behaviour

Encode successfully


#### Actual behaviour

Get error:
```
failed to pack and hash typedData primary type: provided data '65' doesn't match type 'bytes'
```


#### Steps to reproduce the behaviour

1. Consider a message with a field `sigs` of type `bytes[]` in Solidity. In Go, this is represented as `[][]uint8`.

2. The `func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int)` method recursively encodes each field. When it encounters the `sigs` array, it correctly identifies it as an array due to the trailing "]" in the type string.
<img width="619" alt="image" src="https://github.com/user-attachments/assets/d6b7749d-5b39-44cd-beca-771d12b0d532" />

3. `encodeArrayValue` is called, receiving the `[][]uint8` as `arrayValue` and `bytes` as `parsedType` (correctly parsed from `bytes[]`).
<img width="859" alt="image" src="https://github.com/user-attachments/assets/8ebd2e37-0af8-44ca-bd2a-b81a34f7bc35" />

4. The problem arises within the loop of `encodeArrayValue`. Each item in `arrayValue` (which is a `[]uint8` representing a Solidity `bytes`) is incorrectly treated as another array. This triggers a recursive call to `encodeArrayValue`.
<img width="616" alt="image" src="https://github.com/user-attachments/assets/be2e75f2-9403-428e-8f5a-4b45314f7eef" />

5. In the second, erroneous `encodeArrayValue` call, the `[]uint8` is further split into individual `uint8` values. The `parsedType` remains `bytes` (because `strings.Split("bytes", "[")[0]` is still `bytes`). This leads to an error because a single `uint8` cannot be parsed as `bytes`.

#### Backtrace

````
[backtrace]
````

When submitting logs: please submit them as text and not screenshots.



Discussion:

Patch:
diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go
index b56931c1d18..b8b96bef92d 100644
--- a/signer/core/apitypes/types.go
+++ b/signer/core/apitypes/types.go
@@ -501,7 +501,16 @@ func (typedData *TypedData) encodeArrayValue(encValue interface{}, encType strin
 	for _, item := range arrayValue {
 		if reflect.TypeOf(item).Kind() == reflect.Slice ||
 			reflect.TypeOf(item).Kind() == reflect.Array {
-			encodedData, err := typedData.encodeArrayValue(item, parsedType, depth+1)
+			var (
+				encodedData hexutil.Bytes
+				err         error
+			)
+			if reflect.TypeOf(item).Elem().Kind() == reflect.Uint8 {
+				// the item type is bytes.  encode the bytes array directly instead of recursing.
+				encodedData, err = typedData.EncodePrimitiveValue(parsedType, item, depth+1)
+			} else {
+				encodedData, err = typedData.encodeArrayValue(item, parsedType, depth+1)
+			}
 			if err != nil {
 				return nil, err
 			}


Test patch:
diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go
index d0637010baa..b6c080736c3 100644
--- a/signer/core/signed_data_test.go
+++ b/signer/core/signed_data_test.go
@@ -1014,3 +1014,49 @@ func TestComplexTypedDataWithLowercaseReftype(t *testing.T) {
 		t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash)
 	}
 }
+
+var recursiveBytesTypesStandard = apitypes.Types{
+	"EIP712Domain": {
+		{
+			Name: "name",
+			Type: "string",
+		},
+		{
+			Name: "version",
+			Type: "string",
+		},
+		{
+			Name: "chainId",
+			Type: "uint256",
+		},
+		{
+			Name: "verifyingContract",
+			Type: "address",
+		},
+	},
+	"Val": {
+		{
+			Name: "field",
+			Type: "bytes[][]",
+		},
+	},
+}
+
+var recursiveBytesMessageStandard = map[string]interface{}{
+	"field": [][][]byte{{{1}, {2}}, {{3}, {4}}},
+}
+
+var recursiveBytesTypedData = apitypes.TypedData{
+	Types:       recursiveBytesTypesStandard,
+	PrimaryType: "Val",
+	Domain:      domainStandard,
+	Message:     recursiveBytesMessageStandard,
+}
+
+func TestEncodeDataRecursiveBytes(t *testing.T) {
+	typedData := recursiveBytesTypedData
+	_, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0)
+	if err != nil {
+		t.Fatalf("got err %v", err)
+	}
+}