github.com/status-im/status-go@v1.1.0/protocol/identity/colorhash/colorhash.go (about) 1 package colorhash 2 3 import ( 4 "math/big" 5 6 "github.com/status-im/status-go/multiaccounts" 7 "github.com/status-im/status-go/protocol/identity" 8 ) 9 10 const ( 11 colorHashSegmentMaxLen = 5 12 colorHashColorsCount = 32 13 ) 14 15 var colorHashAlphabet [][]int 16 17 func GenerateFor(pubkey string) (hash multiaccounts.ColorHash, err error) { 18 if len(colorHashAlphabet) == 0 { 19 colorHashAlphabet = makeColorHashAlphabet(colorHashSegmentMaxLen, colorHashColorsCount) 20 } 21 22 compressedKey, err := identity.ToCompressedKey(pubkey) 23 if err != nil { 24 return nil, err 25 } 26 27 slices, err := identity.Slices(compressedKey) 28 if err != nil { 29 return nil, err 30 } 31 32 return toColorHash(new(big.Int).SetBytes(slices[2]), &colorHashAlphabet, colorHashColorsCount), nil 33 } 34 35 // [[1 0] [1 1] [1 2] ... [units, colors-1]] 36 // [3 12] => 3 units length, 12 color index 37 func makeColorHashAlphabet(units, colors int) (res [][]int) { 38 res = make([][]int, units*colors) 39 idx := 0 40 for i := 0; i < units; i++ { 41 for j := 0; j < colors; j++ { 42 res[idx] = make([]int, 2) 43 res[idx][0] = i + 1 44 res[idx][1] = j 45 idx++ 46 } 47 } 48 return 49 } 50 51 func toColorHash(value *big.Int, alphabet *[][]int, colorsCount int) (hash multiaccounts.ColorHash) { 52 alphabetLen := len(*alphabet) 53 indexes := identity.ToBigBase(value, uint64(alphabetLen)) 54 hash = make(multiaccounts.ColorHash, len(indexes)) 55 for i, v := range indexes { 56 hash[i] = [2]int{} 57 hash[i][0] = (*alphabet)[v][0] 58 hash[i][1] = (*alphabet)[v][1] 59 } 60 61 // colors can't repeat themselves 62 // this makes color hash not fully collision resistant 63 prevColorIdx := hash[0][1] 64 hashLen := len(hash) 65 for i := 1; i < hashLen; i++ { 66 colorIdx := hash[i][1] 67 if colorIdx == prevColorIdx { 68 hash[i][1] = (colorIdx + 1) % colorsCount 69 } 70 prevColorIdx = hash[i][1] 71 } 72 73 return 74 }