github.com/decred/dcrlnd@v0.7.6/shachain/store.go (about) 1 package shachain 2 3 import ( 4 "encoding/binary" 5 "io" 6 7 "github.com/go-errors/errors" 8 ) 9 10 // Store is an interface which serves as an abstraction over data structure 11 // responsible for efficiently storing and restoring hash secrets by given 12 // indexes. 13 // 14 // Description: The Lightning Network wants a chain of (say 1 million) 15 // unguessable 256 bit values; we generate them and send them one at a time to 16 // a remote node. We don't want the remote node to have to store all the 17 // values, so it's better if they can derive them once they see them. 18 type Store interface { 19 // LookUp function is used to restore/lookup/fetch the previous secret 20 // by its index. 21 LookUp(uint64) (*ShaHash, error) 22 23 // AddNextEntry attempts to store the given hash within its internal 24 // storage in an efficient manner. 25 // 26 // NOTE: The hashes derived from the shachain MUST be inserted in the 27 // order they're produced by a shachain.Producer. 28 AddNextEntry(*ShaHash) error 29 30 // Encode writes a binary serialization of the shachain elements 31 // currently saved by implementation of shachain.Store to the passed 32 // io.Writer. 33 Encode(io.Writer) error 34 } 35 36 // RevocationStore is a concrete implementation of the Store interface. The 37 // revocation store is able to efficiently store N derived shachain elements in 38 // a space efficient manner with a space complexity of O(log N). The original 39 // description of the storage methodology can be found here: 40 // https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#efficient-per-commitment-secret-storage 41 type RevocationStore struct { 42 // lenBuckets stores the number of currently active buckets. 43 lenBuckets uint8 44 45 // buckets is an array of elements from which we may derive all 46 // previous elements, each bucket corresponds to the element with the 47 // particular number of trailing zeros. 48 buckets [maxHeight]element 49 50 // index is an available index which will be assigned to the new 51 // element. 52 index index 53 } 54 55 // A compile time check to ensure RevocationStore implements the Store 56 // interface. 57 var _ Store = (*RevocationStore)(nil) 58 59 // NewRevocationStore creates the new shachain store. 60 func NewRevocationStore() *RevocationStore { 61 return &RevocationStore{ 62 lenBuckets: 0, 63 index: startIndex, 64 } 65 } 66 67 // NewRevocationStoreFromBytes recreates the initial store state from the given 68 // binary shachain store representation. 69 func NewRevocationStoreFromBytes(r io.Reader) (*RevocationStore, error) { 70 store := &RevocationStore{} 71 72 if err := binary.Read(r, binary.BigEndian, &store.lenBuckets); err != nil { 73 return nil, err 74 } 75 76 for i := uint8(0); i < store.lenBuckets; i++ { 77 var hashIndex index 78 err := binary.Read(r, binary.BigEndian, &hashIndex) 79 if err != nil { 80 return nil, err 81 } 82 83 var nextHash ShaHash 84 if _, err := io.ReadFull(r, nextHash[:]); err != nil { 85 return nil, err 86 } 87 88 store.buckets[i] = element{ 89 index: hashIndex, 90 hash: nextHash, 91 } 92 } 93 94 if err := binary.Read(r, binary.BigEndian, &store.index); err != nil { 95 return nil, err 96 } 97 98 return store, nil 99 } 100 101 // LookUp function is used to restore/lookup/fetch the previous secret by its 102 // index. If secret which corresponds to given index was not previously placed 103 // in store we will not able to derive it and function will fail. 104 // 105 // NOTE: This function is part of the Store interface. 106 func (store *RevocationStore) LookUp(v uint64) (*ShaHash, error) { 107 ind := newIndex(v) 108 109 // Trying to derive the index from one of the existing buckets elements. 110 for i := uint8(0); i < store.lenBuckets; i++ { 111 element, err := store.buckets[i].derive(ind) 112 if err != nil { 113 continue 114 } 115 116 return &element.hash, nil 117 } 118 119 return nil, errors.Errorf("unable to derive hash #%v", ind) 120 } 121 122 // AddNextEntry attempts to store the given hash within its internal storage in 123 // an efficient manner. 124 // 125 // NOTE: The hashes derived from the shachain MUST be inserted in the order 126 // they're produced by a shachain.Producer. 127 // 128 // NOTE: This function is part of the Store interface. 129 func (store *RevocationStore) AddNextEntry(hash *ShaHash) error { 130 newElement := &element{ 131 index: store.index, 132 hash: *hash, 133 } 134 135 bucket := countTrailingZeros(newElement.index) 136 137 for i := uint8(0); i < bucket; i++ { 138 e, err := newElement.derive(store.buckets[i].index) 139 if err != nil { 140 return err 141 } 142 143 if !e.isEqual(&store.buckets[i]) { 144 return errors.New("hash isn't derivable from " + 145 "previous ones") 146 } 147 } 148 149 store.buckets[bucket] = *newElement 150 if bucket+1 > store.lenBuckets { 151 store.lenBuckets = bucket + 1 152 } 153 154 store.index-- 155 return nil 156 } 157 158 // Encode writes a binary serialization of the shachain elements currently 159 // saved by implementation of shachain.Store to the passed io.Writer. 160 // 161 // NOTE: This function is part of the Store interface. 162 func (store *RevocationStore) Encode(w io.Writer) error { 163 err := binary.Write(w, binary.BigEndian, store.lenBuckets) 164 if err != nil { 165 return err 166 } 167 168 for i := uint8(0); i < store.lenBuckets; i++ { 169 element := store.buckets[i] 170 171 err := binary.Write(w, binary.BigEndian, element.index) 172 if err != nil { 173 return err 174 } 175 176 if _, err = w.Write(element.hash[:]); err != nil { 177 return err 178 } 179 180 } 181 182 return binary.Write(w, binary.BigEndian, store.index) 183 }