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