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