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  }