github.com/decred/dcrlnd@v0.7.6/channeldb/witness_cache.go (about) 1 package channeldb 2 3 import ( 4 "fmt" 5 6 "github.com/decred/dcrlnd/kvdb" 7 "github.com/decred/dcrlnd/lntypes" 8 ) 9 10 var ( 11 // ErrNoWitnesses is an error that's returned when no new witnesses have 12 // been added to the WitnessCache. 13 ErrNoWitnesses = fmt.Errorf("no witnesses") 14 15 // ErrUnknownWitnessType is returned if a caller attempts to 16 ErrUnknownWitnessType = fmt.Errorf("unknown witness type") 17 ) 18 19 // WitnessType is enum that denotes what "type" of witness is being 20 // stored/retrieved. As the WitnessCache itself is agnostic and doesn't enforce 21 // any structure on added witnesses, we use this type to partition the 22 // witnesses on disk, and also to know how to map a witness to its look up key. 23 type WitnessType uint8 24 25 var ( 26 // Sha256HashWitness is a witness that is simply the pre image to a 27 // hash image. In order to map to its key, we'll use sha256. 28 Sha256HashWitness WitnessType = 1 29 ) 30 31 // toDBKey is a helper method that maps a witness type to the key that we'll 32 // use to store it within the database. 33 func (w WitnessType) toDBKey() ([]byte, error) { 34 switch w { 35 36 case Sha256HashWitness: 37 return []byte{byte(w)}, nil 38 39 default: 40 return nil, ErrUnknownWitnessType 41 } 42 } 43 44 var ( 45 // witnessBucketKey is the name of the bucket that we use to store all 46 // witnesses encountered. Within this bucket, we'll create a sub-bucket for 47 // each witness type. 48 witnessBucketKey = []byte("byte") 49 ) 50 51 // WitnessCache is a persistent cache of all witnesses we've encountered on the 52 // network. In the case of multi-hop, multi-step contracts, a cache of all 53 // witnesses can be useful in the case of partial contract resolution. If 54 // negotiations break down, we may be forced to locate the witness for a 55 // portion of the contract on-chain. In this case, we'll then add that witness 56 // to the cache so the incoming contract can fully resolve witness. 57 // Additionally, as one MUST always use a unique witness on the network, we may 58 // use this cache to detect duplicate witnesses. 59 // 60 // TODO(roasbeef): need expiry policy? 61 // - encrypt? 62 type WitnessCache struct { 63 db *DB 64 } 65 66 // NewWitnessCache returns a new instance of the witness cache. 67 func (d *DB) NewWitnessCache() *WitnessCache { 68 return &WitnessCache{ 69 db: d, 70 } 71 } 72 73 // witnessEntry is a key-value struct that holds each key -> witness pair, used 74 // when inserting records into the cache. 75 type witnessEntry struct { 76 key []byte 77 witness []byte 78 } 79 80 // AddSha256Witnesses adds a batch of new sha256 preimages into the witness 81 // cache. This is an alias for AddWitnesses that uses Sha256HashWitness as the 82 // preimages' witness type. 83 func (w *WitnessCache) AddSha256Witnesses(preimages ...lntypes.Preimage) error { 84 // Optimistically compute the preimages' hashes before attempting to 85 // start the db transaction. 86 entries := make([]witnessEntry, 0, len(preimages)) 87 for i := range preimages { 88 hash := preimages[i].Hash() 89 entries = append(entries, witnessEntry{ 90 key: hash[:], 91 witness: preimages[i][:], 92 }) 93 } 94 95 return w.addWitnessEntries(Sha256HashWitness, entries) 96 } 97 98 // addWitnessEntries inserts the witnessEntry key-value pairs into the cache, 99 // using the appropriate witness type to segment the namespace of possible 100 // witness types. 101 func (w *WitnessCache) addWitnessEntries(wType WitnessType, 102 entries []witnessEntry) error { 103 104 // Exit early if there are no witnesses to add. 105 if len(entries) == 0 { 106 return nil 107 } 108 109 return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error { 110 witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey) 111 if err != nil { 112 return err 113 } 114 115 witnessTypeBucketKey, err := wType.toDBKey() 116 if err != nil { 117 return err 118 } 119 witnessTypeBucket, err := witnessBucket.CreateBucketIfNotExists( 120 witnessTypeBucketKey, 121 ) 122 if err != nil { 123 return err 124 } 125 126 for _, entry := range entries { 127 err = witnessTypeBucket.Put(entry.key, entry.witness) 128 if err != nil { 129 return err 130 } 131 } 132 133 return nil 134 }) 135 } 136 137 // LookupSha256Witness attempts to lookup the preimage for a sha256 hash. If 138 // the witness isn't found, ErrNoWitnesses will be returned. 139 func (w *WitnessCache) LookupSha256Witness(hash lntypes.Hash) (lntypes.Preimage, error) { 140 witness, err := w.lookupWitness(Sha256HashWitness, hash[:]) 141 if err != nil { 142 return lntypes.Preimage{}, err 143 } 144 145 return lntypes.MakePreimage(witness) 146 } 147 148 // lookupWitness attempts to lookup a witness according to its type and also 149 // its witness key. In the case that the witness isn't found, ErrNoWitnesses 150 // will be returned. 151 func (w *WitnessCache) lookupWitness(wType WitnessType, witnessKey []byte) ([]byte, error) { 152 var witness []byte 153 err := kvdb.View(w.db, func(tx kvdb.RTx) error { 154 witnessBucket := tx.ReadBucket(witnessBucketKey) 155 if witnessBucket == nil { 156 return ErrNoWitnesses 157 } 158 159 witnessTypeBucketKey, err := wType.toDBKey() 160 if err != nil { 161 return err 162 } 163 witnessTypeBucket := witnessBucket.NestedReadBucket(witnessTypeBucketKey) 164 if witnessTypeBucket == nil { 165 return ErrNoWitnesses 166 } 167 168 dbWitness := witnessTypeBucket.Get(witnessKey) 169 if dbWitness == nil { 170 return ErrNoWitnesses 171 } 172 173 witness = make([]byte, len(dbWitness)) 174 copy(witness, dbWitness) 175 176 return nil 177 }, func() { 178 witness = nil 179 }) 180 if err != nil { 181 return nil, err 182 } 183 184 return witness, nil 185 } 186 187 // DeleteSha256Witness attempts to delete a sha256 preimage identified by hash. 188 func (w *WitnessCache) DeleteSha256Witness(hash lntypes.Hash) error { 189 return w.deleteWitness(Sha256HashWitness, hash[:]) 190 } 191 192 // deleteWitness attempts to delete a particular witness from the database. 193 func (w *WitnessCache) deleteWitness(wType WitnessType, witnessKey []byte) error { 194 return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error { 195 witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey) 196 if err != nil { 197 return err 198 } 199 200 witnessTypeBucketKey, err := wType.toDBKey() 201 if err != nil { 202 return err 203 } 204 witnessTypeBucket, err := witnessBucket.CreateBucketIfNotExists( 205 witnessTypeBucketKey, 206 ) 207 if err != nil { 208 return err 209 } 210 211 return witnessTypeBucket.Delete(witnessKey) 212 }) 213 } 214 215 // DeleteWitnessClass attempts to delete an *entire* class of witnesses. After 216 // this function return with a non-nil error, 217 func (w *WitnessCache) DeleteWitnessClass(wType WitnessType) error { 218 return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error { 219 witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey) 220 if err != nil { 221 return err 222 } 223 224 witnessTypeBucketKey, err := wType.toDBKey() 225 if err != nil { 226 return err 227 } 228 229 return witnessBucket.DeleteNestedBucket(witnessTypeBucketKey) 230 }) 231 }