github.com/decred/dcrlnd@v0.7.6/shachain/element.go (about) 1 package shachain 2 3 import ( 4 "crypto/sha256" 5 "errors" 6 ) 7 8 // element represents the entity which contains the hash and index 9 // corresponding to it. An element is the output of the shachain PRF. By 10 // comparing two indexes we're able to mutate the hash in such way to derive 11 // another element. 12 type element struct { 13 index index 14 hash ShaHash 15 } 16 17 // newElementFromStr creates new element from the given hash string. 18 func newElementFromStr(s string, index index) (*element, error) { 19 hash, err := NewHashFromStr(s) 20 if err != nil { 21 return nil, err 22 } 23 24 return &element{ 25 index: index, 26 hash: *hash, 27 }, nil 28 } 29 30 // derive computes one shachain element from another by applying a series of 31 // bit flips and hashing operations based on the starting and ending index. 32 func (e *element) derive(toIndex index) (*element, error) { 33 fromIndex := e.index 34 35 positions, err := fromIndex.deriveBitTransformations(toIndex) 36 if err != nil { 37 return nil, err 38 } 39 40 buf := e.hash 41 for _, position := range positions { 42 // Flip the bit and then hash the current state. 43 byteNumber := position / 8 44 bitNumber := position % 8 45 46 buf[byteNumber] ^= (1 << bitNumber) 47 48 buf = sha256.Sum256(buf[:]) 49 } 50 51 return &element{ 52 index: toIndex, 53 hash: buf, 54 }, nil 55 } 56 57 // isEqual returns true if two elements are identical and false otherwise. 58 func (e *element) isEqual(e2 *element) bool { 59 return (e.index == e2.index) && e.hash == e2.hash 60 } 61 62 const ( 63 // maxHeight is used to determine the maximum allowable index and the 64 // length of the array required to order to derive all previous hashes 65 // by index. The entries of this array as also known as buckets. 66 maxHeight uint8 = 48 67 68 // rootIndex is an index which corresponds to the root hash. 69 rootIndex index = 0 70 ) 71 72 // startIndex is the index of first element in the shachain PRF. 73 var startIndex index = (1 << maxHeight) - 1 74 75 // index is a number which identifies the hash number and serves as a way to 76 // determine the hashing operation required to derive one hash from another. 77 // index is initialized with the startIndex and decreases down to zero with 78 // successive derivations. 79 type index uint64 80 81 // newIndex is used to create index instance. The inner operations with index 82 // implies that index decreasing from some max number to zero, but for 83 // simplicity and backward compatibility with previous logic it was transformed 84 // to work in opposite way. 85 func newIndex(v uint64) index { 86 return startIndex - index(v) 87 } 88 89 // deriveBitTransformations function checks that the 'to' index is derivable 90 // from the 'from' index by checking the indexes are prefixes of another. The 91 // bit positions where the zeroes should be changed to ones in order for the 92 // indexes to become the same are returned. This set of bits is needed in order 93 // to derive one hash from another. 94 // 95 // NOTE: The index 'to' is derivable from index 'from' iff index 'from' lies 96 // left and above index 'to' on graph below, for example: 97 // 1. 7(0b111) -> 7 98 // 2. 6(0b110) -> 6,7 99 // 3. 5(0b101) -> 5 100 // 4. 4(0b100) -> 4,5,6,7 101 // 5. 3(0b011) -> 3 102 // 6. 2(0b010) -> 2, 3 103 // 7. 1(0b001) -> 1 104 // 105 // ^ bucket number 106 // | 107 // 3 | x 108 // | | 109 // 2 | | x 110 // | | | 111 // 1 | | x | x 112 // | | | | | 113 // 0 | | x | x | x | x 114 // | | | | | | | | | 115 // +---|---|---|---|---|---|---|---|---> index 116 // 0 1 2 3 4 5 6 7 117 func (from index) deriveBitTransformations(to index) ([]uint8, error) { 118 var positions []uint8 119 120 if from == to { 121 return positions, nil 122 } 123 124 // + --------------- + 125 // | № | from | to | 126 // + -- + ---- + --- + 127 // | 48 | 1 | 1 | 128 // | 47 | 0 | 0 | [48-5] - same part of 'from' and 'to' 129 // | 46 | 0 | 0 | indexes which also is called prefix. 130 // .... 131 // | 5 | 1 | 1 | 132 // | 4 | 0 | 1 | <--- position after which indexes becomes 133 // | 3 | 0 | 0 | different, after this position 134 // | 2 | 0 | 1 | bits in 'from' index all should be 135 // | 1 | 0 | 0 | zeros or such indexes considered to be 136 // | 0 | 0 | 1 | not derivable. 137 // + -- + ---- + --- + 138 zeros := countTrailingZeros(from) 139 if uint64(from) != getPrefix(to, zeros) { 140 return nil, errors.New("prefixes are different - indexes " + 141 "aren't derivable") 142 } 143 144 // The remaining part of 'to' index represents the positions which we 145 // will use then in order to derive one element from another. 146 for position := zeros - 1; ; position-- { 147 if getBit(to, position) == 1 { 148 positions = append(positions, position) 149 } 150 151 if position == 0 { 152 break 153 } 154 } 155 156 return positions, nil 157 }