mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
Drafted first version of EIP-712, including tests
This commit is contained in:
parent
3fc520c443
commit
653708e59f
5 changed files with 228 additions and 724 deletions
|
|
@ -203,3 +203,22 @@ t=2018-10-21T20:03:51+0100 lvl=info msg=Configured api=signer audit log=audit.lo
|
||||||
t=2018-10-21T20:03:53+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55028\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[type:string name:name] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[type:Person name:from] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]]}"
|
t=2018-10-21T20:03:53+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55028\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[type:string name:name] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[type:Person name:from] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]]}"
|
||||||
t=2018-10-21T20:17:01+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
t=2018-10-21T20:17:01+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
t=2018-10-21T20:17:44+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55221\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[type:address name:wallet]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21]]}"
|
t=2018-10-21T20:17:44+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55221\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[type:address name:wallet]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21]]}"
|
||||||
|
t=2018-10-23T18:55:35+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:36:14+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:38:40+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55416\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[Person:[map[name:name type:string] map[type:address name:wallet]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]] EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
|
||||||
|
t=2018-10-23T19:46:18+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:46:45+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:46:48+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55481\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[from:map[wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 name:Alice] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
|
||||||
|
t=2018-10-23T19:47:53+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:48:10+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:48:14+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55506\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21]]}"
|
||||||
|
t=2018-10-23T19:48:57+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:49:10+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:49:16+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55529\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[type:Person name:to] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[contents:Hello, Bob! from:map[wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 name:Alice] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]]}"
|
||||||
|
t=2018-10-23T19:49:49+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:49:57+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55542\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
|
||||||
|
t=2018-10-23T19:51:44+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:51:57+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T19:52:05+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55568\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]] EIP712Domain:[map[type:string name:name] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]]}"
|
||||||
|
t=2018-10-23T20:12:04+0100 lvl=info msg=Configured api=signer audit log=audit.log
|
||||||
|
t=2018-10-23T20:12:14+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:55752\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66D76e6A80DC7D46d7EC1b79b15Dfa34c3C6eF21 [chksum ok]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[type:string name:contents]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ func initialize(c *cli.Context) error {
|
||||||
// If using the stdioui, we can't do the 'confirm'-flow
|
// If using the stdioui, we can't do the 'confirm'-flow
|
||||||
fmt.Fprintf(logOutput, legalWarning)
|
fmt.Fprintf(logOutput, legalWarning)
|
||||||
} else {
|
} else {
|
||||||
// Temporarily disabled while in development
|
// Temporarily disabled
|
||||||
//if !confirm(legalWarning) {
|
//if !confirm(legalWarning) {
|
||||||
// return fmt.Errorf("aborted by user")
|
// return fmt.Errorf("aborted by user")
|
||||||
//}
|
//}
|
||||||
|
|
@ -558,7 +558,7 @@ func readMasterKey(ctx *cli.Context, ui core.SignerUI) ([]byte, error) {
|
||||||
var password string
|
var password string
|
||||||
// If ui is not nil, get the password from ui.
|
// If ui is not nil, get the password from ui.
|
||||||
if ui != nil {
|
if ui != nil {
|
||||||
// Temporarily disabled while in development
|
// Temporarily disabled
|
||||||
//resp, err := ui.OnInputRequired(core.UserInputRequest{
|
//resp, err := ui.OnInputRequired(core.UserInputRequest{
|
||||||
// Title: "Master Password",
|
// Title: "Master Password",
|
||||||
// Prompt: "Please enter the password to decrypt the master seed",
|
// Prompt: "Please enter the password to decrypt the master seed",
|
||||||
|
|
|
||||||
|
|
@ -245,72 +245,6 @@ func TestNewAcc(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func signTextValidator(t *testing.T) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
func signApplicationClique(t *testing.T) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
func signTextPlain(t *testing.T) {
|
|
||||||
api, control := setup(t)
|
|
||||||
//Create two accounts
|
|
||||||
createAccount(control, api, t)
|
|
||||||
createAccount(control, api, t)
|
|
||||||
control <- "1"
|
|
||||||
list, err := api.List(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
a := common.NewMixedcaseAddress(list[0])
|
|
||||||
|
|
||||||
control <- "Y"
|
|
||||||
control <- "wrongpassword"
|
|
||||||
h, err := api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
|
|
||||||
if h != nil {
|
|
||||||
t.Errorf("Expected nil-data, got %x", h)
|
|
||||||
}
|
|
||||||
if err != keystore.ErrDecrypt {
|
|
||||||
t.Errorf("Expected ErrLocked! %v", err)
|
|
||||||
}
|
|
||||||
control <- "No way"
|
|
||||||
h, err = api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
|
|
||||||
if h != nil {
|
|
||||||
t.Errorf("Expected nil-data, got %x", h)
|
|
||||||
}
|
|
||||||
if err != ErrRequestDenied {
|
|
||||||
t.Errorf("Expected ErrRequestDenied! %v", err)
|
|
||||||
}
|
|
||||||
control <- "Y"
|
|
||||||
control <- "a_long_password"
|
|
||||||
h, err = api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if h == nil || len(h) != 65 {
|
|
||||||
t.Errorf("Expected 65 byte signature (got %d bytes)", len(h))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func signTypedData(t *testing.T) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignData(t *testing.T) {
|
|
||||||
// application/validator or `0x00`
|
|
||||||
signTextValidator(t)
|
|
||||||
|
|
||||||
// data/structured `0x01`
|
|
||||||
signTypedData(t)
|
|
||||||
|
|
||||||
// application/clique or `0x02`
|
|
||||||
signApplicationClique(t)
|
|
||||||
|
|
||||||
// text/plain or `0x45`
|
|
||||||
signTextPlain(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
|
func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
|
||||||
to := common.NewMixedcaseAddress(common.HexToAddress("0x1337"))
|
to := common.NewMixedcaseAddress(common.HexToAddress("0x1337"))
|
||||||
gas := hexutil.Uint64(21000)
|
gas := hexutil.Uint64(21000)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,31 @@
|
||||||
|
// Copyright 2018 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
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"mime"
|
"mime"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
|
@ -44,11 +58,11 @@ type EIP712TypePriority struct {
|
||||||
type EIP712Data = map[string]interface{}
|
type EIP712Data = map[string]interface{}
|
||||||
|
|
||||||
type EIP712Domain struct {
|
type EIP712Domain struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ChainId *big.Int `json:"chainId"`
|
ChainId *big.Int `json:"chainId"`
|
||||||
VerifyingContract common.Address `json:"verifyingContract"`
|
VerifyingContract string `json:"verifyingContract"`
|
||||||
Salt hexutil.Bytes `json:"salt"`
|
Salt hexutil.Bytes `json:"salt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -208,43 +222,26 @@ func signCliqueHeader(header *types.Header) (hexutil.Bytes, error) {
|
||||||
return hash.Bytes(), nil
|
return hash.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTypedData signs EIP712 conformant typed data
|
// SignTypedData signs EIP-712 conformant typed data
|
||||||
// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
|
// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
|
||||||
func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) {
|
func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) {
|
||||||
domainTypes := EIP712Types{
|
domainSeparator := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
|
||||||
"EIP712Domain": typedData.Types["EIP712Domain"],
|
typedDataHash := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
|
||||||
}
|
|
||||||
domainSeparatorBytes := typedData.hashStruct(domainTypes, typedData.Domain.Map(), "EIP712Domain", 0)
|
|
||||||
domainSeparator := common.BytesToHash(domainSeparatorBytes)
|
|
||||||
|
|
||||||
domainlessTypes := make(EIP712Types)
|
|
||||||
for typeKey, typeVal := range typedData.Types {
|
|
||||||
if typeKey == "EIP712Domain" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
domainlessTypes[typeKey] = typeVal
|
|
||||||
}
|
|
||||||
typedDataHashBytes := typedData.hashStruct(domainlessTypes, typedData.Message, typedData.PrimaryType, 0)
|
|
||||||
typedDataHash := common.BytesToHash(typedDataHashBytes)
|
|
||||||
|
|
||||||
typedDataJson, err := json.Marshal(typedData.Map())
|
typedDataJson, err := json.Marshal(typedData.Map())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
printJson("SignTypedData", typedData.Map())
|
|
||||||
fmt.Printf("domainSeparator: %s\n", domainSeparator.String())
|
|
||||||
fmt.Printf("typedDataHash: %s\n\n", typedDataHash.String())
|
|
||||||
|
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
buffer.WriteString("\x19")
|
buffer.WriteString("\x19")
|
||||||
buffer.WriteString(fmt.Sprintf("\x19\\x%x", DataTyped.ByteVersion))
|
buffer.WriteString("\x01")
|
||||||
buffer.Write(domainSeparator.Bytes())
|
buffer.WriteString(common.Bytes2Hex(domainSeparator))
|
||||||
buffer.Write(typedDataHash.Bytes())
|
buffer.WriteString(common.Bytes2Hex(typedDataHash))
|
||||||
|
req := &SignDataRequest{
|
||||||
msg := buffer.String()
|
Rawdata: typedDataJson,
|
||||||
sighash := crypto.Keccak256(buffer.Bytes())
|
Message: buffer.String(),
|
||||||
req := &SignDataRequest{Rawdata: typedDataJson, Message: msg, Hash: sighash, ContentType: DataTyped.Mime}
|
Hash: crypto.Keccak256(buffer.Bytes()),
|
||||||
|
ContentType: DataTyped.Mime,
|
||||||
|
}
|
||||||
signature, err := api.Sign(ctx, addr, req)
|
signature, err := api.Sign(ctx, addr, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.UI.ShowError(err.Error())
|
api.UI.ShowError(err.Error())
|
||||||
|
|
@ -255,199 +252,194 @@ func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAd
|
||||||
|
|
||||||
// hashStruct generates the following encoding for the given domain and message:
|
// hashStruct generates the following encoding for the given domain and message:
|
||||||
// `encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)`
|
// `encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)`
|
||||||
func (typedData *TypedData) hashStruct(_types EIP712Types, data EIP712Data, dataType string, depth int) []byte {
|
func (typedData *TypedData) HashStruct(primaryType string, data EIP712Data) []byte {
|
||||||
typeEncoding := typedData.encodeType(_types)
|
return crypto.Keccak256(typedData.EncodeData(primaryType, data))
|
||||||
typeHash := hex.EncodeToString(crypto.Keccak256(typeEncoding))
|
|
||||||
|
|
||||||
dataEncoding := typedData.encodeData(_types, data, dataType, depth)
|
|
||||||
dataHash := hex.EncodeToString(crypto.Keccak256(dataEncoding))
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
buffer.WriteString(typeHash)
|
|
||||||
buffer.WriteString(dataHash)
|
|
||||||
encoding := crypto.Keccak256(buffer.Bytes())
|
|
||||||
|
|
||||||
if depth == 0 {
|
|
||||||
fmt.Printf("typeEncoding %s\n", common.Bytes2Hex(typeEncoding))
|
|
||||||
fmt.Printf("dataEncoding %s\n", common.Bytes2Hex(dataEncoding))
|
|
||||||
}
|
|
||||||
|
|
||||||
printJson("hashStruct", map[string]interface{}{
|
|
||||||
"depth": depth,
|
|
||||||
"encoding": buffer.String(),
|
|
||||||
})
|
|
||||||
return encoding
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodeType generates the followign encoding:
|
// dependencies returns an array of custom types ordered by their
|
||||||
// `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
|
// hierarchical reference tree
|
||||||
//
|
func (typedData *TypedData) Dependencies(primaryType string, found []string) []string {
|
||||||
// each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name
|
includes := func(arr []string, str string) bool {
|
||||||
func (typedData *TypedData) encodeType(_types EIP712Types) []byte {
|
|
||||||
var priorities = make(map[string]uint)
|
|
||||||
for key := range _types {
|
|
||||||
priorities[key] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the priority for every new custom type discovered
|
|
||||||
update := func(typeKey string, typeVal string) {
|
|
||||||
priorities[typeVal]++
|
|
||||||
|
|
||||||
// Importantly, we also have to check for parent types to increment them too
|
|
||||||
for _, typeObj := range _types[typeVal] {
|
|
||||||
_typeVal := typeObj["type"]
|
|
||||||
|
|
||||||
firstChar := []rune(_typeVal)[0]
|
|
||||||
if unicode.IsUpper(firstChar) {
|
|
||||||
priorities[_typeVal]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if referenced type has already been visited to optimise algo
|
|
||||||
visited := func(arr []string, val string) bool {
|
|
||||||
for _, obj := range arr {
|
for _, obj := range arr {
|
||||||
if obj == val {
|
if obj == str {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for typeKey, typeArr := range _types {
|
if includes(found, primaryType) {
|
||||||
var typeValArr []string
|
return found
|
||||||
for _, typeObj := range typeArr {
|
}
|
||||||
typeVal := typeObj["type"]
|
if typedData.Types[primaryType] == nil {
|
||||||
|
return found
|
||||||
// filtering the structs from the primitives
|
}
|
||||||
if _types[typeVal] != nil && !visited(typeValArr, typeVal) {
|
found = append(found, primaryType)
|
||||||
typeValArr = append(typeValArr, typeVal)
|
for _, field := range typedData.Types[primaryType] {
|
||||||
update(typeKey, typeVal)
|
for _, dep := range typedData.Dependencies(field["type"], found) {
|
||||||
|
if !includes(found, dep) {
|
||||||
|
found = append(found, dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typeValArr = []string{}
|
|
||||||
}
|
}
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
if _types[typedData.PrimaryType] != nil {
|
// encodeType generates the following encoding:
|
||||||
priorities[typedData.PrimaryType] = math.MaxInt32
|
// `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
|
||||||
|
//
|
||||||
|
// each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name
|
||||||
|
func (typedData *TypedData) EncodeType(primaryType string) []byte {
|
||||||
|
// Get dependencies primary first, then alphabetical
|
||||||
|
deps := typedData.Dependencies(primaryType, []string{})
|
||||||
|
for i, dep := range deps {
|
||||||
|
if dep == primaryType {
|
||||||
|
deps = append(deps[:i], deps[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
deps = append([]string{primaryType}, deps...)
|
||||||
|
|
||||||
sortedPriorities := sortByPriorityAndName(priorities)
|
// Format as a string with fields
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
for _, priority := range sortedPriorities {
|
for _, dep := range deps {
|
||||||
typeKey := priority.Type
|
buffer.WriteString(dep)
|
||||||
typeArr := _types[typeKey]
|
|
||||||
|
|
||||||
buffer.WriteString(typeKey)
|
|
||||||
buffer.WriteString("(")
|
buffer.WriteString("(")
|
||||||
|
for _, obj := range typedData.Types[dep] {
|
||||||
for _, typeObj := range typeArr {
|
buffer.WriteString(obj["type"])
|
||||||
buffer.WriteString(typeObj["type"])
|
|
||||||
buffer.WriteString(" ")
|
buffer.WriteString(" ")
|
||||||
buffer.WriteString(typeObj["name"])
|
buffer.WriteString(obj["name"])
|
||||||
buffer.WriteString(",")
|
buffer.WriteString(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.Truncate(buffer.Len() - 1)
|
buffer.Truncate(buffer.Len() - 1)
|
||||||
buffer.WriteString(")")
|
buffer.WriteString(")")
|
||||||
}
|
}
|
||||||
|
|
||||||
printJson("encodeType", map[string]interface{}{
|
|
||||||
"types": _types,
|
|
||||||
"encoding": buffer.String(),
|
|
||||||
})
|
|
||||||
return buffer.Bytes()
|
return buffer.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (typedData *TypedData) TypeHash(primaryType string) []byte {
|
||||||
|
return crypto.Keccak256(typedData.EncodeType(primaryType))
|
||||||
|
}
|
||||||
|
|
||||||
|
func bytesValueOf(_interface interface{}) []byte {
|
||||||
|
bytesVal, ok := _interface.([]byte)
|
||||||
|
if ok {
|
||||||
|
return bytesVal
|
||||||
|
}
|
||||||
|
|
||||||
|
switch reflect.TypeOf(_interface) {
|
||||||
|
case reflect.TypeOf(string("")):
|
||||||
|
return []byte(_interface.(string))
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Errorf("unrecognized interface %v", _interface))
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
// encodeData generates the following encoding:
|
// encodeData generates the following encoding:
|
||||||
// `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
|
// `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
|
||||||
//
|
//
|
||||||
// each encoded member is 32-byte long
|
// each encoded member is 32-byte long
|
||||||
func (typedData *TypedData) encodeData(_types EIP712Types, data interface{}, dataType string, depth int) []byte {
|
func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}) []byte {
|
||||||
var buffer bytes.Buffer
|
encTypes := []string{}
|
||||||
|
encValues := []interface{}{}
|
||||||
|
|
||||||
// TODO regex
|
// Add typehash
|
||||||
// handle arrays
|
encTypes = append(encTypes, "bytes32")
|
||||||
if strings.Contains(dataType, "[]") {
|
encValues = append(encValues, typedData.TypeHash(primaryType))
|
||||||
arrayVal := data.([]interface{})
|
|
||||||
dataType := "TODO"
|
|
||||||
|
|
||||||
var arrayBuffer bytes.Buffer
|
// Handle primitive values
|
||||||
for obj := range arrayVal {
|
handlePrimitiveValue := func(encType string, encValue interface{}) (string, interface{}) {
|
||||||
objEncoding := typedData.encodeData(_types, obj, dataType, depth+1)
|
var primitiveEncType string
|
||||||
arrayBuffer.Write(objEncoding)
|
var primitiveEncValue interface{}
|
||||||
}
|
|
||||||
|
|
||||||
encoding := arrayBuffer.Bytes()
|
switch encType {
|
||||||
buffer.Write(encoding)
|
case "address":
|
||||||
return buffer.Bytes()
|
primitiveEncType = "address"
|
||||||
}
|
bytesValue := []byte{}
|
||||||
|
for i := 0; i < 12; i++ {
|
||||||
// handle maps
|
bytesValue = append(bytesValue, 0)
|
||||||
firstChar := []rune(dataType)[0]
|
|
||||||
if unicode.IsUpper(firstChar) {
|
|
||||||
for mapKey, mapVal := range data.(EIP712Data) {
|
|
||||||
nextDataType := findNextDataType(_types, dataType, mapKey)
|
|
||||||
if reflect.TypeOf(mapVal) == reflect.TypeOf(EIP712Data{}) {
|
|
||||||
data := mapVal.(map[string]interface{})
|
|
||||||
encoding := typedData.hashStruct(_types, data, nextDataType, depth+1)
|
|
||||||
buffer.Write(encoding)
|
|
||||||
} else {
|
|
||||||
encoding := typedData.encodeData(_types, mapVal, nextDataType, depth+1)
|
|
||||||
buffer.Write(encoding)
|
|
||||||
}
|
}
|
||||||
|
foo := common.BytesToAddress([]byte(encValue.(string)))
|
||||||
|
fmt.Println(foo)
|
||||||
|
for _, _byte := range common.BytesToAddress([]byte(encValue.(string))) {
|
||||||
|
bytesValue = append(bytesValue, _byte)
|
||||||
|
}
|
||||||
|
primitiveEncValue = bytesValue
|
||||||
|
break
|
||||||
|
case "bool":
|
||||||
|
primitiveEncType = "uint256"
|
||||||
|
var int64Val int64
|
||||||
|
if encValue.(bool) {
|
||||||
|
int64Val = 1
|
||||||
|
}
|
||||||
|
primitiveEncValue = abi.U256(big.NewInt(int64Val))
|
||||||
|
break
|
||||||
|
case "bytes", "string":
|
||||||
|
primitiveEncType = "bytes32"
|
||||||
|
primitiveEncValue = crypto.Keccak256(bytesValueOf(encValue))
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
if strings.HasPrefix(encType, "bytes") {
|
||||||
|
encTypes = append(encTypes, "bytes32")
|
||||||
|
sizeStr := strings.TrimPrefix(encType, "bytes")
|
||||||
|
size, _ := strconv.Atoi(sizeStr)
|
||||||
|
bytesValue := []byte{}
|
||||||
|
for i := 0; i < 32-size; i++ {
|
||||||
|
bytesValue = append(bytesValue, 0)
|
||||||
|
}
|
||||||
|
for _, _byte := range encValue.([]byte) {
|
||||||
|
bytesValue = append(bytesValue, _byte)
|
||||||
|
}
|
||||||
|
primitiveEncValue = bytesValue
|
||||||
|
} else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
|
||||||
|
primitiveEncType = "uint256"
|
||||||
|
primitiveEncValue = abi.U256(encValue.(*big.Int))
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return buffer.Bytes()
|
return primitiveEncType, primitiveEncValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO regex
|
// Add field contents. Structs and arrays have special handlings.
|
||||||
// handle bytes
|
for _, field := range typedData.Types[primaryType] {
|
||||||
if strings.Contains(dataType, TypeBytes) {
|
encType := field["type"]
|
||||||
bytesVal := data.([]byte)
|
encValue := data[field["name"]]
|
||||||
encoding := crypto.Keccak256(bytesVal)
|
if encType[len(encType)-1:] == "]" {
|
||||||
buffer.Write(encoding)
|
encTypes = append(encTypes, "bytes32")
|
||||||
}
|
parsedType := strings.Split(encType, "[")[0]
|
||||||
|
arrayBuffer := bytes.Buffer{}
|
||||||
// TODO regex
|
for _, item := range encValue.([]interface{}) {
|
||||||
// handle ints
|
if typedData.Types[parsedType] != nil {
|
||||||
if strings.Contains(dataType, TypeInt) {
|
encoding := typedData.EncodeData(parsedType, item.(map[string]interface{}))
|
||||||
encoding := abi.U256(data.(*big.Int)) // not sure if this is big endian order, but it's definitey sign extended to 256 bit because of using the U256 function
|
arrayBuffer.Write(encoding)
|
||||||
buffer.Write(encoding)
|
} else {
|
||||||
return buffer.Bytes()
|
_, encValue := handlePrimitiveValue(encType, encValue)
|
||||||
}
|
arrayBuffer.Write(bytesValueOf(encValue))
|
||||||
|
}
|
||||||
// handle what's left
|
}
|
||||||
switch dataType {
|
encValues = append(encValues, crypto.Keccak256(arrayBuffer.Bytes()))
|
||||||
case TypeAddress:
|
} else if typedData.Types[field["type"]] != nil {
|
||||||
addressVal, _ := data.(common.Address)
|
encTypes = append(encTypes, "bytes32")
|
||||||
encoding := addressVal.Bytes() // hopefully this means uint160 encoding?
|
mapValue := encValue.(map[string]interface{})
|
||||||
buffer.Write(encoding)
|
encValue = crypto.Keccak256(typedData.EncodeData(field["type"], mapValue))
|
||||||
break
|
encValues = append(encValues, encValue)
|
||||||
case TypeBool:
|
} else {
|
||||||
boolVal, _ := data.(bool)
|
primitiveEncType, primitiveEncValue := handlePrimitiveValue(encType, encValue)
|
||||||
var int64Val int64
|
encTypes = append(encTypes, primitiveEncType)
|
||||||
if boolVal {
|
encValues = append(encValues, primitiveEncValue)
|
||||||
int64Val = 1
|
|
||||||
}
|
}
|
||||||
encoding := abi.U256(big.NewInt(int64Val))
|
|
||||||
buffer.Write(encoding)
|
|
||||||
break
|
|
||||||
case TypeString:
|
|
||||||
bytesVal := common.FromHex(data.(string))
|
|
||||||
encoding := crypto.Keccak256(bytesVal)
|
|
||||||
buffer.Write(encoding)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printJson("encodeData", map[string]interface{}{
|
buffer := bytes.Buffer{}
|
||||||
"dataType": dataType,
|
for _, encValue := range encValues {
|
||||||
"data": data,
|
buffer.Write(bytesValueOf(encValue))
|
||||||
"depth": depth,
|
}
|
||||||
"encoding": buffer.String(),
|
|
||||||
})
|
return buffer.Bytes() // https://github.com/ethereumjs/ethereumjs-abi/blob/master/lib/index.js#L336
|
||||||
return buffer.Bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines the content type and then recovers the address associated with the given sig
|
// Determines the content type and then recovers the address associated with the given sig
|
||||||
|
|
@ -488,39 +480,6 @@ func (api *SignerAPI) EcRecover(ctx context.Context, contentType string, data he
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sortByPriorityAndName is a helper function to sort types by priority and name. Priority is calculated b
|
|
||||||
// based upon the number of references.
|
|
||||||
func sortByPriorityAndName(input map[string]uint) []EIP712TypePriority {
|
|
||||||
var priorities []EIP712TypePriority
|
|
||||||
for key, val := range input {
|
|
||||||
priorities = append(priorities, EIP712TypePriority{key, val})
|
|
||||||
}
|
|
||||||
// Alphabetically
|
|
||||||
sort.Slice(priorities, func(i, j int) bool {
|
|
||||||
return priorities[i].Type < priorities[j].Type
|
|
||||||
})
|
|
||||||
// Priority
|
|
||||||
sort.Slice(priorities, func(i, j int) bool {
|
|
||||||
return priorities[i].Value > priorities[j].Value
|
|
||||||
})
|
|
||||||
|
|
||||||
return priorities
|
|
||||||
}
|
|
||||||
|
|
||||||
// findNextDataType
|
|
||||||
// blah blah
|
|
||||||
func findNextDataType(_types EIP712Types, mapType string, mapKey string) string {
|
|
||||||
eip712type := _types[mapType]
|
|
||||||
|
|
||||||
for _, mapObj := range eip712type {
|
|
||||||
if mapObj["name"] == mapKey {
|
|
||||||
return mapObj["type"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON validates the input data
|
// UnmarshalJSON validates the input data
|
||||||
func (typedData *TypedData) UnmarshalJSON(data []byte) error {
|
func (typedData *TypedData) UnmarshalJSON(data []byte) error {
|
||||||
type input struct {
|
type input struct {
|
||||||
|
|
@ -591,6 +550,7 @@ func (types *EIP712Types) IsValid() error {
|
||||||
return fmt.Errorf("referenced type %s is undefined", typeVal)
|
return fmt.Errorf("referenced type %s is undefined", typeVal)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: better type checking
|
||||||
if !isStandardTypeStr(typeVal) {
|
if !isStandardTypeStr(typeVal) {
|
||||||
if (*types)[typeVal] != nil {
|
if (*types)[typeVal] != nil {
|
||||||
return fmt.Errorf("custom type %s must be capitalized", typeVal)
|
return fmt.Errorf("custom type %s must be capitalized", typeVal)
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,14 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"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"
|
"math/big"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var typesStandard = Types{
|
var typesStandard = EIP712Types{
|
||||||
"EIP712Domain": {
|
"EIP712Domain": {
|
||||||
{
|
{
|
||||||
"name": "name",
|
"name": "name",
|
||||||
|
|
@ -73,89 +70,17 @@ var typesStandard = Types{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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": "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"
|
|
||||||
},
|
|
||||||
"contents": "Hello, Bob!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const primaryType = "Mail"
|
const primaryType = "Mail"
|
||||||
|
|
||||||
var domainStandard = TypedDataDomain{
|
var domainStandard = EIP712Domain{
|
||||||
"Ether Mail",
|
"Ether Mail",
|
||||||
"1",
|
"1",
|
||||||
big.NewInt(1),
|
big.NewInt(1),
|
||||||
"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
|
"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
|
||||||
"",
|
nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageStandard = map[string]interface{}{
|
var dataStandard = map[string]interface{}{
|
||||||
"from": map[string]interface{}{
|
"from": map[string]interface{}{
|
||||||
"name": "Cow",
|
"name": "Cow",
|
||||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
|
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
|
||||||
|
|
@ -168,10 +93,10 @@ var messageStandard = map[string]interface{}{
|
||||||
}
|
}
|
||||||
|
|
||||||
var typedData = TypedData{
|
var typedData = TypedData{
|
||||||
Types: typesStandard,
|
typesStandard,
|
||||||
PrimaryType: primaryType,
|
primaryType,
|
||||||
Domain: domainStandard,
|
domainStandard,
|
||||||
Message: messageStandard,
|
dataStandard,
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignData(t *testing.T) {
|
func TestSignData(t *testing.T) {
|
||||||
|
|
@ -188,7 +113,7 @@ func TestSignData(t *testing.T) {
|
||||||
|
|
||||||
control <- "Y"
|
control <- "Y"
|
||||||
control <- "wrongpassword"
|
control <- "wrongpassword"
|
||||||
signature, err := api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
signature, err := api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
|
||||||
if signature != nil {
|
if signature != nil {
|
||||||
t.Errorf("Expected nil-data, got %x", signature)
|
t.Errorf("Expected nil-data, got %x", signature)
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +121,7 @@ func TestSignData(t *testing.T) {
|
||||||
t.Errorf("Expected ErrLocked! %v", err)
|
t.Errorf("Expected ErrLocked! %v", err)
|
||||||
}
|
}
|
||||||
control <- "No way"
|
control <- "No way"
|
||||||
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
|
||||||
if signature != nil {
|
if signature != nil {
|
||||||
t.Errorf("Expected nil-data, got %x", signature)
|
t.Errorf("Expected nil-data, got %x", signature)
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +131,7 @@ func TestSignData(t *testing.T) {
|
||||||
// text/plain
|
// text/plain
|
||||||
control <- "Y"
|
control <- "Y"
|
||||||
control <- "a_long_password"
|
control <- "a_long_password"
|
||||||
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -223,378 +148,44 @@ func TestSignData(t *testing.T) {
|
||||||
if signature == nil || len(signature) != 65 {
|
if signature == nil || len(signature) != 65 {
|
||||||
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
|
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
|
||||||
}
|
}
|
||||||
|
// TODO: test signature r,s,v values
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHashStruct(t *testing.T) {
|
func TestHashStruct(t *testing.T) {
|
||||||
hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
|
mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.HashStruct(typedData.PrimaryType, typedData.Message)))
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
|
|
||||||
if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" {
|
if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" {
|
||||||
t.Errorf("Expected different hashStruct result (got %s)", mainHash)
|
t.Fatal(fmt.Errorf("hashStruct result %s is incorrect", mainHash))
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
|
domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.HashStruct("EIP712Domain", typedData.Domain.Map())))
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
|
|
||||||
if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" {
|
if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" {
|
||||||
t.Errorf("Expected different domain hashStruct result (got %s)", domainHash)
|
t.Fatal(fmt.Errorf("hashStruct result %s is incorrect", domainHash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeType(t *testing.T) {
|
func TestEncodeType(t *testing.T) {
|
||||||
domainTypeEncoding := string(typedData.EncodeType("EIP712Domain"))
|
domainTypeEncoding := string(typedData.EncodeType("EIP712Domain"))
|
||||||
if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" {
|
if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" {
|
||||||
t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding)
|
t.Fatal(fmt.Errorf("encodeType result %s is incorrect", domainTypeEncoding))
|
||||||
}
|
}
|
||||||
|
|
||||||
mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType))
|
mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType))
|
||||||
if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" {
|
if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" {
|
||||||
t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding)
|
t.Fatal(fmt.Errorf("encodeType result %s is incorrect", mailTypeEncoding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTypeHash(t *testing.T) {
|
func TestTypeHash(t *testing.T) {
|
||||||
mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType)))
|
mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType)))
|
||||||
if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" {
|
if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" {
|
||||||
t.Errorf("Expected different typeHash result (got %s)", mailTypeHash)
|
t.Fatal(fmt.Errorf("typeHash result %s is incorrect", mailTypeHash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeData(t *testing.T) {
|
func TestEncodeData(t *testing.T) {
|
||||||
hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0)
|
|
||||||
if err != nil {
|
dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.EncodeData(typedData.PrimaryType, typedData.Message)))
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
|
|
||||||
if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" {
|
if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" {
|
||||||
t.Errorf("Expected different encodeData result (got %s)", dataEncoding)
|
t.Fatal(fmt.Errorf("encodeData result %s is incorrect", dataEncoding))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMalformedData1(t *testing.T) {
|
|
||||||
// Verifies that malformed domain keys are properly caught:
|
|
||||||
//{
|
|
||||||
// "name": "Ether Mail",
|
|
||||||
// "version": "1",
|
|
||||||
// "chainId": 1,
|
|
||||||
// "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
|
||||||
//}
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"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 malformedTypedData TypedData
|
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
|
||||||
}
|
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
|
|
||||||
if err == nil || err.Error() != "provided data '<nil>' doesn't match type 'address'" {
|
|
||||||
t.Errorf("Expected `provided data '<nil>' doesn't match type 'address'`, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMalformedData2(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 malformedTypedData TypedData
|
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
|
||||||
}
|
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.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)
|
|
||||||
}
|
|
||||||
|
|
||||||
malformedTypedData.Types["Mail"][2]["type"] = "Blahonga"
|
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err == nil || err.Error() != "referenced type 'Blahonga' is undefined" {
|
|
||||||
t.Fatalf("Expected `referenced type 'Blahonga' is undefined`, got %v", err)
|
|
||||||
}
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err == nil || err.Error() != "unrecognized type 'Blahonga'" {
|
|
||||||
t.Errorf("Expected `unrecognized type 'Blahonga'`, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMalformedData3(t *testing.T) {
|
|
||||||
// Verifies several quirks
|
|
||||||
// 1. Using dynamic types and only validating the prefix:
|
|
||||||
//{
|
|
||||||
// "name": "chainId",
|
|
||||||
// "type": "uint256 ... and now for something completely different"
|
|
||||||
//}
|
|
||||||
// 2. Extra data in message:
|
|
||||||
//{
|
|
||||||
// "blahonga": "zonk bonk"
|
|
||||||
//}
|
|
||||||
jsonTypedData := `
|
|
||||||
{
|
|
||||||
"types": {
|
|
||||||
"EIP712Domain": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "version",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "chainId",
|
|
||||||
"type": "uint256 ... and now for something completely different"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "verifyingContract",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Person": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "wallet",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Mail": [
|
|
||||||
{
|
|
||||||
"name": "from",
|
|
||||||
"type": "Person"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "to",
|
|
||||||
"type": "Person"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "contents",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"primaryType": "Mail",
|
|
||||||
"domain": {
|
|
||||||
"name": "Ether Mail",
|
|
||||||
"version": "1",
|
|
||||||
"chainId": 1,
|
|
||||||
"verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"from": {
|
|
||||||
"name": "Cow",
|
|
||||||
"wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"name": "Bob",
|
|
||||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
|
||||||
},
|
|
||||||
"contents": "Hello, Bob!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
var malformedTypedData TypedData
|
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
|
||||||
}
|
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err == nil || err.Error() != "unknown atomic type 'uint256 ... and now for something completely different'" {
|
|
||||||
t.Fatalf("Expected `unknown atomic type 'uint256 ... and now for something completely different'`, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
malformedTypedData.Types["EIP712Domain"][2]["type"] = "uint256"
|
|
||||||
malformedTypedData.Message["blahonga"] = "zonk bonk"
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err == nil || err.Error() != "there is extra data provided in the message" {
|
|
||||||
t.Errorf("Expected `there is extra data provided in the message`, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMalformedData4(t *testing.T) {
|
|
||||||
// Verifies data that doesn't fit into it:
|
|
||||||
//{
|
|
||||||
// "test": 65536 <-- test defined as uint8
|
|
||||||
//}
|
|
||||||
var malformedTypedData TypedData
|
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
|
||||||
}
|
|
||||||
// Set test to something outside uint8
|
|
||||||
(malformedTypedData.Message["from"]).(map[string]interface{})["test"] = 65536
|
|
||||||
|
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err == nil || err.Error() != "provided data '65536' doesn't match type 'uint8'" {
|
|
||||||
t.Fatalf("Expected `provided data '65536' doesn't match type 'uint8'`, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
(malformedTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
|
|
||||||
(malformedTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
|
|
||||||
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no err, got %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue