github.com/status-im/status-go@v1.1.0/protocol/identity/emojihash/emojihash.go (about) 1 package emojihash 2 3 import ( 4 "bufio" 5 "bytes" 6 "errors" 7 "math/big" 8 "strings" 9 10 "github.com/status-im/status-go/protocol/identity" 11 "github.com/status-im/status-go/static" 12 ) 13 14 const ( 15 emojiAlphabetLen = 2757 // 20bytes of data described by 14 emojis requires at least 2757 length alphabet 16 emojiHashLen = 14 17 ) 18 19 var emojisAlphabet []string 20 21 func GenerateFor(pubkey string) ([]string, error) { 22 if len(emojisAlphabet) == 0 { 23 alphabet, err := loadAlphabet() 24 if err != nil { 25 return nil, err 26 } 27 emojisAlphabet = *alphabet 28 } 29 30 compressedKey, err := identity.ToCompressedKey(pubkey) 31 if err != nil { 32 return nil, err 33 } 34 35 slices, err := identity.Slices(compressedKey) 36 if err != nil { 37 return nil, err 38 } 39 40 return toEmojiHash(new(big.Int).SetBytes(slices[1]), emojiHashLen, &emojisAlphabet) 41 } 42 43 func loadAlphabet() (*[]string, error) { 44 data, err := static.Asset("emojis.txt") 45 if err != nil { 46 return nil, err 47 } 48 49 alphabet := make([]string, 0, emojiAlphabetLen) 50 51 scanner := bufio.NewScanner(bytes.NewReader(data)) 52 for scanner.Scan() { 53 alphabet = append(alphabet, strings.Replace(scanner.Text(), "\n", "", -1)) 54 } 55 56 // current alphabet contains more emojis than needed, just in case some emojis needs to be removed 57 // make sure only necessary part is loaded 58 if len(alphabet) > emojiAlphabetLen { 59 alphabet = alphabet[:emojiAlphabetLen] 60 } 61 62 return &alphabet, nil 63 } 64 65 func toEmojiHash(value *big.Int, hashLen int, alphabet *[]string) (hash []string, err error) { 66 valueBitLen := value.BitLen() 67 alphabetLen := new(big.Int).SetInt64(int64(len(*alphabet))) 68 69 indexes := identity.ToBigBase(value, alphabetLen.Uint64()) 70 if hashLen == 0 { 71 hashLen = len(indexes) 72 } else if hashLen > len(indexes) { 73 prependLen := hashLen - len(indexes) 74 for i := 0; i < prependLen; i++ { 75 indexes = append([](uint64){0}, indexes...) 76 } 77 } 78 79 // alphabetLen^hashLen 80 possibleCombinations := new(big.Int).Exp(alphabetLen, new(big.Int).SetInt64(int64(hashLen)), nil) 81 82 // 2^valueBitLen 83 requiredCombinations := new(big.Int).Exp(new(big.Int).SetInt64(2), new(big.Int).SetInt64(int64(valueBitLen)), nil) 84 85 if possibleCombinations.Cmp(requiredCombinations) == -1 { 86 return nil, errors.New("alphabet or hash length is too short to encode given value") 87 } 88 89 for _, v := range indexes { 90 hash = append(hash, (*alphabet)[v]) 91 } 92 93 return hash, nil 94 }