github.com/number571/tendermint@v0.34.11-gost/light/store/db/db.go (about) 1 package db 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 7 "github.com/google/orderedcode" 8 dbm "github.com/tendermint/tm-db" 9 10 tmsync "github.com/number571/tendermint/internal/libs/sync" 11 "github.com/number571/tendermint/light/store" 12 tmproto "github.com/number571/tendermint/proto/tendermint/types" 13 "github.com/number571/tendermint/types" 14 ) 15 16 const ( 17 prefixLightBlock = int64(11) 18 prefixSize = int64(12) 19 ) 20 21 type dbs struct { 22 db dbm.DB 23 24 mtx tmsync.RWMutex 25 size uint16 26 } 27 28 // New returns a Store that wraps any DB 29 // If you want to share one DB across many light clients consider using PrefixDB 30 func New(db dbm.DB) store.Store { 31 32 lightStore := &dbs{db: db} 33 34 // retrieve the size of the db 35 size := uint16(0) 36 bz, err := lightStore.db.Get(lightStore.sizeKey()) 37 if err == nil && len(bz) > 0 { 38 size = unmarshalSize(bz) 39 } 40 lightStore.size = size 41 42 return lightStore 43 } 44 45 // SaveLightBlock persists LightBlock to the db. 46 // 47 // Safe for concurrent use by multiple goroutines. 48 func (s *dbs) SaveLightBlock(lb *types.LightBlock) error { 49 if lb.Height <= 0 { 50 panic("negative or zero height") 51 } 52 53 lbpb, err := lb.ToProto() 54 if err != nil { 55 return fmt.Errorf("unable to convert light block to protobuf: %w", err) 56 } 57 58 lbBz, err := lbpb.Marshal() 59 if err != nil { 60 return fmt.Errorf("marshaling LightBlock: %w", err) 61 } 62 63 s.mtx.Lock() 64 defer s.mtx.Unlock() 65 66 b := s.db.NewBatch() 67 defer b.Close() 68 if err = b.Set(s.lbKey(lb.Height), lbBz); err != nil { 69 return err 70 } 71 if err = b.Set(s.sizeKey(), marshalSize(s.size+1)); err != nil { 72 return err 73 } 74 if err = b.WriteSync(); err != nil { 75 return err 76 } 77 s.size++ 78 79 return nil 80 } 81 82 // DeleteLightBlockAndValidatorSet deletes the LightBlock from 83 // the db. 84 // 85 // Safe for concurrent use by multiple goroutines. 86 func (s *dbs) DeleteLightBlock(height int64) error { 87 if height <= 0 { 88 panic("negative or zero height") 89 } 90 91 s.mtx.Lock() 92 defer s.mtx.Unlock() 93 94 b := s.db.NewBatch() 95 defer b.Close() 96 if err := b.Delete(s.lbKey(height)); err != nil { 97 return err 98 } 99 if err := b.Set(s.sizeKey(), marshalSize(s.size-1)); err != nil { 100 return err 101 } 102 if err := b.WriteSync(); err != nil { 103 return err 104 } 105 s.size-- 106 107 return nil 108 } 109 110 // LightBlock retrieves the LightBlock at the given height. 111 // 112 // Safe for concurrent use by multiple goroutines. 113 func (s *dbs) LightBlock(height int64) (*types.LightBlock, error) { 114 if height <= 0 { 115 panic("negative or zero height") 116 } 117 118 bz, err := s.db.Get(s.lbKey(height)) 119 if err != nil { 120 panic(err) 121 } 122 if len(bz) == 0 { 123 return nil, store.ErrLightBlockNotFound 124 } 125 126 var lbpb tmproto.LightBlock 127 err = lbpb.Unmarshal(bz) 128 if err != nil { 129 return nil, fmt.Errorf("unmarshal error: %w", err) 130 } 131 132 lightBlock, err := types.LightBlockFromProto(&lbpb) 133 if err != nil { 134 return nil, fmt.Errorf("proto conversion error: %w", err) 135 } 136 137 return lightBlock, err 138 } 139 140 // LastLightBlockHeight returns the last LightBlock height stored. 141 // 142 // Safe for concurrent use by multiple goroutines. 143 func (s *dbs) LastLightBlockHeight() (int64, error) { 144 itr, err := s.db.ReverseIterator( 145 s.lbKey(1), 146 append(s.lbKey(1<<63-1), byte(0x00)), 147 ) 148 if err != nil { 149 panic(err) 150 } 151 defer itr.Close() 152 153 if itr.Valid() { 154 return s.decodeLbKey(itr.Key()) 155 } 156 157 return -1, itr.Error() 158 } 159 160 // FirstLightBlockHeight returns the first LightBlock height stored. 161 // 162 // Safe for concurrent use by multiple goroutines. 163 func (s *dbs) FirstLightBlockHeight() (int64, error) { 164 itr, err := s.db.Iterator( 165 s.lbKey(1), 166 append(s.lbKey(1<<63-1), byte(0x00)), 167 ) 168 if err != nil { 169 panic(err) 170 } 171 defer itr.Close() 172 173 if itr.Valid() { 174 return s.decodeLbKey(itr.Key()) 175 } 176 177 return -1, itr.Error() 178 } 179 180 // LightBlockBefore iterates over light blocks until it finds a block before 181 // the given height. It returns ErrLightBlockNotFound if no such block exists. 182 // 183 // Safe for concurrent use by multiple goroutines. 184 func (s *dbs) LightBlockBefore(height int64) (*types.LightBlock, error) { 185 if height <= 0 { 186 panic("negative or zero height") 187 } 188 189 itr, err := s.db.ReverseIterator( 190 s.lbKey(1), 191 s.lbKey(height), 192 ) 193 if err != nil { 194 panic(err) 195 } 196 defer itr.Close() 197 198 if itr.Valid() { 199 var lbpb tmproto.LightBlock 200 err = lbpb.Unmarshal(itr.Value()) 201 if err != nil { 202 return nil, fmt.Errorf("unmarshal error: %w", err) 203 } 204 205 lightBlock, err := types.LightBlockFromProto(&lbpb) 206 if err != nil { 207 return nil, fmt.Errorf("proto conversion error: %w", err) 208 } 209 return lightBlock, nil 210 } 211 if err = itr.Error(); err != nil { 212 return nil, err 213 } 214 215 return nil, store.ErrLightBlockNotFound 216 } 217 218 // Prune prunes header & validator set pairs until there are only size pairs 219 // left. 220 // 221 // Safe for concurrent use by multiple goroutines. 222 func (s *dbs) Prune(size uint16) error { 223 // 1) Check how many we need to prune. 224 s.mtx.Lock() 225 defer s.mtx.Unlock() 226 sSize := s.size 227 228 if sSize <= size { // nothing to prune 229 return nil 230 } 231 numToPrune := sSize - size 232 233 b := s.db.NewBatch() 234 defer b.Close() 235 236 // 2) use an iterator to batch together all the blocks that need to be deleted 237 if err := s.batchDelete(b, numToPrune); err != nil { 238 return err 239 } 240 241 // 3) // update size 242 s.size = size 243 if err := b.Set(s.sizeKey(), marshalSize(size)); err != nil { 244 return fmt.Errorf("failed to persist size: %w", err) 245 } 246 247 // 4) write batch deletion to disk 248 return b.WriteSync() 249 } 250 251 // Size returns the number of header & validator set pairs. 252 // 253 // Safe for concurrent use by multiple goroutines. 254 func (s *dbs) Size() uint16 { 255 s.mtx.RLock() 256 defer s.mtx.RUnlock() 257 return s.size 258 } 259 260 func (s *dbs) batchDelete(batch dbm.Batch, numToPrune uint16) error { 261 itr, err := s.db.Iterator( 262 s.lbKey(1), 263 append(s.lbKey(1<<63-1), byte(0x00)), 264 ) 265 if err != nil { 266 return err 267 } 268 defer itr.Close() 269 270 for itr.Valid() && numToPrune > 0 { 271 if err = batch.Delete(itr.Key()); err != nil { 272 return err 273 } 274 itr.Next() 275 numToPrune-- 276 } 277 278 return itr.Error() 279 } 280 281 func (s *dbs) sizeKey() []byte { 282 key, err := orderedcode.Append(nil, prefixSize) 283 if err != nil { 284 panic(err) 285 } 286 return key 287 } 288 289 func (s *dbs) lbKey(height int64) []byte { 290 key, err := orderedcode.Append(nil, prefixLightBlock, height) 291 if err != nil { 292 panic(err) 293 } 294 return key 295 } 296 297 func (s *dbs) decodeLbKey(key []byte) (height int64, err error) { 298 var lightBlockPrefix int64 299 remaining, err := orderedcode.Parse(string(key), &lightBlockPrefix, &height) 300 if err != nil { 301 err = fmt.Errorf("failed to parse light block key: %w", err) 302 } 303 if len(remaining) != 0 { 304 err = fmt.Errorf("expected no remainder when parsing light block key but got: %s", remaining) 305 } 306 if lightBlockPrefix != prefixLightBlock { 307 err = fmt.Errorf("expected light block prefix but got: %d", lightBlockPrefix) 308 } 309 return 310 } 311 312 func marshalSize(size uint16) []byte { 313 bs := make([]byte, 2) 314 binary.LittleEndian.PutUint16(bs, size) 315 return bs 316 } 317 318 func unmarshalSize(bz []byte) uint16 { 319 return binary.LittleEndian.Uint16(bz) 320 }