Implemented latest recommendations

This commit is contained in:
Paul Berg 2019-01-20 17:01:19 +00:00
parent b0e872cd21
commit 8af9cda070
4 changed files with 388 additions and 192 deletions

View file

@ -383,8 +383,8 @@ Response
```json
{
"jsonrpc": "2.0",
"id": 3,
"jsonrpc": "2.0",
"result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
}
```
@ -480,8 +480,8 @@ Response
```json
{
"jsonrpc": "2.0",
"id": 1,
"jsonrpc": "2.0",
"result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
}
```

View file

@ -176,9 +176,9 @@ type (
ContentType string `json:"content_type"`
Address common.MixedcaseAddress `json:"address"`
Rawdata interface{} `json:"raw_data"`
Message []*NameValueType `json:"message"`
Hash hexutil.Bytes `json:"hash"`
Meta Metadata `json:"meta"`
Message []*NameValueType `json:"message"`
Hash hexutil.Bytes `json:"hash"`
Meta Metadata `json:"meta"`
}
SignDataResponse struct {
Approved bool `json:"approved"`

View file

@ -116,8 +116,8 @@ type TypedDataDomain struct {
Salt string `json:"salt"`
}
var typedDataRegexp = regexp.MustCompile(`^((address|bool|bytes|string)|((bytes)([1-9]|[1-2][0-9]|3[0-2]))|((int|uint)(8|16|32|64|128|256)))(\[\])?$`)
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[])?$`)
// var typedDataRegexp = regexp.MustCompile(`^((address|bool|bytes|int|uint|string)|((bytes)([1-9]|[1-2][0-9]|3[0-2]))|((int|uint)(8|16|32|64|128|256)))(\[\])?$`)
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`)
// Sign receives a request and produces a signature
@ -299,9 +299,6 @@ func SignTextPlain(data hexutil.Bytes) (hexutil.Bytes, string) {
// SignTypedData signs EIP-712 conformant typed data
// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) {
if err := typedData.Validate(); err != nil {
return nil, err
}
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
return nil, err
@ -396,6 +393,10 @@ func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes {
//
// each encoded member is 32-byte long
func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) {
if err := typedData.validate(); err != nil {
return nil, err
}
buffer := bytes.Buffer{}
// Verify extra data
@ -430,7 +431,7 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
}
arrayBuffer.Write(encodedData)
} else {
bytesValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth)
bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth)
if err != nil {
return nil, err
}
@ -495,15 +496,14 @@ func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interf
}
return crypto.Keccak256(bytesValue), nil
}
// bytes32 etc
if strings.HasPrefix(encType, "bytes") {
sizeStr := strings.TrimPrefix(encType, "bytes")
size, err := strconv.Atoi(sizeStr)
lengthStr := strings.TrimPrefix(encType, "bytes")
length, err := strconv.Atoi(lengthStr)
if err != nil {
return nil, fmt.Errorf("invalid size on bytes: %v", sizeStr)
return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr)
}
if size < 0 || size > 32 {
return nil, fmt.Errorf("invalid size on bytes: %d", size)
if length < 0 || length > 32 {
return nil, fmt.Errorf("invalid size on bytes: %d", length)
}
if byteValue, ok := encValue.(hexutil.Bytes); !ok {
return nil, dataMismatchError(encType, encValue)
@ -511,8 +511,22 @@ func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interf
return math.PaddedBigBytes(new(big.Int).SetBytes(byteValue), 32), nil
}
}
if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
length := 0
if encType == "int" || encType == "uint" {
length = 256
} else {
lengthStr := strings.TrimPrefix(strings.TrimPrefix(encType, "uint"), "int")
atoiSize, err := strconv.Atoi(lengthStr)
if err != nil {
return nil, fmt.Errorf("invalid size on integer: %v", lengthStr)
}
length = atoiSize
}
bigIntValue, ok := encValue.(*big.Int)
if bigIntValue.BitLen() > length {
return nil, fmt.Errorf("integer larger than '%v'", encType)
}
if !ok {
return nil, dataMismatchError(encType, encValue)
}
@ -591,12 +605,12 @@ func UnmarshalValidatorData(data interface{}) (ValidatorData, error) {
}, nil
}
// Validate make sure the types are sound
func (typedData *TypedData) Validate() error {
if err := typedData.Types.Validate(); err != nil {
// Validate makes sure the types are sound
func (typedData *TypedData) validate() error {
if err := typedData.Types.validate(); err != nil {
return err
}
if err := typedData.Domain.Validate(); err != nil {
if err := typedData.Domain.validate(); err != nil {
return err
}
return nil
@ -724,7 +738,7 @@ func (nvt *NameValueType) Pprint(depth int) string {
}
// Validate checks if the types object is conformant to the specs
func (t Types) Validate() error {
func (t Types) validate() error {
for typeKey, typeArr := range t {
for _, typeObj := range typeArr {
if typeKey == typeObj.Type {
@ -737,8 +751,7 @@ func (t Types) Validate() error {
if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
return fmt.Errorf("unknown reference type '%s", typeObj.Type)
}
} else if !typedDataRegexp.MatchString(typeObj.Type) {
} else if !isPrimitiveTypeValid(typeObj.Type) {
return fmt.Errorf("unknown type '%s'", typeObj.Type)
}
}
@ -746,9 +759,120 @@ func (t Types) Validate() error {
return nil
}
// Checks if the primitive value is valid
func isPrimitiveTypeValid(primitiveType string) bool {
if primitiveType == "address" ||
primitiveType == "address[]" ||
primitiveType == "bool" ||
primitiveType == "bool[]" ||
primitiveType == "string" ||
primitiveType == "string[]" {
return true
}
if primitiveType == "bytes" ||
primitiveType == "bytes[]" ||
primitiveType == "bytes1" ||
primitiveType == "bytes1[]" ||
primitiveType == "bytes2" ||
primitiveType == "bytes2[]" ||
primitiveType == "bytes3" ||
primitiveType == "bytes3[]" ||
primitiveType == "bytes4" ||
primitiveType == "bytes4[]" ||
primitiveType == "bytes5" ||
primitiveType == "bytes5[]" ||
primitiveType == "bytes6" ||
primitiveType == "bytes6[]" ||
primitiveType == "bytes7" ||
primitiveType == "bytes7[]" ||
primitiveType == "bytes8" ||
primitiveType == "bytes8[]" ||
primitiveType == "bytes9" ||
primitiveType == "bytes9[]" ||
primitiveType == "bytes10" ||
primitiveType == "bytes10[]" ||
primitiveType == "bytes11" ||
primitiveType == "bytes11[]" ||
primitiveType == "bytes12" ||
primitiveType == "bytes12[]" ||
primitiveType == "bytes13" ||
primitiveType == "bytes13[]" ||
primitiveType == "bytes14" ||
primitiveType == "bytes14[]" ||
primitiveType == "bytes15" ||
primitiveType == "bytes15[]" ||
primitiveType == "bytes16" ||
primitiveType == "bytes16[]" ||
primitiveType == "bytes17" ||
primitiveType == "bytes17[]" ||
primitiveType == "bytes18" ||
primitiveType == "bytes18[]" ||
primitiveType == "bytes19" ||
primitiveType == "bytes19[]" ||
primitiveType == "bytes20" ||
primitiveType == "bytes20[]" ||
primitiveType == "bytes21" ||
primitiveType == "bytes21[]" ||
primitiveType == "bytes22" ||
primitiveType == "bytes22[]" ||
primitiveType == "bytes23" ||
primitiveType == "bytes23[]" ||
primitiveType == "bytes24" ||
primitiveType == "bytes24[]" ||
primitiveType == "bytes25" ||
primitiveType == "bytes25[]" ||
primitiveType == "bytes26" ||
primitiveType == "bytes26[]" ||
primitiveType == "bytes27" ||
primitiveType == "bytes27[]" ||
primitiveType == "bytes28" ||
primitiveType == "bytes28[]" ||
primitiveType == "bytes29" ||
primitiveType == "bytes29[]" ||
primitiveType == "bytes30" ||
primitiveType == "bytes30[]" ||
primitiveType == "bytes31" ||
primitiveType == "bytes31[]" {
return true
}
if primitiveType == "int" ||
primitiveType == "int[]" ||
primitiveType == "int8" ||
primitiveType == "int8[]" ||
primitiveType == "int16" ||
primitiveType == "int16[]" ||
primitiveType == "int32" ||
primitiveType == "int32[]" ||
primitiveType == "int64" ||
primitiveType == "int64[]" ||
primitiveType == "int128" ||
primitiveType == "int128[]" ||
primitiveType == "int256" ||
primitiveType == "int256[]" {
return true
}
if primitiveType == "uint" ||
primitiveType == "uint[]" ||
primitiveType == "uint8" ||
primitiveType == "uint8[]" ||
primitiveType == "uint16" ||
primitiveType == "uint16[]" ||
primitiveType == "uint32" ||
primitiveType == "uint32[]" ||
primitiveType == "uint64" ||
primitiveType == "uint64[]" ||
primitiveType == "uint128" ||
primitiveType == "uint128[]" ||
primitiveType == "uint256" ||
primitiveType == "uint256[]" {
return true
}
return false
}
// Validate checks if the given domain is valid, i.e. contains at least
// the minimum viable keys and values
func (domain *TypedDataDomain) Validate() error {
func (domain *TypedDataDomain) validate() error {
if domain.ChainId == big.NewInt(0) {
return errors.New("chainId must be specified according to EIP-155")
}

View file

@ -193,7 +193,7 @@ func TestSignData(t *testing.T) {
t.Errorf("Expected nil-data, got %x", signature)
}
if err != keystore.ErrDecrypt {
t.Errorf("Expected ErrLocked! %v", err)
t.Errorf("Expected ErrLocked! '%v'", err)
}
control <- "No way"
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
@ -201,7 +201,7 @@ func TestSignData(t *testing.T) {
t.Errorf("Expected nil-data, got %x", signature)
}
if err != ErrRequestDenied {
t.Errorf("Expected ErrRequestDenied! %v", err)
t.Errorf("Expected ErrRequestDenied! '%v'", err)
}
// text/plain
control <- "Y"
@ -283,7 +283,7 @@ func TestMalformedDomainkeys(t *testing.T) {
// "chainId": 1,
// "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
//}
var jsonTypedData = `
jsonTypedData := `
{
"types": {
"EIP712Domain": [
@ -348,20 +348,106 @@ func TestMalformedDomainkeys(t *testing.T) {
"contents": "Hello, Bob!"
}
}
`
var malformedDomainTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &malformedDomainTypedData)
if err != nil {
t.Fatalf("unmarshalling failed '%v'", err)
}
_, err = malformedDomainTypedData.HashStruct("EIP712Domain", malformedDomainTypedData.Domain.Map())
if err == nil || err.Error() != "provided data '<nil>' doesn't match type 'address'" {
t.Errorf("Expected `provided data '<nil>' doesn't match type 'address'`, got '%v'", err)
}
}
func TestMalformedTypesAndExtradata(t *testing.T) {
// Verifies several quirks
// 1. Using dynamic types and only validating the prefix:
//{
// "name": "chainId",
// "type": "uint256 ... and now for something completely different"
//}
// 2. Extra data in message:
//{
// "blahonga": "zonk bonk"
//}
jsonTypedData := `
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256 ... and now for something completely different"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"Person": [
{
"name": "name",
"type": "string"
},
{
"name": "wallet",
"type": "address"
}
],
"Mail": [
{
"name": "from",
"type": "Person"
},
{
"name": "to",
"type": "Person"
},
{
"name": "contents",
"type": "string"
}
]
},
"primaryType": "Mail",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": 1,
"verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
"contents": "Hello, Bob!"
}
}
`
var malformedTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
if err != nil {
t.Fatalf("unmarshalling failed %v", err)
t.Fatalf("unmarshalling failed '%v'", err)
}
err = malformedTypedData.Validate()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
if err == nil || err.Error() != "provided data '<nil>' doesn't match type 'address'" {
t.Errorf("Expected `provided data '<nil>' doesn't match type 'address'`, got %v", err)
malformedTypedData.Types["EIP712Domain"][2].Type = "uint256"
malformedTypedData.Message["blahonga"] = "zonk bonk"
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
if err == nil || err.Error() != "there is extra data provided in the message" {
t.Errorf("Expected `there is extra data provided in the message`, got '%v'", err)
}
}
@ -447,173 +533,146 @@ func TestTypeMismatch(t *testing.T) {
}
}
`
var malformedTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
var mismatchTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &mismatchTypedData)
if err != nil {
t.Fatalf("unmarshalling failed %v", err)
t.Fatalf("unmarshalling failed '%v'", err)
}
err = malformedTypedData.Validate()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
if err.Error() != "provided data 'Hello, Bob!' doesn't match type 'Person'" {
t.Errorf("Expected `provided data 'Hello, Bob!' doesn't match type 'Person'`, got %v", err)
t.Errorf("Expected `provided data 'Hello, Bob!' doesn't match type 'Person'`, got '%v'", err)
}
malformedTypedData.Types["Mail"][2].Type = "Blahonga"
err = malformedTypedData.Validate()
mismatchTypedData.Types["Mail"][2].Type = "Blahonga"
_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
if err == nil || err.Error() != "reference type 'Blahonga' is undefined" {
t.Fatalf("Expected `reference type 'Blahonga' is undefined`, got %v", err)
}
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
if err == nil || err.Error() != "unrecognized type 'Blahonga'" {
t.Errorf("Expected `unrecognized type 'Blahonga'`, got %v", err)
t.Fatalf("Expected `reference type 'Blahonga' is undefined`, got '%v'", err)
}
}
func TestMalformedTypesAndExtradata(t *testing.T) {
// Verifies several quirks
// 1. Using dynamic types and only validating the prefix:
//{
// "name": "chainId",
// "type": "uint256 ... and now for something completely different"
//}
// 2. Extra data in message:
//{
// "blahonga": "zonk bonk"
//}
jsonTypedData := `
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256 ... and now for something completely different"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"Person": [
{
"name": "name",
"type": "string"
},
{
"name": "wallet",
"type": "address"
}
],
"Mail": [
{
"name": "from",
"type": "Person"
},
{
"name": "to",
"type": "Person"
},
{
"name": "contents",
"type": "string"
}
]
},
"primaryType": "Mail",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": 1,
"verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
"contents": "Hello, Bob!"
}
}
`
var malformedTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
if err != nil {
t.Fatalf("unmarshalling failed %v", err)
}
err = malformedTypedData.Validate()
if err == nil || err.Error() != "unknown type 'uint256 ... and now for something completely different'" {
t.Fatalf("Expected `unknown type 'uint256 ... and now for something completely different'`, got %v", err)
}
malformedTypedData.Types["EIP712Domain"][2].Type = "uint256"
malformedTypedData.Message["blahonga"] = "zonk bonk"
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
if err == nil || err.Error() != "there is extra data provided in the message" {
t.Errorf("Expected `there is extra data provided in the message`, got %v", err)
}
}
func TestTypeMismatch(t *testing.T) {
func TestTypeOverflow(t *testing.T) {
// Verifies data that doesn't fit into it:
//{
// "test": 65536 <-- test defined as uint8
//}
var malformedTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
var overflowTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &overflowTypedData)
if err != nil {
t.Fatalf("unmarshalling failed %v", err)
t.Fatalf("unmarshalling failed '%v'", err)
}
// Set test to something outside uint8
(malformedTypedData.Message["from"]).(map[string]interface{})["test"] = 65536
(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(65536)
err = malformedTypedData.Validate()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
if err == nil || err.Error() != "integer larger than 'uint8'" {
t.Fatalf("Expected `integer larger than 'uint8'`, got '%v'", err)
}
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
if err == nil || err.Error() != "provided data '65536' doesn't match type 'uint8'" {
t.Fatalf("Expected `provided data '65536' doesn't match type 'uint8'`, got %v", err)
}
(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
(overflowTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
(malformedTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
(malformedTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
if err != nil {
t.Fatalf("Expected no err, got %v", err)
t.Fatalf("Expected no err, got '%v'", err)
}
}
func TestFormatter(t *testing.T) {
func TestArray(t *testing.T) {
// Makes sure that arrays work fine
//{
// "type": "address[]"
//},
//{
// "type": "string[]"
//},
//{
// "type": "uint16[]",
//}
var d TypedData
err := json.Unmarshal([]byte(jsonTypedData), &d)
jsonTypedData := `
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"Foo": [
{
"name": "bar",
"type": "address[]"
}
]
},
"primaryType": "Foo",
"domain": {
"name": "Lorem",
"version": "1",
"chainId": 1,
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"bar": [
"0x0000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000002",
"0x0000000000000000000000000000000000000003"
]
}
}
`
var arrayTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &arrayTypedData)
if err != nil {
t.Fatalf("unmarshalling failed %v", err)
t.Fatalf("unmarshalling failed '%v'", err)
}
formatted := d.Format()
for _, item := range formatted {
fmt.Printf("%v\n", item.Pprint(0))
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
if err != nil {
t.Fatalf("Expected no err, got '%v'", err)
}
j, _ := json.Marshal(formatted)
fmt.Printf("%v\n", string(j))
// Change array to string
arrayTypedData.Types["Foo"][0].Type = "string[]"
arrayTypedData.Message["bar"] = []interface{}{
"lorem",
"ipsum",
"dolores",
}
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
if err != nil {
t.Fatalf("Expected no err, got '%v'", err)
}
// Change array to uint
arrayTypedData.Types["Foo"][0].Type = "uint[]"
arrayTypedData.Message["bar"] = []interface{}{
big.NewInt(1955),
big.NewInt(108),
big.NewInt(44010),
}
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
if err != nil {
t.Fatalf("Expected no err, got '%v'", err)
}
// Should not work with fixed-size arrays
arrayTypedData.Types["Foo"][0].Type = "uint[3]"
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
if err == nil || err.Error() != "unknown type 'uint[3]'" {
t.Fatalf("Expected `unknown type 'uint[3]'`, got '%v'", err)
}
}
func TestCustomTypeAsArray(t *testing.T) {
@ -626,44 +685,45 @@ func TestCustomTypeAsArray(t *testing.T) {
"type": "string"
},
{
"name": "version",
"name": "version",
"type": "string"
},
{
"name": "chainId",
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"name": "verifyingContract",
"type": "address"
}
],
"Person": [
{
"name": "name",
"name": "name",
"type": "string"
},
{
"name": "wallet",
"name": "wallet",
"type": "address"
}
],
"Person[]": [
{
"name": "baz",
"name": "baz",
"type": "string"
}],
}
],
"Mail": [
{
"name": "from",
"name": "from",
"type": "Person"
},
{
"name": "to",
"name": "to",
"type": "Person[]"
},
{
"name": "contents",
"name": "contents",
"type": "string"
}
]
@ -677,8 +737,8 @@ func TestCustomTypeAsArray(t *testing.T) {
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {"baz": "foo"},
"contents": "Hello, Bob!"
@ -689,14 +749,26 @@ func TestCustomTypeAsArray(t *testing.T) {
var malformedTypedData TypedData
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
if err != nil {
t.Fatalf("unmarshalling failed %v", err)
}
err = malformedTypedData.Validate()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
t.Fatalf("unmarshalling failed '%v'", err)
}
_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
if err != nil {
t.Errorf("Expected no error, got %v", err)
t.Errorf("Expected no error, got '%v'", err)
}
}
func TestFormatter(t *testing.T) {
var d TypedData
err := json.Unmarshal([]byte(jsonTypedData), &d)
if err != nil {
t.Fatalf("unmarshalling failed '%v'", err)
}
formatted := d.Format()
for _, item := range formatted {
fmt.Printf("'%v'\n", item.Pprint(0))
}
j, _ := json.Marshal(formatted)
fmt.Printf("'%v'\n", string(j))
}