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  }