github.com/metacurrency/holochain@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/hash/hash.go (about) 1 // Copyright (C) 2013-2018, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.) 2 // Use of this source code is governed by GPLv3 found in the LICENSE file 3 //---------------------------------------------------------------------------------------- 4 // Hash type for Holochains 5 // Holochain hashes are SHA256 binary values encoded to strings as base58 6 7 package hash 8 9 import ( 10 "encoding/binary" 11 peer "github.com/libp2p/go-libp2p-peer" 12 mh "github.com/multiformats/go-multihash" 13 "io" 14 "math/big" 15 "sort" 16 ) 17 18 // Hash of Entry's Content 19 type Hash string 20 21 // HashSpec holds the info that tells what kind of hash this is 22 type HashSpec struct { 23 Code uint64 24 Length int 25 } 26 27 // NewHash builds a Hash from a b58 string encoded hash 28 func NewHash(s string) (h Hash, err error) { 29 var multiH mh.Multihash 30 multiH, err = mh.FromB58String(s) 31 h = Hash(multiH) 32 return 33 } 34 35 // HashFromBytes cast a byte slice to Hash type, and validate 36 // the id to make sure it is a multihash. 37 func HashFromBytes(b []byte) (h Hash, err error) { 38 var multiH mh.Multihash 39 if multiH, err = mh.Cast(b); err != nil { 40 h = NullHash() 41 return 42 } 43 h = Hash(multiH) 44 return 45 } 46 47 // HashFromPeerID copy the bytes from a peer ID to Hash. 48 // Hashes and peer ID's are the exact same format, a multihash. 49 // NOTE: assumes that the multihash is valid 50 func HashFromPeerID(id peer.ID) (h Hash) { 51 return Hash(id) 52 } 53 54 // PeerIDFromHash copy the bytes from a hash to a peer ID. 55 // Hashes and peer ID's are the exact same format, a multihash. 56 // NOTE: assumes that the multihash is valid 57 func PeerIDFromHash(h Hash) (id peer.ID) { 58 id = peer.ID(h) 59 return 60 } 61 62 // String encodes a hash to a human readable string 63 func (h Hash) String() string { 64 //if cap(h) == 0 { 65 // return "" 66 //} 67 return mh.Multihash(h).B58String() 68 } 69 70 // Sum builds a digest according to the specs in the Holochain 71 func Sum(hc HashSpec, data []byte) (hash Hash, err error) { 72 var multiH mh.Multihash 73 multiH, err = mh.Sum(data, hc.Code, hc.Length) 74 hash = Hash(multiH) 75 return 76 } 77 78 // IsNullHash checks to see if this hash's value is the null hash 79 func (h Hash) IsNullHash() bool { 80 return h == "" 81 //return cap(h) == 1 && h[0] == 0 82 } 83 84 // NullHash builds a null valued hash 85 func NullHash() (h Hash) { 86 // null := [1]byte{0} 87 // h = null[:] 88 return "" 89 } 90 91 // Clone returns a copy of a hash 92 func (h Hash) Clone() (hash Hash) { 93 // hash = make([]byte, len(h)) 94 // copy(hash, h) 95 hash = h 96 return 97 } 98 99 // Equal checks to see if two hashes have the same value 100 func (h Hash) Equal(h2 Hash) bool { 101 if h.IsNullHash() && h2.IsNullHash() { 102 return true 103 } 104 return h == h2 105 } 106 107 // MarshalHash writes a hash to a binary stream 108 func (h Hash) MarshalHash(writer io.Writer) (err error) { 109 if h.IsNullHash() { 110 b := make([]byte, 34) 111 err = binary.Write(writer, binary.LittleEndian, b) 112 } else { 113 err = binary.Write(writer, binary.LittleEndian, []byte(h)) 114 } 115 return 116 } 117 118 // UnmarshalHash reads a hash from a binary stream 119 func UnmarshalHash(reader io.Reader) (hash Hash, err error) { 120 b := make([]byte, 34) 121 err = binary.Read(reader, binary.LittleEndian, b) 122 if err == nil { 123 if b[0] == 0 { 124 hash = NullHash() 125 } else { 126 hash = Hash(b) 127 } 128 } 129 return 130 } 131 132 // XOR takes two byte slices, XORs them together, returns the resulting slice. 133 // taken from https://github.com/ipfs/go-ipfs-util/blob/master/util.go 134 func XOR(a, b []byte) []byte { 135 c := make([]byte, len(a)) 136 for i := 0; i < len(a); i++ { 137 c[i] = a[i] ^ b[i] 138 } 139 return c 140 } 141 142 // The code below is adapted from https://github.com/libp2p/go-libp2p-kbucket 143 144 // Distance returns the distance metric between two hashes 145 func HashXORDistance(h1, h2 Hash) *big.Int { 146 // XOR the hashes 147 k3 := XOR([]byte(h1), []byte(h2)) 148 149 // interpret it as an integer 150 dist := big.NewInt(0).SetBytes(k3) 151 return dist 152 } 153 154 // Less returns whether the first key is smaller than the second. 155 func HashLess(h1, h2 Hash) bool { 156 a := h1 157 b := h2 158 for i := 0; i < len(a); i++ { 159 if a[i] != b[i] { 160 return a[i] < b[i] 161 } 162 } 163 return true 164 } 165 166 // ZeroPrefixLen returns the number of consecutive zeroes in a byte slice. 167 func ZeroPrefixLen(id []byte) int { 168 for i := 0; i < len(id); i++ { 169 for j := 0; j < 8; j++ { 170 if (id[i]>>uint8(7-j))&0x1 != 0 { 171 return i*8 + j 172 } 173 } 174 } 175 return len(id) * 8 176 } 177 178 // hashDistance helper struct for sorting by distance which pre-caches the distance 179 // to center so as not to recalculate it on every sort comparison. 180 type HashDistance struct { 181 Hash interface{} 182 Distance *big.Int 183 } 184 185 type HashSorterArr []*HashDistance 186 187 func (p HashSorterArr) Len() int { return len(p) } 188 func (p HashSorterArr) Swap(a, b int) { p[a], p[b] = p[b], p[a] } 189 func (p HashSorterArr) Less(a, b int) bool { 190 return p[a].Distance.Cmp(p[b].Distance) == -1 191 } 192 193 // SortByDistance takes a center Hash, and a list of Hashes toSort. 194 // It returns a new list, where the Hashes toSort have been sorted by their 195 // distance to the center Hash. 196 func SortByDistance(center Hash, toSort []Hash) []Hash { 197 var hsarr HashSorterArr 198 for _, h := range toSort { 199 hd := &HashDistance{ 200 Hash: h, 201 Distance: HashXORDistance(h, center), 202 } 203 hsarr = append(hsarr, hd) 204 } 205 sort.Sort(hsarr) 206 var out []Hash 207 for _, hd := range hsarr { 208 out = append(out, hd.Hash.(Hash)) 209 } 210 return out 211 }