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
This commit is contained in:
Martin Holst Swende 2019-06-03 16:56:05 +02:00 committed by Guillaume Ballet
parent 7a22da98b9
commit 17381ecc66
27 changed files with 1333 additions and 564 deletions

View File

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

View File

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

View File

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

View File

@ -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])
} }
@ -111,7 +115,7 @@ 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"`
} }
@ -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{})
if len(deps) > 0 {
slicedDeps := deps[1:] slicedDeps := deps[1:]
sort.Strings(slicedDeps) sort.Strings(slicedDeps)
deps = append([]string{primaryType}, 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" {
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 { if err != nil {
return nil, fmt.Errorf("invalid size on integer: %v", lengthStr) return nil, err
} }
length = atoiSize return abi.U256(b), nil
}
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 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)
if err != nil {
return nil, err
}
item.Value = mapOutput item.Value = mapOutput
} else { } else {
primitiveOutput := formatPrimitiveValue(field.Type, encValue) item.Value = "<nil>"
}
} else {
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)
} }
return "NA" 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 "", 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")
} }

View File

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

View File

@ -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,495 +313,13 @@ 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)
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)
}
(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)
}
}
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)
if err != nil {
t.Fatalf("unmarshalling failed '%v'", err)
}
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.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)
}
}
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)
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)
}
}
func TestFormatter(t *testing.T) { func TestFormatter(t *testing.T) {
var d core.TypedData var d core.TypedData
err := json.Unmarshal([]byte(jsonTypedData), &d) err := json.Unmarshal([]byte(jsonTypedData), &d)
if err != nil { if err != nil {
t.Fatalf("unmarshalling failed '%v'", err) t.Fatalf("unmarshalling failed '%v'", err)
} }
formatted := d.Format() formatted, _ := d.Format()
for _, item := range formatted { for _, item := range formatted {
fmt.Printf("'%v'\n", item.Pprint(0)) fmt.Printf("'%v'\n", item.Pprint(0))
} }
@ -806,3 +327,82 @@ func TestFormatter(t *testing.T) {
j, _ := json.Marshal(formatted) j, _ := json.Marshal(formatted)
fmt.Printf("'%v'\n", string(j)) fmt.Printf("'%v'\n", string(j))
} }
func sign(typedData core.TypedData) ([]byte, []byte, error) {
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
return nil, nil, err
}
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
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 TestJsonFiles(t *testing.T) {
testfiles, err := ioutil.ReadDir("testdata/")
if err != nil {
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)
}
}
}
// 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("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()
}
}

5
signer/core/testdata/README.md vendored Normal file
View File

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

60
signer/core/testdata/arrays-1.json vendored Normal file
View File

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

View File

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

76
signer/core/testdata/eip712.json vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
{"types":{"0":[{}]}}