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