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