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
ChrisChinchilla-patch-3
Martin Holst Swende 6 years ago committed by Guillaume Ballet
parent 7a22da98b9
commit 17381ecc66
  1. 35
      cmd/clef/main.go
  2. 7
      common/math/big.go
  3. 1
      signer/core/api.go
  4. 212
      signer/core/signed_data.go
  5. 51
      signer/core/signed_data_internal_test.go
  6. 562
      signer/core/signed_data_test.go
  7. 5
      signer/core/testdata/README.md
  8. 60
      signer/core/testdata/arrays-1.json
  9. 54
      signer/core/testdata/custom_arraytype.json
  10. 76
      signer/core/testdata/eip712.json
  11. 67
      signer/core/testdata/expfail_arraytype_overload.json
  12. 64
      signer/core/testdata/expfail_datamismatch_1.json
  13. 76
      signer/core/testdata/expfail_extradata-1.json
  14. 77
      signer/core/testdata/expfail_extradata-2.json
  15. 64
      signer/core/testdata/expfail_malformeddomainkeys.json
  16. 64
      signer/core/testdata/expfail_nonexistant_type.json
  17. 38
      signer/core/testdata/expfail_toolargeuint.json
  18. 38
      signer/core/testdata/expfail_toolargeuint2.json
  19. 38
      signer/core/testdata/expfail_unconvertiblefloat.json
  20. 38
      signer/core/testdata/expfail_unconvertiblefloat2.json
  21. 38
      signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f
  22. 60
      signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92
  23. 38
      signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d
  24. 1
      signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1
  25. 54
      signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8
  26. 64
      signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d
  27. 1
      signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921

@ -33,6 +33,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
"time"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
@ -638,6 +639,10 @@ func testExternalUI(api *core.SignerAPI) {
errs := make([]string, 0) errs := make([]string, 0)
a := common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef") a := common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef")
addErr := func(errStr string) {
log.Info("Test error", "error", errStr)
errs = append(errs, errStr)
}
queryUser := func(q string) string { queryUser := func(q string) string {
resp, err := api.UI.OnInputRequired(core.UserInputRequest{ resp, err := api.UI.OnInputRequired(core.UserInputRequest{
@ -645,36 +650,39 @@ func testExternalUI(api *core.SignerAPI) {
Prompt: q, Prompt: q,
}) })
if err != nil { if err != nil {
errs = append(errs, err.Error()) addErr(err.Error())
} }
return resp.Text return resp.Text
} }
expectResponse := func(testcase, question, expect string) { expectResponse := func(testcase, question, expect string) {
if got := queryUser(question); got != expect { 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) { expectApprove := func(testcase string, err error) {
if err == nil || err == accounts.ErrUnknownAccount { if err == nil || err == accounts.ErrUnknownAccount {
return 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) { expectDeny := func(testcase string, err error) {
if err == nil || err != core.ErrRequestDenied { 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 // Test display of info and error
{ {
api.UI.ShowInfo("If you see this message, enter 'yes' to next question") 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") expectResponse("showinfo", "Did you see the message? [yes/no]", "yes")
api.UI.ShowError("If you see this message, enter 'yes' to the next question") 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") expectResponse("showerror", "Did you see the message? [yes/no]", "yes")
} }
{ // Sign data test - clique header { // Sign data test - clique header
api.UI.ShowInfo("Please approve the next request for signing a clique header") api.UI.ShowInfo("Please approve the next request for signing a clique header")
time.Sleep(delay)
cliqueHeader := types.Header{ cliqueHeader := types.Header{
common.HexToHash("0000H45H"), common.HexToHash("0000H45H"),
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)) _, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp))
expectApprove("signdata - clique header", err) 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 { // Sign data test - plain text
api.UI.ShowInfo("Please approve the next request for signing text") api.UI.ShowInfo("Please approve the next request for signing text")
time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectApprove("signdata - text", err) expectApprove("signdata - text", err)
} }
{ // Sign data test - plain text reject { // Sign data test - plain text reject
api.UI.ShowInfo("Please deny the next request for signing text") api.UI.ShowInfo("Please deny the next request for signing text")
time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectDeny("signdata - text", err) expectDeny("signdata - text", err)
@ -715,6 +736,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign transaction { // Sign transaction
api.UI.ShowInfo("Please reject next transaction") api.UI.ShowInfo("Please reject next transaction")
time.Sleep(delay)
data := hexutil.Bytes([]byte{}) data := hexutil.Bytes([]byte{})
to := common.NewMixedcaseAddress(a) to := common.NewMixedcaseAddress(a)
tx := core.SendTxArgs{ tx := core.SendTxArgs{
@ -733,16 +755,19 @@ func testExternalUI(api *core.SignerAPI) {
} }
{ // Listing { // Listing
api.UI.ShowInfo("Please reject listing-request") api.UI.ShowInfo("Please reject listing-request")
time.Sleep(delay)
_, err := api.List(ctx) _, err := api.List(ctx)
expectDeny("list", err) expectDeny("list", err)
} }
{ // Import { // Import
api.UI.ShowInfo("Please reject new account-request") api.UI.ShowInfo("Please reject new account-request")
time.Sleep(delay)
_, err := api.New(ctx) _, err := api.New(ctx)
expectDeny("newaccount", err) expectDeny("newaccount", err)
} }
{ // Metadata { // Metadata
api.UI.ShowInfo("Please check if you see the Origin in next listing (approve or deny)") 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")) api.List(context.WithValue(ctx, "Origin", "origin.com"))
expectResponse("metadata - origin", "Did you see origin (origin.com)? [yes/no] ", "yes") expectResponse("metadata - origin", "Did you see origin (origin.com)? [yes/no] ", "yes")
} }

@ -42,6 +42,13 @@ const (
// HexOrDecimal256 marshals big.Int as hex or decimal. // HexOrDecimal256 marshals big.Int as hex or decimal.
type HexOrDecimal256 big.Int 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. // UnmarshalText implements encoding.TextUnmarshaler.
func (i *HexOrDecimal256) UnmarshalText(input []byte) error { func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
bigint, ok := ParseBig256(string(input)) bigint, ok := ParseBig256(string(input))

@ -482,7 +482,6 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
return nil, err return nil, err
} }
} }
req := SignTxRequest{ req := SignTxRequest{
Transaction: args, Transaction: args,
Meta: MetadataFromContext(ctx), Meta: MetadataFromContext(ctx),

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"mime" "mime"
"reflect"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
@ -95,6 +96,9 @@ func (t *Type) typeName() string {
} }
func (t *Type) isReferenceType() bool { func (t *Type) isReferenceType() bool {
if len(t.Type) == 0 {
return false
}
// Reference types must have a leading uppercase characer // Reference types must have a leading uppercase characer
return unicode.IsUpper([]rune(t.Type)[0]) return unicode.IsUpper([]rune(t.Type)[0])
} }
@ -109,11 +113,11 @@ type TypePriority struct {
type TypedDataMessage = map[string]interface{} type TypedDataMessage = map[string]interface{}
type TypedDataDomain struct { type TypedDataDomain struct {
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
ChainId *big.Int `json:"chainId"` ChainId *math.HexOrDecimal256 `json:"chainId"`
VerifyingContract string `json:"verifyingContract"` VerifyingContract string `json:"verifyingContract"`
Salt string `json:"salt"` Salt string `json:"salt"`
} }
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`) 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))) rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
sighash := crypto.Keccak256(rawData) 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} req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Message: message, Hash: sighash}
signature, err := api.sign(addr, req, true) signature, err := api.sign(addr, req, true)
if err != nil { if err != nil {
@ -377,9 +384,11 @@ func (typedData *TypedData) Dependencies(primaryType string, found []string) []s
func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes { func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes {
// Get dependencies primary first, then alphabetical // Get dependencies primary first, then alphabetical
deps := typedData.Dependencies(primaryType, []string{}) deps := typedData.Dependencies(primaryType, []string{})
slicedDeps := deps[1:] if len(deps) > 0 {
sort.Strings(slicedDeps) slicedDeps := deps[1:]
deps = append([]string{primaryType}, slicedDeps...) sort.Strings(slicedDeps)
deps = append([]string{primaryType}, slicedDeps...)
}
// Format as a string with fields // Format as a string with fields
var buffer bytes.Buffer var buffer bytes.Buffer
@ -476,10 +485,60 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
return buffer.Bytes(), nil 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 // EncodePrimitiveValue deals with the primitive values found
// while searching through the typed data // while searching through the typed data
func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) { func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) {
switch encType { switch encType {
case "address": case "address":
stringValue, ok := encValue.(string) 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") { if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
length := 0 b, err := parseInteger(encType, encValue)
if encType == "int" || encType == "uint" { if err != nil {
length = 256 return nil, err
} 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)
} }
return abi.U256(bigIntValue), nil return abi.U256(b), nil
} }
return nil, fmt.Errorf("unrecognized type '%s'", encType) return nil, fmt.Errorf("unrecognized type '%s'", encType)
@ -649,35 +689,32 @@ func (typedData *TypedData) Map() map[string]interface{} {
return dataMap 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 // Format returns a representation of typedData, which can be easily displayed by a user-interface
// without in-depth knowledge about 712 rules // 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 var nvts []*NameValueType
nvts = append(nvts, &NameValueType{ nvts = append(nvts, &NameValueType{
Name: "EIP712Domain", Name: "EIP712Domain",
Value: typedData.formatData("EIP712Domain", typedData.Domain.Map()), Value: domain,
Typ: "domain", Typ: "domain",
}) })
nvts = append(nvts, &NameValueType{ nvts = append(nvts, &NameValueType{
Name: typedData.PrimaryType, Name: typedData.PrimaryType,
Value: typedData.formatData(typedData.PrimaryType, typedData.Message), Value: ptype,
Typ: "primary type", 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 var output []*NameValueType
// Add field contents. Structs and arrays have special handlers. // 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 { for _, v := range arrayValue {
if typedData.Types[parsedType] != nil { if typedData.Types[parsedType] != nil {
mapValue, _ := v.(map[string]interface{}) 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 item.Value = mapOutput
} else { } else {
primitiveOutput := formatPrimitiveValue(field.Type, encValue) primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
if err != nil {
return nil, err
}
item.Value = primitiveOutput item.Value = primitiveOutput
} }
} }
} else if typedData.Types[field.Type] != nil { } else if typedData.Types[field.Type] != nil {
mapValue, _ := encValue.(map[string]interface{}) if mapValue, ok := encValue.(map[string]interface{}); ok {
mapOutput := typedData.formatData(field.Type, mapValue) mapOutput, err := typedData.formatData(field.Type, mapValue)
item.Value = mapOutput if err != nil {
return nil, err
}
item.Value = mapOutput
} else {
item.Value = "<nil>"
}
} else { } else {
primitiveOutput := formatPrimitiveValue(field.Type, encValue) primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
if err != nil {
return nil, err
}
item.Value = primitiveOutput item.Value = primitiveOutput
} }
output = append(output, item) 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 { switch encType {
case "address": case "address":
stringValue, _ := encValue.(string) if stringValue, ok := encValue.(string); !ok {
return common.HexToAddress(stringValue).String() return "", fmt.Errorf("could not format value %v as address", encValue)
} else {
return common.HexToAddress(stringValue).String(), nil
}
case "bool": case "bool":
boolValue, _ := encValue.(bool) if boolValue, ok := encValue.(bool); !ok {
return fmt.Sprintf("%t", boolValue) return "", fmt.Errorf("could not format value %v as bool", encValue)
} else {
return fmt.Sprintf("%t", boolValue), nil
}
case "bytes", "string": case "bytes", "string":
return fmt.Sprintf("%s", encValue) return fmt.Sprintf("%s", encValue), nil
} }
if strings.HasPrefix(encType, "bytes") { if strings.HasPrefix(encType, "bytes") {
return fmt.Sprintf("%s", encValue) return fmt.Sprintf("%s", encValue), nil
} else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
bigIntValue, _ := encValue.(*big.Int) }
return fmt.Sprintf("%d (0x%x)", bigIntValue, bigIntValue) 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 // 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 // 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 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 { if typeKey == typeObj.Type {
return fmt.Errorf("type '%s' cannot reference itself", typeObj.Type) return fmt.Errorf("type '%s' cannot reference itself", typeObj.Type)
} }
if typeObj.isReferenceType() { 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) return fmt.Errorf("reference type '%s' is undefined", typeObj.Type)
} }
if !typedDataReferenceTypeRegexp.MatchString(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 // validate checks if the given domain is valid, i.e. contains at least
// the minimum viable keys and values // the minimum viable keys and values
func (domain *TypedDataDomain) validate() error { 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") return errors.New("chainId must be specified according to EIP-155")
} }

@ -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)
}
}
}

@ -20,12 +20,16 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big" "io/ioutil"
"path"
"strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "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" "github.com/ethereum/go-ethereum/signer/core"
) )
@ -128,7 +132,7 @@ var jsonTypedData = `
"domain": { "domain": {
"name": "Ether Mail", "name": "Ether Mail",
"version": "1", "version": "1",
"chainId": 1, "chainId": "1",
"verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
}, },
"message": { "message": {
@ -151,7 +155,7 @@ const primaryType = "Mail"
var domainStandard = core.TypedDataDomain{ var domainStandard = core.TypedDataDomain{
"Ether Mail", "Ether Mail",
"1", "1",
big.NewInt(1), math.NewHexOrDecimal256(1),
"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
"", "",
} }
@ -241,7 +245,6 @@ func TestDomainChainId(t *testing.T) {
if _, ok := withoutChainID.Domain.Map()["chainId"]; ok { if _, ok := withoutChainID.Domain.Map()["chainId"]; ok {
t.Errorf("Expected the chainId key to not be present in the domain map") t.Errorf("Expected the chainId key to not be present in the domain map")
} }
withChainID := core.TypedData{ withChainID := core.TypedData{
Types: core.Types{ Types: core.Types{
"EIP712Domain": []core.Type{ "EIP712Domain": []core.Type{
@ -251,7 +254,7 @@ func TestDomainChainId(t *testing.T) {
}, },
Domain: core.TypedDataDomain{ Domain: core.TypedDataDomain{
Name: "test", Name: "test",
ChainId: big.NewInt(1), ChainId: math.NewHexOrDecimal256(1),
}, },
} }
@ -310,499 +313,96 @@ func TestEncodeData(t *testing.T) {
} }
} }
func TestMalformedDomainkeys(t *testing.T) { func TestFormatter(t *testing.T) {
// Verifies that malformed domain keys are properly caught: var d core.TypedData
//{ err := json.Unmarshal([]byte(jsonTypedData), &d)
// "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)
if err != nil { if err != nil {
t.Fatalf("unmarshalling failed '%v'", err) t.Fatalf("unmarshalling failed '%v'", err)
} }
// Set test to something outside uint8 formatted, _ := d.Format()
(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(65536) for _, item := range formatted {
fmt.Printf("'%v'\n", item.Pprint(0))
_, 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)
} }
(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3) j, _ := json.Marshal(formatted)
(overflowTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4) fmt.Printf("'%v'\n", string(j))
_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
if err != nil {
t.Fatalf("Expected no err, got '%v'", err)
}
} }
func TestArray(t *testing.T) { func sign(typedData core.TypedData) ([]byte, []byte, error) {
// Makes sure that arrays work fine domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
//{
// "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)
if err != nil { 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 { if err != nil {
t.Fatalf("Expected no err, got '%v'", err) return nil, nil, 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)
} }
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) { func TestJsonFiles(t *testing.T) {
var jsonTypedData = ` testfiles, err := ioutil.ReadDir("testdata/")
{
"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)
if err != nil { if err != nil {
t.Fatalf("unmarshalling failed '%v'", err) t.Fatalf("failed reading files: %v", err)
} }
_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map()) for i, fInfo := range testfiles {
if err != nil { if !strings.HasSuffix(fInfo.Name(), "json") {
t.Errorf("Expected no error, got '%v'", err) 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) { // TestFuzzerFiles tests some files that have been found by fuzzing to cause
var d core.TypedData // crashes or hangs.
err := json.Unmarshal([]byte(jsonTypedData), &d) func TestFuzzerFiles(t *testing.T) {
corpusdir := path.Join("testdata", "fuzzing")
testfiles, err := ioutil.ReadDir(corpusdir)
if err != nil { 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))
} }

@ -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.

@ -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
]
}
}

@ -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!"
}
}

@ -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!"
}
}

@ -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!"
}
}

@ -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!"
}
}

@ -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!"
}
}

@ -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!"
}
}

@ -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!"
}
}

@ -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!"
}
}

@ -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"
}
}

@ -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
}
}

@ -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"
}
}

@ -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
}
}

@ -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"
}
}

@ -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
]
}
}

@ -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
}
}

@ -0,0 +1 @@
{"domain":{"version":"0","chainId":""}}

@ -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!" }
}

@ -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"
},
"": ""
}
}
Loading…
Cancel
Save