aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Holst Swende <martin@swende.se>2019-06-03 22:56:05 +0800
committerGuillaume Ballet <gballet@gmail.com>2019-06-03 22:56:05 +0800
commit17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a (patch)
treefda58619a484ec51368859549718a0032567d455
parent7a22da98b9f81d206eb65d1fa4f5e773d888bac3 (diff)
downloadgo-tangerine-17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a.tar
go-tangerine-17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a.tar.gz
go-tangerine-17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a.tar.bz2
go-tangerine-17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a.tar.lz
go-tangerine-17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a.tar.xz
go-tangerine-17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a.tar.zst
go-tangerine-17381ecc6695ea9c2d8e5ee0aee5cf70d59a301a.zip
core/signer, clef: improve ui-test flow, fix errors in uint handling (#19584)
* core/signer, clef: improve ui-test flow, fix errors in uint handling for eip-712 * core/signer: add fuzzer testcases + crashfixes * signer: address review concerns, check sign in integer parsing
-rw-r--r--cmd/clef/main.go35
-rw-r--r--common/math/big.go7
-rw-r--r--signer/core/api.go1
-rw-r--r--signer/core/signed_data.go212
-rw-r--r--signer/core/signed_data_internal_test.go51
-rw-r--r--signer/core/signed_data_test.go562
-rw-r--r--signer/core/testdata/README.md5
-rw-r--r--signer/core/testdata/arrays-1.json60
-rw-r--r--signer/core/testdata/custom_arraytype.json54
-rw-r--r--signer/core/testdata/eip712.json76
-rw-r--r--signer/core/testdata/expfail_arraytype_overload.json67
-rw-r--r--signer/core/testdata/expfail_datamismatch_1.json64
-rw-r--r--signer/core/testdata/expfail_extradata-1.json76
-rw-r--r--signer/core/testdata/expfail_extradata-2.json77
-rw-r--r--signer/core/testdata/expfail_malformeddomainkeys.json64
-rw-r--r--signer/core/testdata/expfail_nonexistant_type.json64
-rw-r--r--signer/core/testdata/expfail_toolargeuint.json38
-rw-r--r--signer/core/testdata/expfail_toolargeuint2.json38
-rw-r--r--signer/core/testdata/expfail_unconvertiblefloat.json38
-rw-r--r--signer/core/testdata/expfail_unconvertiblefloat2.json38
-rw-r--r--signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f38
-rw-r--r--signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d9260
-rw-r--r--signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d38
-rw-r--r--signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf11
-rw-r--r--signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce854
-rw-r--r--signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d64
-rw-r--r--signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc309211
27 files changed, 1326 insertions, 557 deletions
diff --git a/cmd/clef/main.go b/cmd/clef/main.go
index fecfcafaf..ad7ba186d 100644
--- a/cmd/clef/main.go
+++ b/cmd/clef/main.go
@@ -33,6 +33,7 @@ import (
"path/filepath"
"runtime"
"strings"
+ "time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
@@ -638,6 +639,10 @@ func testExternalUI(api *core.SignerAPI) {
errs := make([]string, 0)
a := common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef")
+ addErr := func(errStr string) {
+ log.Info("Test error", "error", errStr)
+ errs = append(errs, errStr)
+ }
queryUser := func(q string) string {
resp, err := api.UI.OnInputRequired(core.UserInputRequest{
@@ -645,36 +650,39 @@ func testExternalUI(api *core.SignerAPI) {
Prompt: q,
})
if err != nil {
- errs = append(errs, err.Error())
+ addErr(err.Error())
}
return resp.Text
}
expectResponse := func(testcase, question, expect string) {
if got := queryUser(question); got != expect {
- errs = append(errs, fmt.Sprintf("%s: got %v, expected %v", testcase, got, expect))
+ addErr(fmt.Sprintf("%s: got %v, expected %v", testcase, got, expect))
}
}
expectApprove := func(testcase string, err error) {
if err == nil || err == accounts.ErrUnknownAccount {
return
}
- errs = append(errs, fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error()))
+ addErr(fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error()))
}
expectDeny := func(testcase string, err error) {
if err == nil || err != core.ErrRequestDenied {
- errs = append(errs, fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err))
+ addErr(fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err))
}
}
-
+ var delay = 1 * time.Second
// Test display of info and error
{
api.UI.ShowInfo("If you see this message, enter 'yes' to next question")
+ time.Sleep(delay)
expectResponse("showinfo", "Did you see the message? [yes/no]", "yes")
api.UI.ShowError("If you see this message, enter 'yes' to the next question")
+ time.Sleep(delay)
expectResponse("showerror", "Did you see the message? [yes/no]", "yes")
}
{ // Sign data test - clique header
api.UI.ShowInfo("Please approve the next request for signing a clique header")
+ time.Sleep(delay)
cliqueHeader := types.Header{
common.HexToHash("0000H45H"),
common.HexToHash("0000H45H"),
@@ -700,14 +708,27 @@ func testExternalUI(api *core.SignerAPI) {
_, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp))
expectApprove("signdata - clique header", err)
}
+ { // Sign data test - typed data
+ api.UI.ShowInfo("Please approve the next request for signing EIP-712 typed data")
+ time.Sleep(delay)
+ addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
+ data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"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","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}`
+ //_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data)))
+ var typedData core.TypedData
+ err := json.Unmarshal([]byte(data), &typedData)
+ _, err = api.SignTypedData(ctx, *addr, typedData)
+ expectApprove("sign 712 typed data", err)
+ }
{ // Sign data test - plain text
api.UI.ShowInfo("Please approve the next request for signing text")
+ time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectApprove("signdata - text", err)
}
{ // Sign data test - plain text reject
api.UI.ShowInfo("Please deny the next request for signing text")
+ time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectDeny("signdata - text", err)
@@ -715,6 +736,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign transaction
api.UI.ShowInfo("Please reject next transaction")
+ time.Sleep(delay)
data := hexutil.Bytes([]byte{})
to := common.NewMixedcaseAddress(a)
tx := core.SendTxArgs{
@@ -733,16 +755,19 @@ func testExternalUI(api *core.SignerAPI) {
}
{ // Listing
api.UI.ShowInfo("Please reject listing-request")
+ time.Sleep(delay)
_, err := api.List(ctx)
expectDeny("list", err)
}
{ // Import
api.UI.ShowInfo("Please reject new account-request")
+ time.Sleep(delay)
_, err := api.New(ctx)
expectDeny("newaccount", err)
}
{ // Metadata
api.UI.ShowInfo("Please check if you see the Origin in next listing (approve or deny)")
+ time.Sleep(delay)
api.List(context.WithValue(ctx, "Origin", "origin.com"))
expectResponse("metadata - origin", "Did you see origin (origin.com)? [yes/no] ", "yes")
}
diff --git a/common/math/big.go b/common/math/big.go
index 9d2e7946d..d31c59af1 100644
--- a/common/math/big.go
+++ b/common/math/big.go
@@ -42,6 +42,13 @@ const (
// HexOrDecimal256 marshals big.Int as hex or decimal.
type HexOrDecimal256 big.Int
+// NewHexOrDecimal256 creates a new HexOrDecimal256
+func NewHexOrDecimal256(x int64) *HexOrDecimal256 {
+ b := big.NewInt(x)
+ h := HexOrDecimal256(*b)
+ return &h
+}
+
// UnmarshalText implements encoding.TextUnmarshaler.
func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
bigint, ok := ParseBig256(string(input))
diff --git a/signer/core/api.go b/signer/core/api.go
index 783aaece4..73a94634e 100644
--- a/signer/core/api.go
+++ b/signer/core/api.go
@@ -482,7 +482,6 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
return nil, err
}
}
-
req := SignTxRequest{
Transaction: args,
Meta: MetadataFromContext(ctx),
diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go
index 9dfd7b3f6..27eca9183 100644
--- a/signer/core/signed_data.go
+++ b/signer/core/signed_data.go
@@ -23,6 +23,7 @@ import (
"fmt"
"math/big"
"mime"
+ "reflect"
"regexp"
"sort"
"strconv"
@@ -95,6 +96,9 @@ func (t *Type) typeName() string {
}
func (t *Type) isReferenceType() bool {
+ if len(t.Type) == 0 {
+ return false
+ }
// Reference types must have a leading uppercase characer
return unicode.IsUpper([]rune(t.Type)[0])
}
@@ -109,11 +113,11 @@ type TypePriority struct {
type TypedDataMessage = map[string]interface{}
type TypedDataDomain struct {
- Name string `json:"name"`
- Version string `json:"version"`
- ChainId *big.Int `json:"chainId"`
- VerifyingContract string `json:"verifyingContract"`
- Salt string `json:"salt"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ ChainId *math.HexOrDecimal256 `json:"chainId"`
+ VerifyingContract string `json:"verifyingContract"`
+ Salt string `json:"salt"`
}
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`)
@@ -323,7 +327,10 @@ func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAd
}
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
sighash := crypto.Keccak256(rawData)
- message := typedData.Format()
+ message, err := typedData.Format()
+ if err != nil {
+ return nil, err
+ }
req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Message: message, Hash: sighash}
signature, err := api.sign(addr, req, true)
if err != nil {
@@ -377,9 +384,11 @@ func (typedData *TypedData) Dependencies(primaryType string, found []string) []s
func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes {
// Get dependencies primary first, then alphabetical
deps := typedData.Dependencies(primaryType, []string{})
- slicedDeps := deps[1:]
- sort.Strings(slicedDeps)
- deps = append([]string{primaryType}, slicedDeps...)
+ if len(deps) > 0 {
+ slicedDeps := deps[1:]
+ sort.Strings(slicedDeps)
+ deps = append([]string{primaryType}, slicedDeps...)
+ }
// Format as a string with fields
var buffer bytes.Buffer
@@ -476,10 +485,60 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
return buffer.Bytes(), nil
}
+func parseInteger(encType string, encValue interface{}) (*big.Int, error) {
+ var (
+ length = 0
+ signed = strings.HasPrefix(encType, "int")
+ b *big.Int
+ )
+ if encType == "int" || encType == "uint" {
+ length = 256
+ } else {
+ lengthStr := ""
+ if strings.HasPrefix(encType, "uint") {
+ lengthStr = strings.TrimPrefix(encType, "uint")
+ } else {
+ lengthStr = strings.TrimPrefix(encType, "int")
+ }
+ atoiSize, err := strconv.Atoi(lengthStr)
+ if err != nil {
+ return nil, fmt.Errorf("invalid size on integer: %v", lengthStr)
+ }
+ length = atoiSize
+ }
+ switch v := encValue.(type) {
+ case *math.HexOrDecimal256:
+ b = (*big.Int)(v)
+ case string:
+ var hexIntValue math.HexOrDecimal256
+ if err := hexIntValue.UnmarshalText([]byte(v)); err != nil {
+ return nil, err
+ }
+ b = (*big.Int)(&hexIntValue)
+ case float64:
+ // JSON parses non-strings as float64. Fail if we cannot
+ // convert it losslessly
+ if float64(int64(v)) == v {
+ b = big.NewInt(int64(v))
+ } else {
+ return nil, fmt.Errorf("invalid float value %v for type %v", v, encType)
+ }
+ }
+ if b == nil {
+ return nil, fmt.Errorf("invalid integer value %v/%v for type %v", encValue, reflect.TypeOf(encValue), encType)
+ }
+ if b.BitLen() > length {
+ return nil, fmt.Errorf("integer larger than '%v'", encType)
+ }
+ if !signed && b.Sign() == -1 {
+ return nil, fmt.Errorf("invalid negative value for unsigned type %v", encType)
+ }
+ return b, nil
+}
+
// EncodePrimitiveValue deals with the primitive values found
// while searching through the typed data
func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) {
-
switch encType {
case "address":
stringValue, ok := encValue.(string)
@@ -527,30 +586,11 @@ func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interf
}
}
if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
- length := 0
- if encType == "int" || encType == "uint" {
- length = 256
- } else {
- lengthStr := ""
- if strings.HasPrefix(encType, "uint") {
- lengthStr = strings.TrimPrefix(encType, "uint")
- } else {
- lengthStr = strings.TrimPrefix(encType, "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)
+ b, err := parseInteger(encType, encValue)
+ if err != nil {
+ return nil, err
}
- return abi.U256(bigIntValue), nil
+ return abi.U256(b), nil
}
return nil, fmt.Errorf("unrecognized type '%s'", encType)
@@ -649,35 +689,32 @@ func (typedData *TypedData) Map() map[string]interface{} {
return dataMap
}
-// PrettyPrint generates a nice output to help the users
-// of clef present data in their apps
-func (typedData *TypedData) PrettyPrint() string {
- output := bytes.Buffer{}
- formatted := typedData.Format()
- for _, item := range formatted {
- output.WriteString(fmt.Sprintf("%v\n", item.Pprint(0)))
- }
- return output.String()
-}
-
// Format returns a representation of typedData, which can be easily displayed by a user-interface
// without in-depth knowledge about 712 rules
-func (typedData *TypedData) Format() []*NameValueType {
+func (typedData *TypedData) Format() ([]*NameValueType, error) {
+ domain, err := typedData.formatData("EIP712Domain", typedData.Domain.Map())
+ if err != nil {
+ return nil, err
+ }
+ ptype, err := typedData.formatData(typedData.PrimaryType, typedData.Message)
+ if err != nil {
+ return nil, err
+ }
var nvts []*NameValueType
nvts = append(nvts, &NameValueType{
Name: "EIP712Domain",
- Value: typedData.formatData("EIP712Domain", typedData.Domain.Map()),
+ Value: domain,
Typ: "domain",
})
nvts = append(nvts, &NameValueType{
Name: typedData.PrimaryType,
- Value: typedData.formatData(typedData.PrimaryType, typedData.Message),
+ Value: ptype,
Typ: "primary type",
})
- return nvts
+ return nvts, nil
}
-func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) []*NameValueType {
+func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) ([]*NameValueType, error) {
var output []*NameValueType
// Add field contents. Structs and arrays have special handlers.
@@ -694,44 +731,70 @@ func (typedData *TypedData) formatData(primaryType string, data map[string]inter
for _, v := range arrayValue {
if typedData.Types[parsedType] != nil {
mapValue, _ := v.(map[string]interface{})
- mapOutput := typedData.formatData(parsedType, mapValue)
+ mapOutput, err := typedData.formatData(parsedType, mapValue)
+ if err != nil {
+ return nil, err
+ }
item.Value = mapOutput
} else {
- primitiveOutput := formatPrimitiveValue(field.Type, encValue)
+ primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
+ if err != nil {
+ return nil, err
+ }
item.Value = primitiveOutput
}
}
} else if typedData.Types[field.Type] != nil {
- mapValue, _ := encValue.(map[string]interface{})
- mapOutput := typedData.formatData(field.Type, mapValue)
- item.Value = mapOutput
+ if mapValue, ok := encValue.(map[string]interface{}); ok {
+ mapOutput, err := typedData.formatData(field.Type, mapValue)
+ if err != nil {
+ return nil, err
+ }
+ item.Value = mapOutput
+ } else {
+ item.Value = "<nil>"
+ }
} else {
- primitiveOutput := formatPrimitiveValue(field.Type, encValue)
+ primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
+ if err != nil {
+ return nil, err
+ }
item.Value = primitiveOutput
}
output = append(output, item)
}
- return output
+ return output, nil
}
-func formatPrimitiveValue(encType string, encValue interface{}) string {
+func formatPrimitiveValue(encType string, encValue interface{}) (string, error) {
switch encType {
case "address":
- stringValue, _ := encValue.(string)
- return common.HexToAddress(stringValue).String()
+ if stringValue, ok := encValue.(string); !ok {
+ return "", fmt.Errorf("could not format value %v as address", encValue)
+ } else {
+ return common.HexToAddress(stringValue).String(), nil
+ }
case "bool":
- boolValue, _ := encValue.(bool)
- return fmt.Sprintf("%t", boolValue)
+ if boolValue, ok := encValue.(bool); !ok {
+ return "", fmt.Errorf("could not format value %v as bool", encValue)
+ } else {
+ return fmt.Sprintf("%t", boolValue), nil
+ }
case "bytes", "string":
- return fmt.Sprintf("%s", encValue)
+ return fmt.Sprintf("%s", encValue), nil
}
if strings.HasPrefix(encType, "bytes") {
- return fmt.Sprintf("%s", encValue)
- } else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
- bigIntValue, _ := encValue.(*big.Int)
- return fmt.Sprintf("%d (0x%x)", bigIntValue, bigIntValue)
+ return fmt.Sprintf("%s", encValue), nil
+
+ }
+ if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
+ if b, err := parseInteger(encType, encValue); err != nil {
+ return "", err
+ } else {
+ return fmt.Sprintf("%d (0x%x)", b, b), nil
+ }
}
- return "NA"
+ return "", fmt.Errorf("unhandled type %v", encType)
}
// NameValueType is a very simple struct with Name, Value and Type. It's meant for simple
@@ -762,12 +825,21 @@ func (nvt *NameValueType) Pprint(depth int) string {
// Validate checks if the types object is conformant to the specs
func (t Types) validate() error {
for typeKey, typeArr := range t {
- for _, typeObj := range typeArr {
+ if len(typeKey) == 0 {
+ return fmt.Errorf("empty type key")
+ }
+ for i, typeObj := range typeArr {
+ if len(typeObj.Type) == 0 {
+ return fmt.Errorf("type %v:%d: empty Type", typeKey, i)
+ }
+ if len(typeObj.Name) == 0 {
+ return fmt.Errorf("type %v:%d: empty Name", typeKey, i)
+ }
if typeKey == typeObj.Type {
return fmt.Errorf("type '%s' cannot reference itself", typeObj.Type)
}
if typeObj.isReferenceType() {
- if _, exist := t[typeObj.Type]; !exist {
+ if _, exist := t[typeObj.typeName()]; !exist {
return fmt.Errorf("reference type '%s' is undefined", typeObj.Type)
}
if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
@@ -895,7 +967,7 @@ func isPrimitiveTypeValid(primitiveType string) bool {
// validate checks if the given domain is valid, i.e. contains at least
// the minimum viable keys and values
func (domain *TypedDataDomain) validate() error {
- if domain.ChainId == big.NewInt(0) {
+ if domain.ChainId == nil {
return errors.New("chainId must be specified according to EIP-155")
}
diff --git a/signer/core/signed_data_internal_test.go b/signer/core/signed_data_internal_test.go
new file mode 100644
index 000000000..b81872566
--- /dev/null
+++ b/signer/core/signed_data_internal_test.go
@@ -0,0 +1,51 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+//
+package core
+
+import (
+ "math/big"
+ "testing"
+)
+
+func TestParseInteger(t *testing.T) {
+ for i, tt := range []struct {
+ t string
+ v interface{}
+ exp *big.Int
+ }{
+ {"uint32", "-123", nil},
+ {"int32", "-123", big.NewInt(-123)},
+ {"uint32", "0xff", big.NewInt(0xff)},
+ {"int8", "0xffff", nil},
+ } {
+ res, err := parseInteger(tt.t, tt.v)
+ if tt.exp == nil && res == nil {
+ continue
+ }
+ if tt.exp == nil && res != nil {
+ t.Errorf("test %d, got %v, expected nil", i, res)
+ continue
+ }
+ if tt.exp != nil && res == nil {
+ t.Errorf("test %d, got '%v', expected %v", i, err, tt.exp)
+ continue
+ }
+ if tt.exp.Cmp(res) != 0 {
+ t.Errorf("test %d, got %v expected %v", i, res, tt.exp)
+ }
+ }
+}
diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go
index f7d199422..5a18defaa 100644
--- a/signer/core/signed_data_test.go
+++ b/signer/core/signed_data_test.go
@@ -20,12 +20,16 @@ import (
"context"
"encoding/json"
"fmt"
- "math/big"
+ "io/ioutil"
+ "path"
+ "strings"
"testing"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/signer/core"
)
@@ -128,7 +132,7 @@ var jsonTypedData = `
"domain": {
"name": "Ether Mail",
"version": "1",
- "chainId": 1,
+ "chainId": "1",
"verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
@@ -151,7 +155,7 @@ const primaryType = "Mail"
var domainStandard = core.TypedDataDomain{
"Ether Mail",
"1",
- big.NewInt(1),
+ math.NewHexOrDecimal256(1),
"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
"",
}
@@ -241,7 +245,6 @@ func TestDomainChainId(t *testing.T) {
if _, ok := withoutChainID.Domain.Map()["chainId"]; ok {
t.Errorf("Expected the chainId key to not be present in the domain map")
}
-
withChainID := core.TypedData{
Types: core.Types{
"EIP712Domain": []core.Type{
@@ -251,7 +254,7 @@ func TestDomainChainId(t *testing.T) {
},
Domain: core.TypedDataDomain{
Name: "test",
- ChainId: big.NewInt(1),
+ ChainId: math.NewHexOrDecimal256(1),
},
}
@@ -310,499 +313,96 @@ func TestEncodeData(t *testing.T) {
}
}
-func TestMalformedDomainkeys(t *testing.T) {
- // Verifies that malformed domain keys are properly caught:
- //{
- // "name": "Ether Mail",
- // "version": "1",
- // "chainId": 1,
- // "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
- //}
- jsonTypedData := `
- {
- "types": {
- "EIP712Domain": [
- {
- "name": "name",
- "type": "string"
- },
- {
- "name": "version",
- "type": "string"
- },
- {
- "name": "chainId",
- "type": "uint256"
- },
- {
- "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,
- "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
- },
- "message": {
- "from": {
- "name": "Cow",
- "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
- },
- "to": {
- "name": "Bob",
- "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
- },
- "contents": "Hello, Bob!"
- }
- }
-`
- var malformedDomainTypedData core.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 core.TypedData
- err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
- if err != nil {
- t.Fatalf("unmarshalling failed '%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) {
- // Verifies that:
- // 1. Mismatches between the given type and data, i.e. `Person` and
- // the data item is a string, are properly caught:
- //{
- // "name": "contents",
- // "type": "Person"
- //},
- //{
- // "contents": "Hello, Bob!" <-- string not "Person"
- //}
- // 2. Nonexistent types are properly caught:
- //{
- // "name": "contents",
- // "type": "Blahonga"
- //}
- jsonTypedData := `
- {
- "types": {
- "EIP712Domain": [
- {
- "name": "name",
- "type": "string"
- },
- {
- "name": "version",
- "type": "string"
- },
- {
- "name": "chainId",
- "type": "uint256"
- },
- {
- "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": "Person"
- }
- ]
- },
- "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 mismatchTypedData core.TypedData
- err := json.Unmarshal([]byte(jsonTypedData), &mismatchTypedData)
- if err != nil {
- t.Fatalf("unmarshalling failed '%v'", err)
- }
- _, 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)
- }
-
- 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)
- }
-}
-
-func TestTypeOverflow(t *testing.T) {
- // Verifies data that doesn't fit into it:
- //{
- // "test": 65536 <-- test defined as uint8
- //}
- var overflowTypedData core.TypedData
- err := json.Unmarshal([]byte(jsonTypedData), &overflowTypedData)
+func TestFormatter(t *testing.T) {
+ var d core.TypedData
+ err := json.Unmarshal([]byte(jsonTypedData), &d)
if err != nil {
t.Fatalf("unmarshalling failed '%v'", err)
}
- // Set test to something outside uint8
- (overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(65536)
-
- _, 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)
+ formatted, _ := d.Format()
+ for _, item := range formatted {
+ fmt.Printf("'%v'\n", item.Pprint(0))
}
- (overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
- (overflowTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
-
- _, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
- if err != nil {
- t.Fatalf("Expected no err, got '%v'", err)
- }
+ j, _ := json.Marshal(formatted)
+ fmt.Printf("'%v'\n", string(j))
}
-func TestArray(t *testing.T) {
- // Makes sure that arrays work fine
- //{
- // "type": "address[]"
- //},
- //{
- // "type": "string[]"
- //},
- //{
- // "type": "uint16[]",
- //}
-
- 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 core.TypedData
- err := json.Unmarshal([]byte(jsonTypedData), &arrayTypedData)
+func sign(typedData core.TypedData) ([]byte, []byte, error) {
+ domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
- t.Fatalf("unmarshalling failed '%v'", err)
+ return nil, nil, err
}
- _, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
+ typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
- t.Fatalf("Expected no err, got '%v'", err)
- }
-
- // 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)
+ return nil, nil, err
}
+ rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
+ sighash := crypto.Keccak256(rawData)
+ return typedDataHash, sighash, nil
}
-func TestCustomTypeAsArray(t *testing.T) {
- var jsonTypedData = `
- {
- "types": {
- "EIP712Domain": [
- {
- "name": "name",
- "type": "string"
- },
- {
- "name": "version",
- "type": "string"
- },
- {
- "name": "chainId",
- "type": "uint256"
- },
- {
- "name": "verifyingContract",
- "type": "address"
- }
- ],
- "Person": [
- {
- "name": "name",
- "type": "string"
- },
- {
- "name": "wallet",
- "type": "address"
- }
- ],
- "Person[]": [
- {
- "name": "baz",
- "type": "string"
- }
- ],
- "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": {"baz": "foo"},
- "contents": "Hello, Bob!"
- }
- }
-
-`
- var malformedTypedData core.TypedData
- err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
+func TestJsonFiles(t *testing.T) {
+ testfiles, err := ioutil.ReadDir("testdata/")
if err != nil {
- 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.Fatalf("failed reading files: %v", err)
+ }
+ for i, fInfo := range testfiles {
+ if !strings.HasSuffix(fInfo.Name(), "json") {
+ continue
+ }
+ expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail")
+ data, err := ioutil.ReadFile(path.Join("testdata", fInfo.Name()))
+ if err != nil {
+ t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
+ continue
+ }
+ var typedData core.TypedData
+ err = json.Unmarshal([]byte(data), &typedData)
+ if err != nil {
+ t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err)
+ continue
+ }
+ _, _, err = sign(typedData)
+ fmt.Printf("Error %v\n", err)
+ if err != nil && !expectedFailure {
+ t.Errorf("Test %d failed, file %v: %v", i, fInfo.Name(), err)
+ }
+ if expectedFailure && err == nil {
+ t.Errorf("Test %d succeeded (expected failure), file %v: %v", i, fInfo.Name(), err)
+ }
}
}
-func TestFormatter(t *testing.T) {
- var d core.TypedData
- err := json.Unmarshal([]byte(jsonTypedData), &d)
+// TestFuzzerFiles tests some files that have been found by fuzzing to cause
+// crashes or hangs.
+func TestFuzzerFiles(t *testing.T) {
+ corpusdir := path.Join("testdata", "fuzzing")
+ testfiles, err := ioutil.ReadDir(corpusdir)
if err != nil {
- t.Fatalf("unmarshalling failed '%v'", err)
+ t.Fatalf("failed reading files: %v", err)
+ }
+ verbose := false
+ for i, fInfo := range testfiles {
+ data, err := ioutil.ReadFile(path.Join(corpusdir, fInfo.Name()))
+ if err != nil {
+ t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
+ continue
+ }
+ var typedData core.TypedData
+ err = json.Unmarshal([]byte(data), &typedData)
+ if err != nil {
+ t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err)
+ continue
+ }
+ _, err = typedData.EncodeData("EIP712Domain", typedData.Domain.Map(), 1)
+ if verbose && err != nil {
+ fmt.Printf("%d, EncodeData[1] err: %v\n", i, err)
+ }
+ _, err = typedData.EncodeData(typedData.PrimaryType, typedData.Message, 1)
+ if verbose && err != nil {
+ fmt.Printf("%d, EncodeData[2] err: %v\n", i, err)
+ }
+ typedData.Format()
}
- formatted := d.Format()
- for _, item := range formatted {
- fmt.Printf("'%v'\n", item.Pprint(0))
- }
-
- j, _ := json.Marshal(formatted)
- fmt.Printf("'%v'\n", string(j))
}
diff --git a/signer/core/testdata/README.md b/signer/core/testdata/README.md
new file mode 100644
index 000000000..f425450a0
--- /dev/null
+++ b/signer/core/testdata/README.md
@@ -0,0 +1,5 @@
+### EIP 712 tests
+
+These tests are json files which are converted into eip-712 typed data.
+All files are expected to be proper json, and tests will fail if they are not.
+Files that begin with `expfail' are expected to not pass the hashstruct construction.
diff --git a/signer/core/testdata/arrays-1.json b/signer/core/testdata/arrays-1.json
new file mode 100644
index 000000000..fea82b42c
--- /dev/null
+++ b/signer/core/testdata/arrays-1.json
@@ -0,0 +1,60 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Foo": [
+ {
+ "name": "addys",
+ "type": "address[]"
+ },
+ {
+ "name": "stringies",
+ "type": "string[]"
+ },
+ {
+ "name": "inties",
+ "type": "uint[]"
+ }
+ ]
+ },
+ "primaryType": "Foo",
+ "domain": {
+ "name": "Lorem",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "addys": [
+ "0x0000000000000000000000000000000000000001",
+ "0x0000000000000000000000000000000000000002",
+ "0x0000000000000000000000000000000000000003"
+ ],
+ "stringies": [
+ "lorem",
+ "ipsum",
+ "dolores"
+ ],
+ "inties": [
+ "0x0000000000000000000000000000000000000001",
+ "3",
+ 4.0
+ ]
+ }
+}
diff --git a/signer/core/testdata/custom_arraytype.json b/signer/core/testdata/custom_arraytype.json
new file mode 100644
index 000000000..078de88c2
--- /dev/null
+++ b/signer/core/testdata/custom_arraytype.json
@@ -0,0 +1,54 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Person": [
+ {
+ "name": "name",
+ "type": "string"
+ }
+ ],
+ "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"},
+ "to": [{ "name": "Moose"},{ "name": "Goose"}],
+ "contents": "Hello, Bob!"
+ }
+}
diff --git a/signer/core/testdata/eip712.json b/signer/core/testdata/eip712.json
new file mode 100644
index 000000000..7b1cb8ae2
--- /dev/null
+++ b/signer/core/testdata/eip712.json
@@ -0,0 +1,76 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Person": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "test",
+ "type": "uint8"
+ },
+ {
+ "name": "test2",
+ "type": "uint8"
+ },
+ {
+ "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",
+ "test": "3",
+ "test2": 5.0,
+ "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+ },
+ "to": {
+ "name": "Bob",
+ "test": "0",
+ "test2": 5,
+ "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+ },
+ "contents": "Hello, Bob!"
+ }
+}
diff --git a/signer/core/testdata/expfail_arraytype_overload.json b/signer/core/testdata/expfail_arraytype_overload.json
new file mode 100644
index 000000000..786487f10
--- /dev/null
+++ b/signer/core/testdata/expfail_arraytype_overload.json
@@ -0,0 +1,67 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Person": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "wallet",
+ "type": "address"
+ }
+ ],
+ "Person[]": [
+ {
+ "name": "baz",
+ "type": "string"
+ }
+ ],
+ "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": {"baz": "foo"},
+ "contents": "Hello, Bob!"
+ }
+}
diff --git a/signer/core/testdata/expfail_datamismatch_1.json b/signer/core/testdata/expfail_datamismatch_1.json
new file mode 100644
index 000000000..d19d470d1
--- /dev/null
+++ b/signer/core/testdata/expfail_datamismatch_1.json
@@ -0,0 +1,64 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "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": "Person"
+ }
+ ]
+ },
+ "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!"
+ }
+}
diff --git a/signer/core/testdata/expfail_extradata-1.json b/signer/core/testdata/expfail_extradata-1.json
new file mode 100644
index 000000000..fd704209b
--- /dev/null
+++ b/signer/core/testdata/expfail_extradata-1.json
@@ -0,0 +1,76 @@
+{
+ "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": "test",
+ "type": "uint8"
+ },
+ {
+ "name": "test2",
+ "type": "uint8"
+ },
+ {
+ "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",
+ "test": "3",
+ "test2": 5.0,
+ "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+ },
+ "to": {
+ "name": "Bob",
+ "test": "0",
+ "test2": 5,
+ "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+ },
+ "contents": "Hello, Bob!"
+ }
+}
diff --git a/signer/core/testdata/expfail_extradata-2.json b/signer/core/testdata/expfail_extradata-2.json
new file mode 100644
index 000000000..10f91c23a
--- /dev/null
+++ b/signer/core/testdata/expfail_extradata-2.json
@@ -0,0 +1,77 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Person": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "test",
+ "type": "uint8"
+ },
+ {
+ "name": "test2",
+ "type": "uint8"
+ },
+ {
+ "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": {
+ "blahonga": "zonk bonk",
+ "from": {
+ "name": "Cow",
+ "test": "3",
+ "test2": 5.0,
+ "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+ },
+ "to": {
+ "name": "Bob",
+ "test": "0",
+ "test2": 5,
+ "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+ },
+ "contents": "Hello, Bob!"
+ }
+}
diff --git a/signer/core/testdata/expfail_malformeddomainkeys.json b/signer/core/testdata/expfail_malformeddomainkeys.json
new file mode 100644
index 000000000..354b3cc85
--- /dev/null
+++ b/signer/core/testdata/expfail_malformeddomainkeys.json
@@ -0,0 +1,64 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "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",
+ "vFAILFAILerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "from": {
+ "name": "Cow",
+ "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+ },
+ "to": {
+ "name": "Bob",
+ "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+ },
+ "contents": "Hello, Bob!"
+ }
+}
diff --git a/signer/core/testdata/expfail_nonexistant_type.json b/signer/core/testdata/expfail_nonexistant_type.json
new file mode 100644
index 000000000..d06bc20b9
--- /dev/null
+++ b/signer/core/testdata/expfail_nonexistant_type.json
@@ -0,0 +1,64 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "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": "Blahonga"
+ }
+ ]
+ },
+ "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!"
+ }
+}
diff --git a/signer/core/testdata/expfail_toolargeuint.json b/signer/core/testdata/expfail_toolargeuint.json
new file mode 100644
index 000000000..9854b65b1
--- /dev/null
+++ b/signer/core/testdata/expfail_toolargeuint.json
@@ -0,0 +1,38 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Mail": [
+ {
+ "name": "test",
+ "type": "uint8"
+ }
+ ]
+ },
+ "primaryType": "Mail",
+ "domain": {
+ "name": "Ether Mail",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "test":"257"
+ }
+}
diff --git a/signer/core/testdata/expfail_toolargeuint2.json b/signer/core/testdata/expfail_toolargeuint2.json
new file mode 100644
index 000000000..c63ed41f9
--- /dev/null
+++ b/signer/core/testdata/expfail_toolargeuint2.json
@@ -0,0 +1,38 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Mail": [
+ {
+ "name": "test",
+ "type": "uint8"
+ }
+ ]
+ },
+ "primaryType": "Mail",
+ "domain": {
+ "name": "Ether Mail",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "test":257
+ }
+}
diff --git a/signer/core/testdata/expfail_unconvertiblefloat.json b/signer/core/testdata/expfail_unconvertiblefloat.json
new file mode 100644
index 000000000..8229a333c
--- /dev/null
+++ b/signer/core/testdata/expfail_unconvertiblefloat.json
@@ -0,0 +1,38 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Mail": [
+ {
+ "name": "test",
+ "type": "uint8"
+ }
+ ]
+ },
+ "primaryType": "Mail",
+ "domain": {
+ "name": "Ether Mail",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "test":"255.3"
+ }
+}
diff --git a/signer/core/testdata/expfail_unconvertiblefloat2.json b/signer/core/testdata/expfail_unconvertiblefloat2.json
new file mode 100644
index 000000000..59e6d38d2
--- /dev/null
+++ b/signer/core/testdata/expfail_unconvertiblefloat2.json
@@ -0,0 +1,38 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Mail": [
+ {
+ "name": "test",
+ "type": "uint8"
+ }
+ ]
+ },
+ "primaryType": "Mail",
+ "domain": {
+ "name": "Ether Mail",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "test": 255.3
+ }
+}
diff --git a/signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f b/signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f
new file mode 100644
index 000000000..8229a333c
--- /dev/null
+++ b/signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f
@@ -0,0 +1,38 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Mail": [
+ {
+ "name": "test",
+ "type": "uint8"
+ }
+ ]
+ },
+ "primaryType": "Mail",
+ "domain": {
+ "name": "Ether Mail",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "test":"255.3"
+ }
+}
diff --git a/signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 b/signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92
new file mode 100644
index 000000000..fea82b42c
--- /dev/null
+++ b/signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92
@@ -0,0 +1,60 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Foo": [
+ {
+ "name": "addys",
+ "type": "address[]"
+ },
+ {
+ "name": "stringies",
+ "type": "string[]"
+ },
+ {
+ "name": "inties",
+ "type": "uint[]"
+ }
+ ]
+ },
+ "primaryType": "Foo",
+ "domain": {
+ "name": "Lorem",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "addys": [
+ "0x0000000000000000000000000000000000000001",
+ "0x0000000000000000000000000000000000000002",
+ "0x0000000000000000000000000000000000000003"
+ ],
+ "stringies": [
+ "lorem",
+ "ipsum",
+ "dolores"
+ ],
+ "inties": [
+ "0x0000000000000000000000000000000000000001",
+ "3",
+ 4.0
+ ]
+ }
+}
diff --git a/signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d b/signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d
new file mode 100644
index 000000000..c63ed41f9
--- /dev/null
+++ b/signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d
@@ -0,0 +1,38 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Mail": [
+ {
+ "name": "test",
+ "type": "uint8"
+ }
+ ]
+ },
+ "primaryType": "Mail",
+ "domain": {
+ "name": "Ether Mail",
+ "version": "1",
+ "chainId": "1",
+ "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "test":257
+ }
+}
diff --git a/signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 b/signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1
new file mode 100644
index 000000000..9bc43938d
--- /dev/null
+++ b/signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1
@@ -0,0 +1 @@
+{"domain":{"version":"0","chainId":""}} \ No newline at end of file
diff --git a/signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 b/signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8
new file mode 100644
index 000000000..fe27de916
--- /dev/null
+++ b/signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8
@@ -0,0 +1,54 @@
+{ "types": { "":[ {
+ "name": "name",
+ "type":"string" },
+ {
+ "name":"version",
+ "type": "string" }, {
+ "name": "chaiI",
+ "type":"uint256 . ad nowretig omeedifere" }, {
+ "ae": "eifinC",
+ "ty":"dess"
+ }
+ ],
+ "Person":[
+ {
+ "name":"name",
+ "type": "string"
+ }, {
+ "name":"tes", "type":"it8"
+ },
+ { "name":"t", "tye":"uit8"
+ },
+ {
+ "a":"ale",
+ "type": "ress"
+ }
+ ],
+ "Mail": [
+ {
+ "name":"from", "type":"Person" },
+ {
+ "name": "to", "type": "Person"
+ },
+ {
+ "name": "contents",
+ "type": "string"
+ }
+ ]
+ }, "primaryType": "Mail",
+ "domain": {
+"name":"theMail", "version": "1",
+ "chainId": "1",
+ "verifyingntract": "0xCcccCCCcCCCCCCCcCCcCCCcCcccccC"
+ },
+ "message": { "from": {
+ "name": "Cow",
+ "test": "3",
+ "est2":5.0,
+ "llt": "0xcD2a3938E13D947E0bE734DfDD86" }, "to": { "name": "Bob",
+ "ts":"",
+ "tet2": 5,
+ "allet": "0bBBBBbbBBbbbbBbbBbbbbBBBbB"
+ },
+ "contents": "Hello, Bob!" }
+} \ No newline at end of file
diff --git a/signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d b/signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d
new file mode 100644
index 000000000..c5e14b39e
--- /dev/null
+++ b/signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d
@@ -0,0 +1,64 @@
+{
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "int"
+ },
+ {
+ "name": "verifyingContract",
+ "type": "address"
+ }
+ ],
+ "Person": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "wallet",
+ "type": "address"
+ }
+ ],
+ "Mail": [
+ {
+ "name": "from",
+ "type": "Person"
+ },
+ {
+ "name": "to",
+ "type": "Mail"
+ },
+ {
+ "name": "s",
+ "type": "Person"
+ }
+ ]
+ },
+ "primaryType": "Mail",
+ "domain": {
+ "name": "l",
+ "version": "1",
+ "chainId": "",
+ "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+ },
+ "message": {
+ "from": {
+ "name": "",
+ "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+ },
+ "to": {
+ "name": "",
+ "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+ },
+ "": ""
+ }
+}
diff --git a/signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 b/signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921
new file mode 100644
index 000000000..c4841cb07
--- /dev/null
+++ b/signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921
@@ -0,0 +1 @@
+{"types":{"0":[{}]}} \ No newline at end of file