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