github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/signer/core/signed_data.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 // 17 package core 18 19 import ( 20 "bytes" 21 "context" 22 "errors" 23 "fmt" 24 "math/big" 25 "mime" 26 "regexp" 27 "sort" 28 "strconv" 29 "strings" 30 "unicode" 31 32 "github.com/ethereum/go-ethereum/accounts" 33 "github.com/ethereum/go-ethereum/accounts/abi" 34 "github.com/ethereum/go-ethereum/common" 35 "github.com/ethereum/go-ethereum/common/hexutil" 36 "github.com/ethereum/go-ethereum/common/math" 37 "github.com/ethereum/go-ethereum/consensus/clique" 38 "github.com/ethereum/go-ethereum/core/types" 39 "github.com/ethereum/go-ethereum/crypto" 40 "github.com/ethereum/go-ethereum/rlp" 41 ) 42 43 type SigFormat struct { 44 Mime string 45 ByteVersion byte 46 } 47 48 var ( 49 TextValidator = SigFormat{ 50 accounts.MimetypeTextWithValidator, 51 0x00, 52 } 53 DataTyped = SigFormat{ 54 accounts.MimetypeTypedData, 55 0x01, 56 } 57 ApplicationClique = SigFormat{ 58 accounts.MimetypeClique, 59 0x02, 60 } 61 TextPlain = SigFormat{ 62 accounts.MimetypeTextPlain, 63 0x45, 64 } 65 ) 66 67 type ValidatorData struct { 68 Address common.Address 69 Message hexutil.Bytes 70 } 71 72 type TypedData struct { 73 Types Types `json:"types"` 74 PrimaryType string `json:"primaryType"` 75 Domain TypedDataDomain `json:"domain"` 76 Message TypedDataMessage `json:"message"` 77 } 78 79 type Type struct { 80 Name string `json:"name"` 81 Type string `json:"type"` 82 } 83 84 func (t *Type) isArray() bool { 85 return strings.HasSuffix(t.Type, "[]") 86 } 87 88 // typeName returns the canonical name of the type. If the type is 'Person[]', then 89 // this method returns 'Person' 90 func (t *Type) typeName() string { 91 if strings.HasSuffix(t.Type, "[]") { 92 return strings.TrimSuffix(t.Type, "[]") 93 } 94 return t.Type 95 } 96 97 func (t *Type) isReferenceType() bool { 98 // Reference types must have a leading uppercase characer 99 return unicode.IsUpper([]rune(t.Type)[0]) 100 } 101 102 type Types map[string][]Type 103 104 type TypePriority struct { 105 Type string 106 Value uint 107 } 108 109 type TypedDataMessage = map[string]interface{} 110 111 type TypedDataDomain struct { 112 Name string `json:"name"` 113 Version string `json:"version"` 114 ChainId *big.Int `json:"chainId"` 115 VerifyingContract string `json:"verifyingContract"` 116 Salt string `json:"salt"` 117 } 118 119 var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`) 120 121 // sign receives a request and produces a signature 122 123 // Note, the produced signature conforms to the secp256k1 curve R, S and V values, 124 // where the V value will be 27 or 28 for legacy reasons, if legacyV==true. 125 func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, legacyV bool) (hexutil.Bytes, error) { 126 127 // We make the request prior to looking up if we actually have the account, to prevent 128 // account-enumeration via the API 129 res, err := api.UI.ApproveSignData(req) 130 if err != nil { 131 return nil, err 132 } 133 if !res.Approved { 134 return nil, ErrRequestDenied 135 } 136 // Look up the wallet containing the requested signer 137 account := accounts.Account{Address: addr.Address()} 138 wallet, err := api.am.Find(account) 139 if err != nil { 140 return nil, err 141 } 142 pw, err := api.lookupOrQueryPassword(account.Address, 143 "Password for signing", 144 fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex())) 145 if err != nil { 146 return nil, err 147 } 148 // Sign the data with the wallet 149 signature, err := wallet.SignDataWithPassphrase(account, pw, req.ContentType, req.Rawdata) 150 if err != nil { 151 return nil, err 152 } 153 if legacyV { 154 signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper 155 } 156 return signature, nil 157 } 158 159 // SignData signs the hash of the provided data, but does so differently 160 // depending on the content-type specified. 161 // 162 // Different types of validation occur. 163 func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) { 164 var req, transformV, err = api.determineSignatureFormat(ctx, contentType, addr, data) 165 if err != nil { 166 return nil, err 167 } 168 169 signature, err := api.sign(addr, req, transformV) 170 if err != nil { 171 api.UI.ShowError(err.Error()) 172 return nil, err 173 } 174 return signature, nil 175 } 176 177 // determineSignatureFormat determines which signature method should be used based upon the mime type 178 // In the cases where it matters ensure that the charset is handled. The charset 179 // resides in the 'params' returned as the second returnvalue from mime.ParseMediaType 180 // charset, ok := params["charset"] 181 // As it is now, we accept any charset and just treat it as 'raw'. 182 // This method returns the mimetype for signing along with the request 183 func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (*SignDataRequest, bool, error) { 184 var ( 185 req *SignDataRequest 186 useEthereumV = true // Default to use V = 27 or 28, the legacy Ethereum format 187 ) 188 mediaType, _, err := mime.ParseMediaType(contentType) 189 if err != nil { 190 return nil, useEthereumV, err 191 } 192 193 switch mediaType { 194 case TextValidator.Mime: 195 // Data with an intended validator 196 validatorData, err := UnmarshalValidatorData(data) 197 if err != nil { 198 return nil, useEthereumV, err 199 } 200 sighash, msg := SignTextValidator(validatorData) 201 message := []*NameValueType{ 202 { 203 Name: "message", 204 Typ: "text", 205 Value: msg, 206 }, 207 } 208 req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Message: message, Hash: sighash} 209 case ApplicationClique.Mime: 210 // Clique is the Ethereum PoA standard 211 stringData, ok := data.(string) 212 if !ok { 213 return nil, useEthereumV, fmt.Errorf("input for %v must be an hex-encoded string", ApplicationClique.Mime) 214 } 215 cliqueData, err := hexutil.Decode(stringData) 216 if err != nil { 217 return nil, useEthereumV, err 218 } 219 header := &types.Header{} 220 if err := rlp.DecodeBytes(cliqueData, header); err != nil { 221 return nil, useEthereumV, err 222 } 223 // The incoming clique header is already truncated, sent to us with a extradata already shortened 224 if len(header.Extra) < 65 { 225 // Need to add it back, to get a suitable length for hashing 226 newExtra := make([]byte, len(header.Extra)+65) 227 copy(newExtra, header.Extra) 228 header.Extra = newExtra 229 } 230 // Get back the rlp data, encoded by us 231 sighash, cliqueRlp, err := cliqueHeaderHashAndRlp(header) 232 if err != nil { 233 return nil, useEthereumV, err 234 } 235 message := []*NameValueType{ 236 { 237 Name: "Clique header", 238 Typ: "clique", 239 Value: fmt.Sprintf("clique header %d [0x%x]", header.Number, header.Hash()), 240 }, 241 } 242 // Clique uses V on the form 0 or 1 243 useEthereumV = false 244 req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueRlp, Message: message, Hash: sighash} 245 default: // also case TextPlain.Mime: 246 // Calculates an Ethereum ECDSA signature for: 247 // hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}") 248 // We expect it to be a string 249 if stringData, ok := data.(string); !ok { 250 return nil, useEthereumV, fmt.Errorf("input for text/plain must be an hex-encoded string") 251 } else { 252 if textData, err := hexutil.Decode(stringData); err != nil { 253 return nil, useEthereumV, err 254 } else { 255 sighash, msg := accounts.TextAndHash(textData) 256 message := []*NameValueType{ 257 { 258 Name: "message", 259 Typ: accounts.MimetypeTextPlain, 260 Value: msg, 261 }, 262 } 263 req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Message: message, Hash: sighash} 264 } 265 } 266 } 267 req.Address = addr 268 req.Meta = MetadataFromContext(ctx) 269 return req, useEthereumV, nil 270 271 } 272 273 // SignTextWithValidator signs the given message which can be further recovered 274 // with the given validator. 275 // hash = keccak256("\x19\x00"${address}${data}). 276 func SignTextValidator(validatorData ValidatorData) (hexutil.Bytes, string) { 277 msg := fmt.Sprintf("\x19\x00%s%s", string(validatorData.Address.Bytes()), string(validatorData.Message)) 278 fmt.Printf("SignTextValidator:%s\n", msg) 279 return crypto.Keccak256([]byte(msg)), msg 280 } 281 282 // cliqueHeaderHashAndRlp returns the hash which is used as input for the proof-of-authority 283 // signing. It is the hash of the entire header apart from the 65 byte signature 284 // contained at the end of the extra data. 285 // 286 // The method requires the extra data to be at least 65 bytes -- the original implementation 287 // in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic 288 // and simply return an error instead 289 func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) { 290 if len(header.Extra) < 65 { 291 err = fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra)) 292 return 293 } 294 rlp = clique.CliqueRLP(header) 295 hash = clique.SealHash(header).Bytes() 296 return hash, rlp, err 297 } 298 299 // SignTypedData signs EIP-712 conformant typed data 300 // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") 301 func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) { 302 domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) 303 if err != nil { 304 return nil, err 305 } 306 typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) 307 if err != nil { 308 return nil, err 309 } 310 rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) 311 sighash := crypto.Keccak256(rawData) 312 message := typedData.Format() 313 req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Message: message, Hash: sighash} 314 signature, err := api.sign(addr, req, true) 315 if err != nil { 316 api.UI.ShowError(err.Error()) 317 return nil, err 318 } 319 return signature, nil 320 } 321 322 // HashStruct generates a keccak256 hash of the encoding of the provided data 323 func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage) (hexutil.Bytes, error) { 324 encodedData, err := typedData.EncodeData(primaryType, data, 1) 325 if err != nil { 326 return nil, err 327 } 328 return crypto.Keccak256(encodedData), nil 329 } 330 331 // Dependencies returns an array of custom types ordered by their hierarchical reference tree 332 func (typedData *TypedData) Dependencies(primaryType string, found []string) []string { 333 includes := func(arr []string, str string) bool { 334 for _, obj := range arr { 335 if obj == str { 336 return true 337 } 338 } 339 return false 340 } 341 342 if includes(found, primaryType) { 343 return found 344 } 345 if typedData.Types[primaryType] == nil { 346 return found 347 } 348 found = append(found, primaryType) 349 for _, field := range typedData.Types[primaryType] { 350 for _, dep := range typedData.Dependencies(field.Type, found) { 351 if !includes(found, dep) { 352 found = append(found, dep) 353 } 354 } 355 } 356 return found 357 } 358 359 // EncodeType generates the following encoding: 360 // `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"` 361 // 362 // each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name 363 func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes { 364 // Get dependencies primary first, then alphabetical 365 deps := typedData.Dependencies(primaryType, []string{}) 366 slicedDeps := deps[1:] 367 sort.Strings(slicedDeps) 368 deps = append([]string{primaryType}, slicedDeps...) 369 370 // Format as a string with fields 371 var buffer bytes.Buffer 372 for _, dep := range deps { 373 buffer.WriteString(dep) 374 buffer.WriteString("(") 375 for _, obj := range typedData.Types[dep] { 376 buffer.WriteString(obj.Type) 377 buffer.WriteString(" ") 378 buffer.WriteString(obj.Name) 379 buffer.WriteString(",") 380 } 381 buffer.Truncate(buffer.Len() - 1) 382 buffer.WriteString(")") 383 } 384 return buffer.Bytes() 385 } 386 387 // TypeHash creates the keccak256 hash of the data 388 func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes { 389 return crypto.Keccak256(typedData.EncodeType(primaryType)) 390 } 391 392 // EncodeData generates the following encoding: 393 // `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)` 394 // 395 // each encoded member is 32-byte long 396 func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) { 397 if err := typedData.validate(); err != nil { 398 return nil, err 399 } 400 401 buffer := bytes.Buffer{} 402 403 // Verify extra data 404 if len(typedData.Types[primaryType]) < len(data) { 405 return nil, errors.New("there is extra data provided in the message") 406 } 407 408 // Add typehash 409 buffer.Write(typedData.TypeHash(primaryType)) 410 411 // Add field contents. Structs and arrays have special handlers. 412 for _, field := range typedData.Types[primaryType] { 413 encType := field.Type 414 encValue := data[field.Name] 415 if encType[len(encType)-1:] == "]" { 416 arrayValue, ok := encValue.([]interface{}) 417 if !ok { 418 return nil, dataMismatchError(encType, encValue) 419 } 420 421 arrayBuffer := bytes.Buffer{} 422 parsedType := strings.Split(encType, "[")[0] 423 for _, item := range arrayValue { 424 if typedData.Types[parsedType] != nil { 425 mapValue, ok := item.(map[string]interface{}) 426 if !ok { 427 return nil, dataMismatchError(parsedType, item) 428 } 429 encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1) 430 if err != nil { 431 return nil, err 432 } 433 arrayBuffer.Write(encodedData) 434 } else { 435 bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth) 436 if err != nil { 437 return nil, err 438 } 439 arrayBuffer.Write(bytesValue) 440 } 441 } 442 443 buffer.Write(crypto.Keccak256(arrayBuffer.Bytes())) 444 } else if typedData.Types[field.Type] != nil { 445 mapValue, ok := encValue.(map[string]interface{}) 446 if !ok { 447 return nil, dataMismatchError(encType, encValue) 448 } 449 encodedData, err := typedData.EncodeData(field.Type, mapValue, depth+1) 450 if err != nil { 451 return nil, err 452 } 453 buffer.Write(crypto.Keccak256(encodedData)) 454 } else { 455 byteValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth) 456 if err != nil { 457 return nil, err 458 } 459 buffer.Write(byteValue) 460 } 461 } 462 return buffer.Bytes(), nil 463 } 464 465 // EncodePrimitiveValue deals with the primitive values found 466 // while searching through the typed data 467 func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) { 468 469 switch encType { 470 case "address": 471 stringValue, ok := encValue.(string) 472 if !ok || !common.IsHexAddress(stringValue) { 473 return nil, dataMismatchError(encType, encValue) 474 } 475 retval := make([]byte, 32) 476 copy(retval[12:], common.HexToAddress(stringValue).Bytes()) 477 return retval, nil 478 case "bool": 479 boolValue, ok := encValue.(bool) 480 if !ok { 481 return nil, dataMismatchError(encType, encValue) 482 } 483 if boolValue { 484 return math.PaddedBigBytes(common.Big1, 32), nil 485 } 486 return math.PaddedBigBytes(common.Big0, 32), nil 487 case "string": 488 strVal, ok := encValue.(string) 489 if !ok { 490 return nil, dataMismatchError(encType, encValue) 491 } 492 return crypto.Keccak256([]byte(strVal)), nil 493 case "bytes": 494 bytesValue, ok := encValue.([]byte) 495 if !ok { 496 return nil, dataMismatchError(encType, encValue) 497 } 498 return crypto.Keccak256(bytesValue), nil 499 } 500 if strings.HasPrefix(encType, "bytes") { 501 lengthStr := strings.TrimPrefix(encType, "bytes") 502 length, err := strconv.Atoi(lengthStr) 503 if err != nil { 504 return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr) 505 } 506 if length < 0 || length > 32 { 507 return nil, fmt.Errorf("invalid size on bytes: %d", length) 508 } 509 if byteValue, ok := encValue.(hexutil.Bytes); !ok { 510 return nil, dataMismatchError(encType, encValue) 511 } else { 512 return math.PaddedBigBytes(new(big.Int).SetBytes(byteValue), 32), nil 513 } 514 } 515 if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") { 516 length := 0 517 if encType == "int" || encType == "uint" { 518 length = 256 519 } else { 520 lengthStr := "" 521 if strings.HasPrefix(encType, "uint") { 522 lengthStr = strings.TrimPrefix(encType, "uint") 523 } else { 524 lengthStr = strings.TrimPrefix(encType, "int") 525 } 526 atoiSize, err := strconv.Atoi(lengthStr) 527 if err != nil { 528 return nil, fmt.Errorf("invalid size on integer: %v", lengthStr) 529 } 530 length = atoiSize 531 } 532 bigIntValue, ok := encValue.(*big.Int) 533 if bigIntValue.BitLen() > length { 534 return nil, fmt.Errorf("integer larger than '%v'", encType) 535 } 536 if !ok { 537 return nil, dataMismatchError(encType, encValue) 538 } 539 return abi.U256(bigIntValue), nil 540 } 541 return nil, fmt.Errorf("unrecognized type '%s'", encType) 542 543 } 544 545 // dataMismatchError generates an error for a mismatch between 546 // the provided type and data 547 func dataMismatchError(encType string, encValue interface{}) error { 548 return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType) 549 } 550 551 // EcRecover recovers the address associated with the given sig. 552 // Only compatible with `text/plain` 553 func (api *SignerAPI) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) { 554 // Returns the address for the Account that was used to create the signature. 555 // 556 // Note, this function is compatible with eth_sign and personal_sign. As such it recovers 557 // the address of: 558 // hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}") 559 // addr = ecrecover(hash, signature) 560 // 561 // Note, the signature must conform to the secp256k1 curve R, S and V values, where 562 // the V value must be be 27 or 28 for legacy reasons. 563 // 564 // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover 565 if len(sig) != 65 { 566 return common.Address{}, fmt.Errorf("signature must be 65 bytes long") 567 } 568 if sig[64] != 27 && sig[64] != 28 { 569 return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") 570 } 571 sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 572 hash := accounts.TextHash(data) 573 rpk, err := crypto.SigToPub(hash, sig) 574 if err != nil { 575 return common.Address{}, err 576 } 577 return crypto.PubkeyToAddress(*rpk), nil 578 } 579 580 // UnmarshalValidatorData converts the bytes input to typed data 581 func UnmarshalValidatorData(data interface{}) (ValidatorData, error) { 582 raw, ok := data.(map[string]interface{}) 583 if !ok { 584 return ValidatorData{}, errors.New("validator input is not a map[string]interface{}") 585 } 586 addr, ok := raw["address"].(string) 587 if !ok { 588 return ValidatorData{}, errors.New("validator address is not sent as a string") 589 } 590 addrBytes, err := hexutil.Decode(addr) 591 if err != nil { 592 return ValidatorData{}, err 593 } 594 if !ok || len(addrBytes) == 0 { 595 return ValidatorData{}, errors.New("validator address is undefined") 596 } 597 598 message, ok := raw["message"].(string) 599 if !ok { 600 return ValidatorData{}, errors.New("message is not sent as a string") 601 } 602 messageBytes, err := hexutil.Decode(message) 603 if err != nil { 604 return ValidatorData{}, err 605 } 606 if !ok || len(messageBytes) == 0 { 607 return ValidatorData{}, errors.New("message is undefined") 608 } 609 610 return ValidatorData{ 611 Address: common.BytesToAddress(addrBytes), 612 Message: messageBytes, 613 }, nil 614 } 615 616 // validate makes sure the types are sound 617 func (typedData *TypedData) validate() error { 618 if err := typedData.Types.validate(); err != nil { 619 return err 620 } 621 if err := typedData.Domain.validate(); err != nil { 622 return err 623 } 624 return nil 625 } 626 627 // Map generates a map version of the typed data 628 func (typedData *TypedData) Map() map[string]interface{} { 629 dataMap := map[string]interface{}{ 630 "types": typedData.Types, 631 "domain": typedData.Domain.Map(), 632 "primaryType": typedData.PrimaryType, 633 "message": typedData.Message, 634 } 635 return dataMap 636 } 637 638 // PrettyPrint generates a nice output to help the users 639 // of clef present data in their apps 640 func (typedData *TypedData) PrettyPrint() string { 641 output := bytes.Buffer{} 642 formatted := typedData.Format() 643 for _, item := range formatted { 644 output.WriteString(fmt.Sprintf("%v\n", item.Pprint(0))) 645 } 646 return output.String() 647 } 648 649 // Format returns a representation of typedData, which can be easily displayed by a user-interface 650 // without in-depth knowledge about 712 rules 651 func (typedData *TypedData) Format() []*NameValueType { 652 var nvts []*NameValueType 653 nvts = append(nvts, &NameValueType{ 654 Name: "EIP712Domain", 655 Value: typedData.formatData("EIP712Domain", typedData.Domain.Map()), 656 Typ: "domain", 657 }) 658 nvts = append(nvts, &NameValueType{ 659 Name: typedData.PrimaryType, 660 Value: typedData.formatData(typedData.PrimaryType, typedData.Message), 661 Typ: "primary type", 662 }) 663 return nvts 664 } 665 666 func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) []*NameValueType { 667 var output []*NameValueType 668 669 // Add field contents. Structs and arrays have special handlers. 670 for _, field := range typedData.Types[primaryType] { 671 encName := field.Name 672 encValue := data[encName] 673 item := &NameValueType{ 674 Name: encName, 675 Typ: field.Type, 676 } 677 if field.isArray() { 678 arrayValue, _ := encValue.([]interface{}) 679 parsedType := field.typeName() 680 for _, v := range arrayValue { 681 if typedData.Types[parsedType] != nil { 682 mapValue, _ := v.(map[string]interface{}) 683 mapOutput := typedData.formatData(parsedType, mapValue) 684 item.Value = mapOutput 685 } else { 686 primitiveOutput := formatPrimitiveValue(field.Type, encValue) 687 item.Value = primitiveOutput 688 } 689 } 690 } else if typedData.Types[field.Type] != nil { 691 mapValue, _ := encValue.(map[string]interface{}) 692 mapOutput := typedData.formatData(field.Type, mapValue) 693 item.Value = mapOutput 694 } else { 695 primitiveOutput := formatPrimitiveValue(field.Type, encValue) 696 item.Value = primitiveOutput 697 } 698 output = append(output, item) 699 } 700 return output 701 } 702 703 func formatPrimitiveValue(encType string, encValue interface{}) string { 704 switch encType { 705 case "address": 706 stringValue, _ := encValue.(string) 707 return common.HexToAddress(stringValue).String() 708 case "bool": 709 boolValue, _ := encValue.(bool) 710 return fmt.Sprintf("%t", boolValue) 711 case "bytes", "string": 712 return fmt.Sprintf("%s", encValue) 713 } 714 if strings.HasPrefix(encType, "bytes") { 715 return fmt.Sprintf("%s", encValue) 716 } else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") { 717 bigIntValue, _ := encValue.(*big.Int) 718 return fmt.Sprintf("%d (0x%x)", bigIntValue, bigIntValue) 719 } 720 return "NA" 721 } 722 723 // NameValueType is a very simple struct with Name, Value and Type. It's meant for simple 724 // json structures used to communicate signing-info about typed data with the UI 725 type NameValueType struct { 726 Name string `json:"name"` 727 Value interface{} `json:"value"` 728 Typ string `json:"type"` 729 } 730 731 // Pprint returns a pretty-printed version of nvt 732 func (nvt *NameValueType) Pprint(depth int) string { 733 output := bytes.Buffer{} 734 output.WriteString(strings.Repeat("\u00a0", depth*2)) 735 output.WriteString(fmt.Sprintf("%s [%s]: ", nvt.Name, nvt.Typ)) 736 if nvts, ok := nvt.Value.([]*NameValueType); ok { 737 output.WriteString("\n") 738 for _, next := range nvts { 739 sublevel := next.Pprint(depth + 1) 740 output.WriteString(sublevel) 741 } 742 } else { 743 output.WriteString(fmt.Sprintf("%q\n", nvt.Value)) 744 } 745 return output.String() 746 } 747 748 // Validate checks if the types object is conformant to the specs 749 func (t Types) validate() error { 750 for typeKey, typeArr := range t { 751 for _, typeObj := range typeArr { 752 if typeKey == typeObj.Type { 753 return fmt.Errorf("type '%s' cannot reference itself", typeObj.Type) 754 } 755 if typeObj.isReferenceType() { 756 if _, exist := t[typeObj.Type]; !exist { 757 return fmt.Errorf("reference type '%s' is undefined", typeObj.Type) 758 } 759 if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) { 760 return fmt.Errorf("unknown reference type '%s", typeObj.Type) 761 } 762 } else if !isPrimitiveTypeValid(typeObj.Type) { 763 return fmt.Errorf("unknown type '%s'", typeObj.Type) 764 } 765 } 766 } 767 return nil 768 } 769 770 // Checks if the primitive value is valid 771 func isPrimitiveTypeValid(primitiveType string) bool { 772 if primitiveType == "address" || 773 primitiveType == "address[]" || 774 primitiveType == "bool" || 775 primitiveType == "bool[]" || 776 primitiveType == "string" || 777 primitiveType == "string[]" { 778 return true 779 } 780 if primitiveType == "bytes" || 781 primitiveType == "bytes[]" || 782 primitiveType == "bytes1" || 783 primitiveType == "bytes1[]" || 784 primitiveType == "bytes2" || 785 primitiveType == "bytes2[]" || 786 primitiveType == "bytes3" || 787 primitiveType == "bytes3[]" || 788 primitiveType == "bytes4" || 789 primitiveType == "bytes4[]" || 790 primitiveType == "bytes5" || 791 primitiveType == "bytes5[]" || 792 primitiveType == "bytes6" || 793 primitiveType == "bytes6[]" || 794 primitiveType == "bytes7" || 795 primitiveType == "bytes7[]" || 796 primitiveType == "bytes8" || 797 primitiveType == "bytes8[]" || 798 primitiveType == "bytes9" || 799 primitiveType == "bytes9[]" || 800 primitiveType == "bytes10" || 801 primitiveType == "bytes10[]" || 802 primitiveType == "bytes11" || 803 primitiveType == "bytes11[]" || 804 primitiveType == "bytes12" || 805 primitiveType == "bytes12[]" || 806 primitiveType == "bytes13" || 807 primitiveType == "bytes13[]" || 808 primitiveType == "bytes14" || 809 primitiveType == "bytes14[]" || 810 primitiveType == "bytes15" || 811 primitiveType == "bytes15[]" || 812 primitiveType == "bytes16" || 813 primitiveType == "bytes16[]" || 814 primitiveType == "bytes17" || 815 primitiveType == "bytes17[]" || 816 primitiveType == "bytes18" || 817 primitiveType == "bytes18[]" || 818 primitiveType == "bytes19" || 819 primitiveType == "bytes19[]" || 820 primitiveType == "bytes20" || 821 primitiveType == "bytes20[]" || 822 primitiveType == "bytes21" || 823 primitiveType == "bytes21[]" || 824 primitiveType == "bytes22" || 825 primitiveType == "bytes22[]" || 826 primitiveType == "bytes23" || 827 primitiveType == "bytes23[]" || 828 primitiveType == "bytes24" || 829 primitiveType == "bytes24[]" || 830 primitiveType == "bytes25" || 831 primitiveType == "bytes25[]" || 832 primitiveType == "bytes26" || 833 primitiveType == "bytes26[]" || 834 primitiveType == "bytes27" || 835 primitiveType == "bytes27[]" || 836 primitiveType == "bytes28" || 837 primitiveType == "bytes28[]" || 838 primitiveType == "bytes29" || 839 primitiveType == "bytes29[]" || 840 primitiveType == "bytes30" || 841 primitiveType == "bytes30[]" || 842 primitiveType == "bytes31" || 843 primitiveType == "bytes31[]" { 844 return true 845 } 846 if primitiveType == "int" || 847 primitiveType == "int[]" || 848 primitiveType == "int8" || 849 primitiveType == "int8[]" || 850 primitiveType == "int16" || 851 primitiveType == "int16[]" || 852 primitiveType == "int32" || 853 primitiveType == "int32[]" || 854 primitiveType == "int64" || 855 primitiveType == "int64[]" || 856 primitiveType == "int128" || 857 primitiveType == "int128[]" || 858 primitiveType == "int256" || 859 primitiveType == "int256[]" { 860 return true 861 } 862 if primitiveType == "uint" || 863 primitiveType == "uint[]" || 864 primitiveType == "uint8" || 865 primitiveType == "uint8[]" || 866 primitiveType == "uint16" || 867 primitiveType == "uint16[]" || 868 primitiveType == "uint32" || 869 primitiveType == "uint32[]" || 870 primitiveType == "uint64" || 871 primitiveType == "uint64[]" || 872 primitiveType == "uint128" || 873 primitiveType == "uint128[]" || 874 primitiveType == "uint256" || 875 primitiveType == "uint256[]" { 876 return true 877 } 878 return false 879 } 880 881 // validate checks if the given domain is valid, i.e. contains at least 882 // the minimum viable keys and values 883 func (domain *TypedDataDomain) validate() error { 884 if domain.ChainId == big.NewInt(0) { 885 return errors.New("chainId must be specified according to EIP-155") 886 } 887 888 if len(domain.Name) == 0 && len(domain.Version) == 0 && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 { 889 return errors.New("domain is undefined") 890 } 891 892 return nil 893 } 894 895 // Map is a helper function to generate a map version of the domain 896 func (domain *TypedDataDomain) Map() map[string]interface{} { 897 dataMap := map[string]interface{}{} 898 899 if domain.ChainId != nil { 900 dataMap["chainId"] = domain.ChainId 901 } 902 903 if len(domain.Name) > 0 { 904 dataMap["name"] = domain.Name 905 } 906 907 if len(domain.Version) > 0 { 908 dataMap["version"] = domain.Version 909 } 910 911 if len(domain.VerifyingContract) > 0 { 912 dataMap["verifyingContract"] = domain.VerifyingContract 913 } 914 915 if len(domain.Salt) > 0 { 916 dataMap["salt"] = domain.Salt 917 } 918 return dataMap 919 }