github.com/theQRL/go-zond@v0.2.1/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 "bytes" 21 "context" 22 "encoding/json" 23 "fmt" 24 "os" 25 "path" 26 "strings" 27 "testing" 28 29 "github.com/theQRL/go-zond/accounts/keystore" 30 "github.com/theQRL/go-zond/common" 31 "github.com/theQRL/go-zond/common/hexutil" 32 "github.com/theQRL/go-zond/common/math" 33 "github.com/theQRL/go-zond/crypto" 34 "github.com/theQRL/go-zond/signer/core" 35 "github.com/theQRL/go-zond/signer/core/apitypes" 36 ) 37 38 var typesStandard = apitypes.Types{ 39 "EIP712Domain": { 40 { 41 Name: "name", 42 Type: "string", 43 }, 44 { 45 Name: "version", 46 Type: "string", 47 }, 48 { 49 Name: "chainId", 50 Type: "uint256", 51 }, 52 { 53 Name: "verifyingContract", 54 Type: "address", 55 }, 56 }, 57 "Person": { 58 { 59 Name: "name", 60 Type: "string", 61 }, 62 { 63 Name: "wallet", 64 Type: "address", 65 }, 66 }, 67 "Mail": { 68 { 69 Name: "from", 70 Type: "Person", 71 }, 72 { 73 Name: "to", 74 Type: "Person", 75 }, 76 { 77 Name: "contents", 78 Type: "string", 79 }, 80 }, 81 } 82 83 var jsonTypedData = ` 84 { 85 "types": { 86 "EIP712Domain": [ 87 { 88 "name": "name", 89 "type": "string" 90 }, 91 { 92 "name": "version", 93 "type": "string" 94 }, 95 { 96 "name": "chainId", 97 "type": "uint256" 98 }, 99 { 100 "name": "verifyingContract", 101 "type": "address" 102 } 103 ], 104 "Person": [ 105 { 106 "name": "name", 107 "type": "string" 108 }, 109 { 110 "name": "test", 111 "type": "uint8" 112 }, 113 { 114 "name": "wallet", 115 "type": "address" 116 } 117 ], 118 "Mail": [ 119 { 120 "name": "from", 121 "type": "Person" 122 }, 123 { 124 "name": "to", 125 "type": "Person" 126 }, 127 { 128 "name": "contents", 129 "type": "string" 130 } 131 ] 132 }, 133 "primaryType": "Mail", 134 "domain": { 135 "name": "Ether Mail", 136 "version": "1", 137 "chainId": "1", 138 "verifyingContract": "ZCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" 139 }, 140 "message": { 141 "from": { 142 "name": "Cow", 143 "test": 3, 144 "wallet": "ZcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" 145 }, 146 "to": { 147 "name": "Bob", 148 "wallet": "ZbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" 149 }, 150 "contents": "Hello, Bob!" 151 } 152 } 153 ` 154 155 const primaryType = "Mail" 156 157 var domainStandard = apitypes.TypedDataDomain{ 158 Name: "Ether Mail", 159 Version: "1", 160 ChainId: math.NewHexOrDecimal256(1), 161 VerifyingContract: "ZCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", 162 Salt: "", 163 } 164 165 var messageStandard = map[string]interface{}{ 166 "from": map[string]interface{}{ 167 "name": "Cow", 168 "wallet": "ZCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", 169 }, 170 "to": map[string]interface{}{ 171 "name": "Bob", 172 "wallet": "ZbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", 173 }, 174 "contents": "Hello, Bob!", 175 } 176 177 var typedData = apitypes.TypedData{ 178 Types: typesStandard, 179 PrimaryType: primaryType, 180 Domain: domainStandard, 181 Message: messageStandard, 182 } 183 184 func TestSignData(t *testing.T) { 185 api, control := setup(t) 186 //Create two accounts 187 createAccount(control, api, t) 188 createAccount(control, api, t) 189 control.approveCh <- "1" 190 list, err := api.List(context.Background()) 191 if err != nil { 192 t.Fatal(err) 193 } 194 a := common.NewMixedcaseAddress(list[0]) 195 196 control.approveCh <- "Y" 197 control.inputCh <- "wrongpassword" 198 signature, err := api.SignData(context.Background(), apitypes.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) 199 if signature != nil { 200 t.Errorf("Expected nil-data, got %x", signature) 201 } 202 if err != keystore.ErrDecrypt { 203 t.Errorf("Expected ErrLocked! '%v'", err) 204 } 205 control.approveCh <- "No way" 206 signature, err = api.SignData(context.Background(), apitypes.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) 207 if signature != nil { 208 t.Errorf("Expected nil-data, got %x", signature) 209 } 210 if err != core.ErrRequestDenied { 211 t.Errorf("Expected ErrRequestDenied! '%v'", err) 212 } 213 // text/plain 214 control.approveCh <- "Y" 215 control.inputCh <- "a_long_password" 216 signature, err = api.SignData(context.Background(), apitypes.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) 217 if err != nil { 218 t.Fatal(err) 219 } 220 if signature == nil || len(signature) != 4595 { 221 t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) 222 } 223 // data/typed via SignTypeData 224 control.approveCh <- "Y" 225 control.inputCh <- "a_long_password" 226 var want []byte 227 if signature, err = api.SignTypedData(context.Background(), a, typedData); err != nil { 228 t.Fatal(err) 229 } else if signature == nil || len(signature) != 4595 { 230 t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) 231 } else { 232 want = signature 233 } 234 235 // data/typed via SignData / mimetype typed data 236 control.approveCh <- "Y" 237 control.inputCh <- "a_long_password" 238 if typedDataJson, err := json.Marshal(typedData); err != nil { 239 t.Fatal(err) 240 } else if signature, err = api.SignData(context.Background(), apitypes.DataTyped.Mime, a, hexutil.Encode(typedDataJson)); err != nil { 241 t.Fatal(err) 242 } else if signature == nil || len(signature) != 4595 { 243 t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) 244 } else if have := signature; !bytes.Equal(have, want) { 245 t.Fatalf("want %x, have %x", want, have) 246 } 247 } 248 249 func TestDomainChainId(t *testing.T) { 250 withoutChainID := apitypes.TypedData{ 251 Types: apitypes.Types{ 252 "EIP712Domain": []apitypes.Type{ 253 {Name: "name", Type: "string"}, 254 }, 255 }, 256 Domain: apitypes.TypedDataDomain{ 257 Name: "test", 258 }, 259 } 260 261 if _, ok := withoutChainID.Domain.Map()["chainId"]; ok { 262 t.Errorf("Expected the chainId key to not be present in the domain map") 263 } 264 // should encode successfully 265 if _, err := withoutChainID.HashStruct("EIP712Domain", withoutChainID.Domain.Map()); err != nil { 266 t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) 267 } 268 withChainID := apitypes.TypedData{ 269 Types: apitypes.Types{ 270 "EIP712Domain": []apitypes.Type{ 271 {Name: "name", Type: "string"}, 272 {Name: "chainId", Type: "uint256"}, 273 }, 274 }, 275 Domain: apitypes.TypedDataDomain{ 276 Name: "test", 277 ChainId: math.NewHexOrDecimal256(1), 278 }, 279 } 280 281 if _, ok := withChainID.Domain.Map()["chainId"]; !ok { 282 t.Errorf("Expected the chainId key be present in the domain map") 283 } 284 // should encode successfully 285 if _, err := withChainID.HashStruct("EIP712Domain", withChainID.Domain.Map()); err != nil { 286 t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) 287 } 288 } 289 290 func TestHashStruct(t *testing.T) { 291 hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) 292 if err != nil { 293 t.Fatal(err) 294 } 295 mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) 296 if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" { 297 t.Errorf("Expected different hashStruct result (got %s)", mainHash) 298 } 299 300 hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) 301 if err != nil { 302 t.Error(err) 303 } 304 domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) 305 if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" { 306 t.Errorf("Expected different domain hashStruct result (got %s)", domainHash) 307 } 308 } 309 310 func TestEncodeType(t *testing.T) { 311 domainTypeEncoding := string(typedData.EncodeType("EIP712Domain")) 312 if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" { 313 t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding) 314 } 315 316 mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType)) 317 if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" { 318 t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding) 319 } 320 } 321 322 func TestTypeHash(t *testing.T) { 323 mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType))) 324 if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" { 325 t.Errorf("Expected different typeHash result (got %s)", mailTypeHash) 326 } 327 } 328 329 func TestEncodeData(t *testing.T) { 330 hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0) 331 if err != nil { 332 t.Fatal(err) 333 } 334 dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) 335 if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" { 336 t.Errorf("Expected different encodeData result (got %s)", dataEncoding) 337 } 338 } 339 340 func TestFormatter(t *testing.T) { 341 var d apitypes.TypedData 342 err := json.Unmarshal([]byte(jsonTypedData), &d) 343 if err != nil { 344 t.Fatalf("unmarshalling failed '%v'", err) 345 } 346 formatted, _ := d.Format() 347 for _, item := range formatted { 348 t.Logf("'%v'\n", item.Pprint(0)) 349 } 350 351 j, _ := json.Marshal(formatted) 352 t.Logf("'%v'\n", string(j)) 353 } 354 355 func sign(typedData apitypes.TypedData) ([]byte, []byte, error) { 356 domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) 357 if err != nil { 358 return nil, nil, err 359 } 360 typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) 361 if err != nil { 362 return nil, nil, err 363 } 364 rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) 365 sighash := crypto.Keccak256(rawData) 366 return typedDataHash, sighash, nil 367 } 368 369 func TestJsonFiles(t *testing.T) { 370 testfiles, err := os.ReadDir("testdata/") 371 if err != nil { 372 t.Fatalf("failed reading files: %v", err) 373 } 374 for i, fInfo := range testfiles { 375 if !strings.HasSuffix(fInfo.Name(), "json") { 376 continue 377 } 378 expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail") 379 data, err := os.ReadFile(path.Join("testdata", fInfo.Name())) 380 if err != nil { 381 t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) 382 continue 383 } 384 var typedData apitypes.TypedData 385 err = json.Unmarshal(data, &typedData) 386 if err != nil { 387 t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) 388 continue 389 } 390 _, _, err = sign(typedData) 391 t.Logf("Error %v\n", err) 392 if err != nil && !expectedFailure { 393 t.Errorf("Test %d failed, file %v: %v", i, fInfo.Name(), err) 394 } 395 if expectedFailure && err == nil { 396 t.Errorf("Test %d succeeded (expected failure), file %v: %v", i, fInfo.Name(), err) 397 } 398 } 399 } 400 401 // TestFuzzerFiles tests some files that have been found by fuzzing to cause 402 // crashes or hangs. 403 func TestFuzzerFiles(t *testing.T) { 404 corpusdir := path.Join("testdata", "fuzzing") 405 testfiles, err := os.ReadDir(corpusdir) 406 if err != nil { 407 t.Fatalf("failed reading files: %v", err) 408 } 409 verbose := false 410 for i, fInfo := range testfiles { 411 data, err := os.ReadFile(path.Join(corpusdir, fInfo.Name())) 412 if err != nil { 413 t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) 414 continue 415 } 416 var typedData apitypes.TypedData 417 err = json.Unmarshal(data, &typedData) 418 if err != nil { 419 t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) 420 continue 421 } 422 _, err = typedData.EncodeData("EIP712Domain", typedData.Domain.Map(), 1) 423 if verbose && err != nil { 424 t.Logf("%d, EncodeData[1] err: %v\n", i, err) 425 } 426 _, err = typedData.EncodeData(typedData.PrimaryType, typedData.Message, 1) 427 if verbose && err != nil { 428 t.Logf("%d, EncodeData[2] err: %v\n", i, err) 429 } 430 typedData.Format() 431 } 432 } 433 434 var complexTypedData = ` 435 { 436 "types": { 437 "EIP712Domain": [ 438 { 439 "name": "chainId", 440 "type": "uint256" 441 }, 442 { 443 "name": "name", 444 "type": "string" 445 }, 446 { 447 "name": "verifyingContract", 448 "type": "address" 449 }, 450 { 451 "name": "version", 452 "type": "string" 453 } 454 ], 455 "Action": [ 456 { 457 "name": "action", 458 "type": "string" 459 }, 460 { 461 "name": "params", 462 "type": "string" 463 } 464 ], 465 "Cell": [ 466 { 467 "name": "capacity", 468 "type": "string" 469 }, 470 { 471 "name": "lock", 472 "type": "string" 473 }, 474 { 475 "name": "type", 476 "type": "string" 477 }, 478 { 479 "name": "data", 480 "type": "string" 481 }, 482 { 483 "name": "extraData", 484 "type": "string" 485 } 486 ], 487 "Transaction": [ 488 { 489 "name": "DAS_MESSAGE", 490 "type": "string" 491 }, 492 { 493 "name": "inputsCapacity", 494 "type": "string" 495 }, 496 { 497 "name": "outputsCapacity", 498 "type": "string" 499 }, 500 { 501 "name": "fee", 502 "type": "string" 503 }, 504 { 505 "name": "action", 506 "type": "Action" 507 }, 508 { 509 "name": "inputs", 510 "type": "Cell[]" 511 }, 512 { 513 "name": "outputs", 514 "type": "Cell[]" 515 }, 516 { 517 "name": "digest", 518 "type": "bytes32" 519 } 520 ] 521 }, 522 "primaryType": "Transaction", 523 "domain": { 524 "chainId": "56", 525 "name": "da.systems", 526 "verifyingContract": "Z0000000000000000000000000000000020210722", 527 "version": "1" 528 }, 529 "message": { 530 "DAS_MESSAGE": "SELL mobcion.bit FOR 100000 CKB", 531 "inputsCapacity": "1216.9999 CKB", 532 "outputsCapacity": "1216.9998 CKB", 533 "fee": "0.0001 CKB", 534 "digest": "0x53a6c0f19ec281604607f5d6817e442082ad1882bef0df64d84d3810dae561eb", 535 "action": { 536 "action": "start_account_sale", 537 "params": "0x00" 538 }, 539 "inputs": [ 540 { 541 "capacity": "218 CKB", 542 "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", 543 "type": "account-cell-type,0x01,0x", 544 "data": "{ account: mobcion.bit, expired_at: 1670913958 }", 545 "extraData": "{ status: 0, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" 546 } 547 ], 548 "outputs": [ 549 { 550 "capacity": "218 CKB", 551 "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", 552 "type": "account-cell-type,0x01,0x", 553 "data": "{ account: mobcion.bit, expired_at: 1670913958 }", 554 "extraData": "{ status: 1, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" 555 }, 556 { 557 "capacity": "201 CKB", 558 "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", 559 "type": "account-sale-cell-type,0x01,0x", 560 "data": "0x1209460ef3cb5f1c68ed2c43a3e020eec2d9de6e...", 561 "extraData": "" 562 } 563 ] 564 } 565 } 566 ` 567 568 func TestComplexTypedData(t *testing.T) { 569 var td apitypes.TypedData 570 err := json.Unmarshal([]byte(complexTypedData), &td) 571 if err != nil { 572 t.Fatalf("unmarshalling failed '%v'", err) 573 } 574 _, sighash, err := sign(td) 575 if err != nil { 576 t.Fatal(err) 577 } 578 expSigHash := common.FromHex("0x42b1aca82bb6900ff75e90a136de550a58f1a220a071704088eabd5e6ce20446") 579 if !bytes.Equal(expSigHash, sighash) { 580 t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) 581 } 582 } 583 584 var complexTypedDataLCRefType = ` 585 { 586 "types": { 587 "EIP712Domain": [ 588 { 589 "name": "chainId", 590 "type": "uint256" 591 }, 592 { 593 "name": "name", 594 "type": "string" 595 }, 596 { 597 "name": "verifyingContract", 598 "type": "address" 599 }, 600 { 601 "name": "version", 602 "type": "string" 603 } 604 ], 605 "Action": [ 606 { 607 "name": "action", 608 "type": "string" 609 }, 610 { 611 "name": "params", 612 "type": "string" 613 } 614 ], 615 "cCell": [ 616 { 617 "name": "capacity", 618 "type": "string" 619 }, 620 { 621 "name": "lock", 622 "type": "string" 623 }, 624 { 625 "name": "type", 626 "type": "string" 627 }, 628 { 629 "name": "data", 630 "type": "string" 631 }, 632 { 633 "name": "extraData", 634 "type": "string" 635 } 636 ], 637 "Transaction": [ 638 { 639 "name": "DAS_MESSAGE", 640 "type": "string" 641 }, 642 { 643 "name": "inputsCapacity", 644 "type": "string" 645 }, 646 { 647 "name": "outputsCapacity", 648 "type": "string" 649 }, 650 { 651 "name": "fee", 652 "type": "string" 653 }, 654 { 655 "name": "action", 656 "type": "Action" 657 }, 658 { 659 "name": "inputs", 660 "type": "cCell[]" 661 }, 662 { 663 "name": "outputs", 664 "type": "cCell[]" 665 }, 666 { 667 "name": "digest", 668 "type": "bytes32" 669 } 670 ] 671 }, 672 "primaryType": "Transaction", 673 "domain": { 674 "chainId": "56", 675 "name": "da.systems", 676 "verifyingContract": "Z0000000000000000000000000000000020210722", 677 "version": "1" 678 }, 679 "message": { 680 "DAS_MESSAGE": "SELL mobcion.bit FOR 100000 CKB", 681 "inputsCapacity": "1216.9999 CKB", 682 "outputsCapacity": "1216.9998 CKB", 683 "fee": "0.0001 CKB", 684 "digest": "0x53a6c0f19ec281604607f5d6817e442082ad1882bef0df64d84d3810dae561eb", 685 "action": { 686 "action": "start_account_sale", 687 "params": "0x00" 688 }, 689 "inputs": [ 690 { 691 "capacity": "218 CKB", 692 "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", 693 "type": "account-cell-type,0x01,0x", 694 "data": "{ account: mobcion.bit, expired_at: 1670913958 }", 695 "extraData": "{ status: 0, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" 696 } 697 ], 698 "outputs": [ 699 { 700 "capacity": "218 CKB", 701 "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", 702 "type": "account-cell-type,0x01,0x", 703 "data": "{ account: mobcion.bit, expired_at: 1670913958 }", 704 "extraData": "{ status: 1, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" 705 }, 706 { 707 "capacity": "201 CKB", 708 "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", 709 "type": "account-sale-cell-type,0x01,0x", 710 "data": "0x1209460ef3cb5f1c68ed2c43a3e020eec2d9de6e...", 711 "extraData": "" 712 } 713 ] 714 } 715 } 716 ` 717 718 func TestComplexTypedDataWithLowercaseReftype(t *testing.T) { 719 var td apitypes.TypedData 720 err := json.Unmarshal([]byte(complexTypedDataLCRefType), &td) 721 if err != nil { 722 t.Fatalf("unmarshalling failed '%v'", err) 723 } 724 _, sighash, err := sign(td) 725 if err != nil { 726 t.Fatal(err) 727 } 728 expSigHash := common.FromHex("0x49191f910874f0148597204d9076af128d4694a7c4b714f1ccff330b87207bff") 729 if !bytes.Equal(expSigHash, sighash) { 730 t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) 731 } 732 }