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