github.com/CryptoKass/go-ethereum@v1.9.8-0.20191108085857-de2259d27c75/signer/core/signed_data_test.go (about)

     1  // Copyright 2019 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core_test
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"path"
    25  	"strings"
    26  	"testing"
    27  
    28  	"github.com/ethereum/go-ethereum/accounts/keystore"
    29  	"github.com/ethereum/go-ethereum/common"
    30  	"github.com/ethereum/go-ethereum/common/hexutil"
    31  	"github.com/ethereum/go-ethereum/common/math"
    32  	"github.com/ethereum/go-ethereum/crypto"
    33  	"github.com/ethereum/go-ethereum/signer/core"
    34  )
    35  
    36  var typesStandard = core.Types{
    37  	"EIP712Domain": {
    38  		{
    39  			Name: "name",
    40  			Type: "string",
    41  		},
    42  		{
    43  			Name: "version",
    44  			Type: "string",
    45  		},
    46  		{
    47  			Name: "chainId",
    48  			Type: "uint256",
    49  		},
    50  		{
    51  			Name: "verifyingContract",
    52  			Type: "address",
    53  		},
    54  	},
    55  	"Person": {
    56  		{
    57  			Name: "name",
    58  			Type: "string",
    59  		},
    60  		{
    61  			Name: "wallet",
    62  			Type: "address",
    63  		},
    64  	},
    65  	"Mail": {
    66  		{
    67  			Name: "from",
    68  			Type: "Person",
    69  		},
    70  		{
    71  			Name: "to",
    72  			Type: "Person",
    73  		},
    74  		{
    75  			Name: "contents",
    76  			Type: "string",
    77  		},
    78  	},
    79  }
    80  
    81  var jsonTypedData = `
    82      {
    83        "types": {
    84          "EIP712Domain": [
    85            {
    86              "name": "name",
    87              "type": "string"
    88            },
    89            {
    90              "name": "version",
    91              "type": "string"
    92            },
    93            {
    94              "name": "chainId",
    95              "type": "uint256"
    96            },
    97            {
    98              "name": "verifyingContract",
    99              "type": "address"
   100            }
   101          ],
   102          "Person": [
   103            {
   104              "name": "name",
   105              "type": "string"
   106            },
   107            {
   108              "name": "test",
   109              "type": "uint8"
   110            },
   111            {
   112              "name": "wallet",
   113              "type": "address"
   114            }
   115          ],
   116          "Mail": [
   117            {
   118              "name": "from",
   119              "type": "Person"
   120            },
   121            {
   122              "name": "to",
   123              "type": "Person"
   124            },
   125            {
   126              "name": "contents",
   127              "type": "string"
   128            }
   129          ]
   130        },
   131        "primaryType": "Mail",
   132        "domain": {
   133          "name": "Ether Mail",
   134          "version": "1",
   135          "chainId": "1",
   136          "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   137        },
   138        "message": {
   139          "from": {
   140            "name": "Cow",
   141  		  "test": 3,
   142            "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
   143          },
   144          "to": {
   145            "name": "Bob",
   146            "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
   147          },
   148          "contents": "Hello, Bob!"
   149        }
   150      }
   151  `
   152  
   153  const primaryType = "Mail"
   154  
   155  var domainStandard = core.TypedDataDomain{
   156  	"Ether Mail",
   157  	"1",
   158  	math.NewHexOrDecimal256(1),
   159  	"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
   160  	"",
   161  }
   162  
   163  var messageStandard = map[string]interface{}{
   164  	"from": map[string]interface{}{
   165  		"name":   "Cow",
   166  		"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
   167  	},
   168  	"to": map[string]interface{}{
   169  		"name":   "Bob",
   170  		"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
   171  	},
   172  	"contents": "Hello, Bob!",
   173  }
   174  
   175  var typedData = core.TypedData{
   176  	Types:       typesStandard,
   177  	PrimaryType: primaryType,
   178  	Domain:      domainStandard,
   179  	Message:     messageStandard,
   180  }
   181  
   182  func TestSignData(t *testing.T) {
   183  	api, control := setup(t)
   184  	//Create two accounts
   185  	createAccount(control, api, t)
   186  	createAccount(control, api, t)
   187  	control.approveCh <- "1"
   188  	list, err := api.List(context.Background())
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  	a := common.NewMixedcaseAddress(list[0])
   193  
   194  	control.approveCh <- "Y"
   195  	control.inputCh <- "wrongpassword"
   196  	signature, err := api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
   197  	if signature != nil {
   198  		t.Errorf("Expected nil-data, got %x", signature)
   199  	}
   200  	if err != keystore.ErrDecrypt {
   201  		t.Errorf("Expected ErrLocked! '%v'", err)
   202  	}
   203  	control.approveCh <- "No way"
   204  	signature, err = api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
   205  	if signature != nil {
   206  		t.Errorf("Expected nil-data, got %x", signature)
   207  	}
   208  	if err != core.ErrRequestDenied {
   209  		t.Errorf("Expected ErrRequestDenied! '%v'", err)
   210  	}
   211  	// text/plain
   212  	control.approveCh <- "Y"
   213  	control.inputCh <- "a_long_password"
   214  	signature, err = api.SignData(context.Background(), core.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	if signature == nil || len(signature) != 65 {
   219  		t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
   220  	}
   221  	// data/typed
   222  	control.approveCh <- "Y"
   223  	control.inputCh <- "a_long_password"
   224  	signature, err = api.SignTypedData(context.Background(), a, typedData)
   225  	if err != nil {
   226  		t.Fatal(err)
   227  	}
   228  	if signature == nil || len(signature) != 65 {
   229  		t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
   230  	}
   231  }
   232  
   233  func TestDomainChainId(t *testing.T) {
   234  	withoutChainID := core.TypedData{
   235  		Types: core.Types{
   236  			"EIP712Domain": []core.Type{
   237  				{Name: "name", Type: "string"},
   238  			},
   239  		},
   240  		Domain: core.TypedDataDomain{
   241  			Name: "test",
   242  		},
   243  	}
   244  
   245  	if _, ok := withoutChainID.Domain.Map()["chainId"]; ok {
   246  		t.Errorf("Expected the chainId key to not be present in the domain map")
   247  	}
   248  	withChainID := core.TypedData{
   249  		Types: core.Types{
   250  			"EIP712Domain": []core.Type{
   251  				{Name: "name", Type: "string"},
   252  				{Name: "chainId", Type: "uint256"},
   253  			},
   254  		},
   255  		Domain: core.TypedDataDomain{
   256  			Name:    "test",
   257  			ChainId: math.NewHexOrDecimal256(1),
   258  		},
   259  	}
   260  
   261  	if _, ok := withChainID.Domain.Map()["chainId"]; !ok {
   262  		t.Errorf("Expected the chainId key be present in the domain map")
   263  	}
   264  }
   265  
   266  func TestHashStruct(t *testing.T) {
   267  	hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  	mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
   272  	if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" {
   273  		t.Errorf("Expected different hashStruct result (got %s)", mainHash)
   274  	}
   275  
   276  	hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
   277  	if err != nil {
   278  		t.Error(err)
   279  	}
   280  	domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
   281  	if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" {
   282  		t.Errorf("Expected different domain hashStruct result (got %s)", domainHash)
   283  	}
   284  }
   285  
   286  func TestEncodeType(t *testing.T) {
   287  	domainTypeEncoding := string(typedData.EncodeType("EIP712Domain"))
   288  	if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" {
   289  		t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding)
   290  	}
   291  
   292  	mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType))
   293  	if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" {
   294  		t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding)
   295  	}
   296  }
   297  
   298  func TestTypeHash(t *testing.T) {
   299  	mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType)))
   300  	if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" {
   301  		t.Errorf("Expected different typeHash result (got %s)", mailTypeHash)
   302  	}
   303  }
   304  
   305  func TestEncodeData(t *testing.T) {
   306  	hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0)
   307  	if err != nil {
   308  		t.Fatal(err)
   309  	}
   310  	dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
   311  	if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" {
   312  		t.Errorf("Expected different encodeData result (got %s)", dataEncoding)
   313  	}
   314  }
   315  
   316  func TestFormatter(t *testing.T) {
   317  	var d core.TypedData
   318  	err := json.Unmarshal([]byte(jsonTypedData), &d)
   319  	if err != nil {
   320  		t.Fatalf("unmarshalling failed '%v'", err)
   321  	}
   322  	formatted, _ := d.Format()
   323  	for _, item := range formatted {
   324  		t.Logf("'%v'\n", item.Pprint(0))
   325  	}
   326  
   327  	j, _ := json.Marshal(formatted)
   328  	t.Logf("'%v'\n", string(j))
   329  }
   330  
   331  func sign(typedData core.TypedData) ([]byte, []byte, error) {
   332  	domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
   333  	if err != nil {
   334  		return nil, nil, err
   335  	}
   336  	typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
   337  	if err != nil {
   338  		return nil, nil, err
   339  	}
   340  	rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
   341  	sighash := crypto.Keccak256(rawData)
   342  	return typedDataHash, sighash, nil
   343  }
   344  
   345  func TestJsonFiles(t *testing.T) {
   346  	testfiles, err := ioutil.ReadDir("testdata/")
   347  	if err != nil {
   348  		t.Fatalf("failed reading files: %v", err)
   349  	}
   350  	for i, fInfo := range testfiles {
   351  		if !strings.HasSuffix(fInfo.Name(), "json") {
   352  			continue
   353  		}
   354  		expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail")
   355  		data, err := ioutil.ReadFile(path.Join("testdata", fInfo.Name()))
   356  		if err != nil {
   357  			t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
   358  			continue
   359  		}
   360  		var typedData core.TypedData
   361  		err = json.Unmarshal([]byte(data), &typedData)
   362  		if err != nil {
   363  			t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err)
   364  			continue
   365  		}
   366  		_, _, err = sign(typedData)
   367  		t.Logf("Error %v\n", err)
   368  		if err != nil && !expectedFailure {
   369  			t.Errorf("Test %d failed, file %v: %v", i, fInfo.Name(), err)
   370  		}
   371  		if expectedFailure && err == nil {
   372  			t.Errorf("Test %d succeeded (expected failure), file %v: %v", i, fInfo.Name(), err)
   373  		}
   374  	}
   375  }
   376  
   377  // TestFuzzerFiles tests some files that have been found by fuzzing to cause
   378  // crashes or hangs.
   379  func TestFuzzerFiles(t *testing.T) {
   380  	corpusdir := path.Join("testdata", "fuzzing")
   381  	testfiles, err := ioutil.ReadDir(corpusdir)
   382  	if err != nil {
   383  		t.Fatalf("failed reading files: %v", err)
   384  	}
   385  	verbose := false
   386  	for i, fInfo := range testfiles {
   387  		data, err := ioutil.ReadFile(path.Join(corpusdir, fInfo.Name()))
   388  		if err != nil {
   389  			t.Errorf("Failed to read file %v: %v", fInfo.Name(), err)
   390  			continue
   391  		}
   392  		var typedData core.TypedData
   393  		err = json.Unmarshal([]byte(data), &typedData)
   394  		if err != nil {
   395  			t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err)
   396  			continue
   397  		}
   398  		_, err = typedData.EncodeData("EIP712Domain", typedData.Domain.Map(), 1)
   399  		if verbose && err != nil {
   400  			t.Logf("%d, EncodeData[1] err: %v\n", i, err)
   401  		}
   402  		_, err = typedData.EncodeData(typedData.PrimaryType, typedData.Message, 1)
   403  		if verbose && err != nil {
   404  			t.Logf("%d, EncodeData[2] err: %v\n", i, err)
   405  		}
   406  		typedData.Format()
   407  	}
   408  }