github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/signer/core/signed_data_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  //
    17  package core_test
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"math/big"
    24  	"testing"
    25  
    26  	"github.com/ethereum/go-ethereum/accounts/keystore"
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/common/hexutil"
    29  	"github.com/ethereum/go-ethereum/signer/core"
    30  )
    31  
    32  var typesStandard = core.Types{
    33  	"EIP712Domain": {
    34  		{
    35  			Name: "name",
    36  			Type: "string",
    37  		},
    38  		{
    39  			Name: "version",
    40  			Type: "string",
    41  		},
    42  		{
    43  			Name: "chainId",
    44  			Type: "uint256",
    45  		},
    46  		{
    47  			Name: "verifyingContract",
    48  			Type: "address",
    49  		},
    50  	},
    51  	"Person": {
    52  		{
    53  			Name: "name",
    54  			Type: "string",
    55  		},
    56  		{
    57  			Name: "wallet",
    58  			Type: "address",
    59  		},
    60  	},
    61  	"Mail": {
    62  		{
    63  			Name: "from",
    64  			Type: "Person",
    65  		},
    66  		{
    67  			Name: "to",
    68  			Type: "Person",
    69  		},
    70  		{
    71  			Name: "contents",
    72  			Type: "string",
    73  		},
    74  	},
    75  }
    76  
    77  var jsonTypedData = `
    78      {
    79        "types": {
    80          "EIP712Domain": [
    81            {
    82              "name": "name",
    83              "type": "string"
    84            },
    85            {
    86              "name": "version",
    87              "type": "string"
    88            },
    89            {
    90              "name": "chainId",
    91              "type": "uint256"
    92            },
    93            {
    94              "name": "verifyingContract",
    95              "type": "address"
    96            }
    97          ],
    98          "Person": [
    99            {
   100              "name": "name",
   101              "type": "string"
   102            },
   103            {
   104              "name": "test",
   105              "type": "uint8"
   106            },
   107            {
   108              "name": "wallet",
   109              "type": "address"
   110            }
   111          ],
   112          "Mail": [
   113            {
   114              "name": "from",
   115              "type": "Person"
   116            },
   117            {
   118              "name": "to",
   119              "type": "Person"
   120            },
   121            {
   122              "name": "contents",
   123              "type": "string"
   124            }
   125          ]
   126        },
   127        "primaryType": "Mail",
   128        "domain": {
   129          "name": "Ether Mail",
   130          "version": "1",
   131          "chainId": 1,
   132          "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   133        },
   134        "message": {
   135          "from": {
   136            "name": "Cow",
   137  		  "test": 3,
   138            "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
   139          },
   140          "to": {
   141            "name": "Bob",
   142            "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
   143          },
   144          "contents": "Hello, Bob!"
   145        }
   146      }
   147  `
   148  
   149  const primaryType = "Mail"
   150  
   151  var domainStandard = core.TypedDataDomain{
   152  	"Ether Mail",
   153  	"1",
   154  	big.NewInt(1),
   155  	"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
   156  	"",
   157  }
   158  
   159  var messageStandard = map[string]interface{}{
   160  	"from": map[string]interface{}{
   161  		"name":   "Cow",
   162  		"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
   163  	},
   164  	"to": map[string]interface{}{
   165  		"name":   "Bob",
   166  		"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
   167  	},
   168  	"contents": "Hello, Bob!",
   169  }
   170  
   171  var typedData = core.TypedData{
   172  	Types:       typesStandard,
   173  	PrimaryType: primaryType,
   174  	Domain:      domainStandard,
   175  	Message:     messageStandard,
   176  }
   177  
   178  func TestSignData(t *testing.T) {
   179  	api, control := setup(t)
   180  	//Create two accounts
   181  	createAccount(control, api, t)
   182  	createAccount(control, api, t)
   183  	control.approveCh <- "1"
   184  	list, err := api.List(context.Background())
   185  	if err != nil {
   186  		t.Fatal(err)
   187  	}
   188  	a := common.NewMixedcaseAddress(list[0])
   189  
   190  	control.approveCh <- "Y"
   191  	control.inputCh <- "wrongpassword"
   192  	signature, err := api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
   193  	if signature != nil {
   194  		t.Errorf("Expected nil-data, got %x", signature)
   195  	}
   196  	if err != keystore.ErrDecrypt {
   197  		t.Errorf("Expected ErrLocked! '%v'", err)
   198  	}
   199  	control.approveCh <- "No way"
   200  	signature, err = api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
   201  	if signature != nil {
   202  		t.Errorf("Expected nil-data, got %x", signature)
   203  	}
   204  	if err != core.ErrRequestDenied {
   205  		t.Errorf("Expected ErrRequestDenied! '%v'", err)
   206  	}
   207  	// text/plain
   208  	control.approveCh <- "Y"
   209  	control.inputCh <- "a_long_password"
   210  	signature, err = api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	if signature == nil || len(signature) != 65 {
   215  		t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
   216  	}
   217  	// data/typed
   218  	control.approveCh <- "Y"
   219  	control.inputCh <- "a_long_password"
   220  	signature, err = api.SignTypedData(context.Background(), a, typedData)
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	if signature == nil || len(signature) != 65 {
   225  		t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
   226  	}
   227  }
   228  
   229  func TestDomainChainId(t *testing.T) {
   230  	withoutChainID := core.TypedData{
   231  		Types: core.Types{
   232  			"EIP712Domain": []core.Type{
   233  				{Name: "name", Type: "string"},
   234  			},
   235  		},
   236  		Domain: core.TypedDataDomain{
   237  			Name: "test",
   238  		},
   239  	}
   240  
   241  	if _, ok := withoutChainID.Domain.Map()["chainId"]; ok {
   242  		t.Errorf("Expected the chainId key to not be present in the domain map")
   243  	}
   244  
   245  	withChainID := core.TypedData{
   246  		Types: core.Types{
   247  			"EIP712Domain": []core.Type{
   248  				{Name: "name", Type: "string"},
   249  				{Name: "chainId", Type: "uint256"},
   250  			},
   251  		},
   252  		Domain: core.TypedDataDomain{
   253  			Name:    "test",
   254  			ChainId: big.NewInt(1),
   255  		},
   256  	}
   257  
   258  	if _, ok := withChainID.Domain.Map()["chainId"]; !ok {
   259  		t.Errorf("Expected the chainId key be present in the domain map")
   260  	}
   261  }
   262  
   263  func TestHashStruct(t *testing.T) {
   264  	hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
   265  	if err != nil {
   266  		t.Fatal(err)
   267  	}
   268  	mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
   269  	if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" {
   270  		t.Errorf("Expected different hashStruct result (got %s)", mainHash)
   271  	}
   272  
   273  	hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
   274  	if err != nil {
   275  		t.Error(err)
   276  	}
   277  	domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
   278  	if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" {
   279  		t.Errorf("Expected different domain hashStruct result (got %s)", domainHash)
   280  	}
   281  }
   282  
   283  func TestEncodeType(t *testing.T) {
   284  	domainTypeEncoding := string(typedData.EncodeType("EIP712Domain"))
   285  	if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" {
   286  		t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding)
   287  	}
   288  
   289  	mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType))
   290  	if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" {
   291  		t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding)
   292  	}
   293  }
   294  
   295  func TestTypeHash(t *testing.T) {
   296  	mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType)))
   297  	if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" {
   298  		t.Errorf("Expected different typeHash result (got %s)", mailTypeHash)
   299  	}
   300  }
   301  
   302  func TestEncodeData(t *testing.T) {
   303  	hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0)
   304  	if err != nil {
   305  		t.Fatal(err)
   306  	}
   307  	dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
   308  	if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" {
   309  		t.Errorf("Expected different encodeData result (got %s)", dataEncoding)
   310  	}
   311  }
   312  
   313  func TestMalformedDomainkeys(t *testing.T) {
   314  	// Verifies that malformed domain keys are properly caught:
   315  	//{
   316  	//	"name": "Ether Mail",
   317  	//	"version": "1",
   318  	//	"chainId": 1,
   319  	//	"vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   320  	//}
   321  	jsonTypedData := `
   322      {
   323        "types": {
   324          "EIP712Domain": [
   325            {
   326              "name": "name",
   327              "type": "string"
   328            },
   329            {
   330              "name": "version",
   331              "type": "string"
   332            },
   333            {
   334              "name": "chainId",
   335              "type": "uint256"
   336            },
   337            {
   338              "name": "verifyingContract",
   339              "type": "address"
   340            }
   341          ],
   342          "Person": [
   343            {
   344              "name": "name",
   345              "type": "string"
   346            },
   347            {
   348              "name": "wallet",
   349              "type": "address"
   350            }
   351          ],
   352          "Mail": [
   353            {
   354              "name": "from",
   355              "type": "Person"
   356            },
   357            {
   358              "name": "to",
   359              "type": "Person"
   360            },
   361            {
   362              "name": "contents",
   363              "type": "string"
   364            }
   365          ]
   366        },
   367        "primaryType": "Mail",
   368        "domain": {
   369          "name": "Ether Mail",
   370          "version": "1",
   371          "chainId": 1,
   372          "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   373        },
   374        "message": {
   375          "from": {
   376            "name": "Cow",
   377            "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
   378          },
   379          "to": {
   380            "name": "Bob",
   381            "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
   382          },
   383          "contents": "Hello, Bob!"
   384        }
   385      }
   386  `
   387  	var malformedDomainTypedData core.TypedData
   388  	err := json.Unmarshal([]byte(jsonTypedData), &malformedDomainTypedData)
   389  	if err != nil {
   390  		t.Fatalf("unmarshalling failed '%v'", err)
   391  	}
   392  	_, err = malformedDomainTypedData.HashStruct("EIP712Domain", malformedDomainTypedData.Domain.Map())
   393  	if err == nil || err.Error() != "provided data '<nil>' doesn't match type 'address'" {
   394  		t.Errorf("Expected `provided data '<nil>' doesn't match type 'address'`, got '%v'", err)
   395  	}
   396  }
   397  
   398  func TestMalformedTypesAndExtradata(t *testing.T) {
   399  	// Verifies several quirks
   400  	// 1. Using dynamic types and only validating the prefix:
   401  	//{
   402  	//	"name": "chainId",
   403  	//	"type": "uint256 ... and now for something completely different"
   404  	//}
   405  	// 2. Extra data in message:
   406  	//{
   407  	//  "blahonga": "zonk bonk"
   408  	//}
   409  	jsonTypedData := `
   410      {
   411        "types": {
   412          "EIP712Domain": [
   413            {
   414              "name": "name",
   415              "type": "string"
   416            },
   417            {
   418              "name": "version",
   419              "type": "string"
   420            },
   421            {
   422              "name": "chainId",
   423              "type": "uint256 ... and now for something completely different"
   424            },
   425            {
   426              "name": "verifyingContract",
   427              "type": "address"
   428            }
   429          ],
   430          "Person": [
   431            {
   432              "name": "name",
   433              "type": "string"
   434            },
   435            {
   436              "name": "wallet",
   437              "type": "address"
   438            }
   439          ],
   440          "Mail": [
   441            {
   442              "name": "from",
   443              "type": "Person"
   444            },
   445            {
   446              "name": "to",
   447              "type": "Person"
   448            },
   449            {
   450              "name": "contents",
   451              "type": "string"
   452            }
   453          ]
   454        },
   455        "primaryType": "Mail",
   456        "domain": {
   457          "name": "Ether Mail",
   458          "version": "1",
   459          "chainId": 1,
   460          "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   461        },
   462        "message": {
   463          "from": {
   464            "name": "Cow",
   465            "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
   466          },
   467          "to": {
   468            "name": "Bob",
   469            "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
   470          },
   471          "contents": "Hello, Bob!"
   472        }
   473      }
   474  `
   475  	var malformedTypedData core.TypedData
   476  	err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
   477  	if err != nil {
   478  		t.Fatalf("unmarshalling failed '%v'", err)
   479  	}
   480  
   481  	malformedTypedData.Types["EIP712Domain"][2].Type = "uint256"
   482  	malformedTypedData.Message["blahonga"] = "zonk bonk"
   483  	_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
   484  	if err == nil || err.Error() != "there is extra data provided in the message" {
   485  		t.Errorf("Expected `there is extra data provided in the message`, got '%v'", err)
   486  	}
   487  }
   488  
   489  func TestTypeMismatch(t *testing.T) {
   490  	// Verifies that:
   491  	// 1. Mismatches between the given type and data, i.e. `Person` and
   492  	// 		the data item is a string, are properly caught:
   493  	//{
   494  	//	"name": "contents",
   495  	//	"type": "Person"
   496  	//},
   497  	//{
   498  	//	"contents": "Hello, Bob!" <-- string not "Person"
   499  	//}
   500  	// 2. Nonexistent types are properly caught:
   501  	//{
   502  	//	"name": "contents",
   503  	//	"type": "Blahonga"
   504  	//}
   505  	jsonTypedData := `
   506      {
   507        "types": {
   508          "EIP712Domain": [
   509            {
   510              "name": "name",
   511              "type": "string"
   512            },
   513            {
   514              "name": "version",
   515              "type": "string"
   516            },
   517            {
   518              "name": "chainId",
   519              "type": "uint256"
   520            },
   521            {
   522              "name": "verifyingContract",
   523              "type": "address"
   524            }
   525          ],
   526          "Person": [
   527            {
   528              "name": "name",
   529              "type": "string"
   530            },
   531            {
   532              "name": "wallet",
   533              "type": "address"
   534            }
   535          ],
   536          "Mail": [
   537            {
   538              "name": "from",
   539              "type": "Person"
   540            },
   541            {
   542              "name": "to",
   543              "type": "Person"
   544            },
   545            {
   546              "name": "contents",
   547              "type": "Person"
   548            }
   549          ]
   550        },
   551        "primaryType": "Mail",
   552        "domain": {
   553          "name": "Ether Mail",
   554          "version": "1",
   555          "chainId": 1,
   556          "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   557        },
   558        "message": {
   559          "from": {
   560            "name": "Cow",
   561            "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
   562          },
   563          "to": {
   564            "name": "Bob",
   565            "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
   566          },
   567          "contents": "Hello, Bob!"
   568        }
   569      }
   570  `
   571  	var mismatchTypedData core.TypedData
   572  	err := json.Unmarshal([]byte(jsonTypedData), &mismatchTypedData)
   573  	if err != nil {
   574  		t.Fatalf("unmarshalling failed '%v'", err)
   575  	}
   576  	_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
   577  	if err.Error() != "provided data 'Hello, Bob!' doesn't match type 'Person'" {
   578  		t.Errorf("Expected `provided data 'Hello, Bob!' doesn't match type 'Person'`, got '%v'", err)
   579  	}
   580  
   581  	mismatchTypedData.Types["Mail"][2].Type = "Blahonga"
   582  	_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
   583  	if err == nil || err.Error() != "reference type 'Blahonga' is undefined" {
   584  		t.Fatalf("Expected `reference type 'Blahonga' is undefined`, got '%v'", err)
   585  	}
   586  }
   587  
   588  func TestTypeOverflow(t *testing.T) {
   589  	// Verifies data that doesn't fit into it:
   590  	//{
   591  	//	"test": 65536 <-- test defined as uint8
   592  	//}
   593  	var overflowTypedData core.TypedData
   594  	err := json.Unmarshal([]byte(jsonTypedData), &overflowTypedData)
   595  	if err != nil {
   596  		t.Fatalf("unmarshalling failed '%v'", err)
   597  	}
   598  	// Set test to something outside uint8
   599  	(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(65536)
   600  
   601  	_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
   602  	if err == nil || err.Error() != "integer larger than 'uint8'" {
   603  		t.Fatalf("Expected `integer larger than 'uint8'`, got '%v'", err)
   604  	}
   605  
   606  	(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
   607  	(overflowTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
   608  
   609  	_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
   610  	if err != nil {
   611  		t.Fatalf("Expected no err, got '%v'", err)
   612  	}
   613  }
   614  
   615  func TestArray(t *testing.T) {
   616  	// Makes sure that arrays work fine
   617  	//{
   618  	//	"type": "address[]"
   619  	//},
   620  	//{
   621  	//	"type": "string[]"
   622  	//},
   623  	//{
   624  	//	"type": "uint16[]",
   625  	//}
   626  
   627  	jsonTypedData := `
   628  		{
   629  	      "types": {
   630  	        "EIP712Domain": [
   631  	          {
   632  	            "name": "name",
   633  	            "type": "string"
   634  	          },
   635  	          {
   636  	            "name": "version",
   637  	            "type": "string"
   638  	          },
   639  	          {
   640  	            "name": "chainId",
   641  	            "type": "uint256"
   642  	          },
   643  	          {
   644  	            "name": "verifyingContract",
   645  	            "type": "address"
   646  	          }
   647  	        ],
   648  	        "Foo": [
   649  	          {
   650  	            "name": "bar",
   651  	            "type": "address[]"
   652  	          }
   653  	        ]
   654  	      },
   655  	      "primaryType": "Foo",
   656  	      "domain": {
   657  	        "name": "Lorem",
   658  	        "version": "1",
   659  	        "chainId": 1,
   660  	        "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   661  	      },
   662  	      "message": {
   663  	        "bar": [
   664  	        	"0x0000000000000000000000000000000000000001",
   665  	        	"0x0000000000000000000000000000000000000002",
   666  	        	"0x0000000000000000000000000000000000000003"
   667          	]
   668  	      }
   669  	    }
   670  	`
   671  	var arrayTypedData core.TypedData
   672  	err := json.Unmarshal([]byte(jsonTypedData), &arrayTypedData)
   673  	if err != nil {
   674  		t.Fatalf("unmarshalling failed '%v'", err)
   675  	}
   676  	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
   677  	if err != nil {
   678  		t.Fatalf("Expected no err, got '%v'", err)
   679  	}
   680  
   681  	// Change array to string
   682  	arrayTypedData.Types["Foo"][0].Type = "string[]"
   683  	arrayTypedData.Message["bar"] = []interface{}{
   684  		"lorem",
   685  		"ipsum",
   686  		"dolores",
   687  	}
   688  	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
   689  	if err != nil {
   690  		t.Fatalf("Expected no err, got '%v'", err)
   691  	}
   692  
   693  	// Change array to uint
   694  	arrayTypedData.Types["Foo"][0].Type = "uint[]"
   695  	arrayTypedData.Message["bar"] = []interface{}{
   696  		big.NewInt(1955),
   697  		big.NewInt(108),
   698  		big.NewInt(44010),
   699  	}
   700  	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
   701  	if err != nil {
   702  		t.Fatalf("Expected no err, got '%v'", err)
   703  	}
   704  
   705  	// Should not work with fixed-size arrays
   706  	arrayTypedData.Types["Foo"][0].Type = "uint[3]"
   707  	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
   708  	if err == nil || err.Error() != "unknown type 'uint[3]'" {
   709  		t.Fatalf("Expected `unknown type 'uint[3]'`, got '%v'", err)
   710  	}
   711  }
   712  
   713  func TestCustomTypeAsArray(t *testing.T) {
   714  	var jsonTypedData = `
   715      {
   716        "types": {
   717          "EIP712Domain": [
   718            {
   719              "name": "name",
   720              "type": "string"
   721            },
   722            {
   723  			"name": "version",
   724              "type": "string"
   725            },
   726            {
   727              "name": "chainId",
   728              "type": "uint256"
   729            },
   730            {
   731  			"name": "verifyingContract",
   732              "type": "address"
   733            }
   734          ],
   735          "Person": [
   736            {
   737  			"name": "name",
   738              "type": "string"
   739            },
   740            {
   741  			"name": "wallet",
   742              "type": "address"
   743            }
   744          ],
   745          "Person[]": [
   746            {
   747  			"name": "baz",
   748              "type": "string"
   749            }
   750  		],
   751          "Mail": [
   752            {
   753  			"name": "from",
   754              "type": "Person"
   755            },
   756            {
   757  			"name": "to",
   758              "type": "Person[]"
   759            },
   760            {
   761  			"name": "contents",
   762              "type": "string"
   763            }
   764          ]
   765        },
   766        "primaryType": "Mail",
   767        "domain": {
   768  		"name": "Ether Mail",
   769          "version": "1",
   770          "chainId": 1,
   771          "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   772        },
   773        "message": {
   774          "from": {
   775  			"name": "Cow",
   776  			"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
   777          },
   778          "to": {"baz": "foo"},
   779          "contents": "Hello, Bob!"
   780        }
   781      }
   782  
   783  `
   784  	var malformedTypedData core.TypedData
   785  	err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
   786  	if err != nil {
   787  		t.Fatalf("unmarshalling failed '%v'", err)
   788  	}
   789  	_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
   790  	if err != nil {
   791  		t.Errorf("Expected no error, got '%v'", err)
   792  	}
   793  }
   794  
   795  func TestFormatter(t *testing.T) {
   796  	var d core.TypedData
   797  	err := json.Unmarshal([]byte(jsonTypedData), &d)
   798  	if err != nil {
   799  		t.Fatalf("unmarshalling failed '%v'", err)
   800  	}
   801  	formatted := d.Format()
   802  	for _, item := range formatted {
   803  		fmt.Printf("'%v'\n", item.Pprint(0))
   804  	}
   805  
   806  	j, _ := json.Marshal(formatted)
   807  	fmt.Printf("'%v'\n", string(j))
   808  }