github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/light/store/db/db.go (about) 1 package db 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "regexp" 7 "strconv" 8 9 "github.com/gogo/protobuf/proto" 10 dbm "github.com/tendermint/tm-db" 11 12 tmsync "github.com/tendermint/tendermint/libs/sync" 13 "github.com/tendermint/tendermint/light/store" 14 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 15 "github.com/tendermint/tendermint/types" 16 ) 17 18 var ( 19 sizeKey = []byte("size") 20 ) 21 22 type dbs struct { 23 db dbm.DB 24 prefix string 25 26 mtx tmsync.RWMutex 27 size uint16 28 } 29 30 // New returns a Store that wraps any DB (with an optional prefix in case you 31 // want to use one DB with many light clients). 32 func New(db dbm.DB, prefix string) store.Store { 33 34 size := uint16(0) 35 bz, err := db.Get(sizeKey) 36 if err == nil && len(bz) > 0 { 37 size = unmarshalSize(bz) 38 } 39 40 return &dbs{db: db, prefix: prefix, size: size} 41 } 42 43 // SaveSignedHeaderAndValidatorSet persists SignedHeader and ValidatorSet to 44 // the db. 45 // 46 // Safe for concurrent use by multiple goroutines. 47 func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error { 48 if sh.Height <= 0 { 49 panic("negative or zero height") 50 } 51 52 pbsh := sh.ToProto() 53 54 shBz, err := proto.Marshal(pbsh) 55 if err != nil { 56 return fmt.Errorf("marshalling SignedHeader: %w", err) 57 } 58 59 pbvs, err := valSet.ToProto() 60 if err != nil { 61 return fmt.Errorf("unable to transition validator set to protobuf: %w", err) 62 } 63 64 valSetBz, err := proto.Marshal(pbvs) 65 if err != nil { 66 return fmt.Errorf("marshalling validator set: %w", err) 67 } 68 69 s.mtx.Lock() 70 defer s.mtx.Unlock() 71 72 b := s.db.NewBatch() 73 defer b.Close() 74 if err = b.Set(s.shKey(sh.Height), shBz); err != nil { 75 return err 76 } 77 if err = b.Set(s.vsKey(sh.Height), valSetBz); err != nil { 78 return err 79 } 80 if err = b.Set(sizeKey, marshalSize(s.size+1)); err != nil { 81 return err 82 } 83 if err = b.WriteSync(); err != nil { 84 return err 85 } 86 s.size++ 87 88 return nil 89 } 90 91 // DeleteSignedHeaderAndValidatorSet deletes SignedHeader and ValidatorSet from 92 // the db. 93 // 94 // Safe for concurrent use by multiple goroutines. 95 func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error { 96 if height <= 0 { 97 panic("negative or zero height") 98 } 99 100 s.mtx.Lock() 101 defer s.mtx.Unlock() 102 103 b := s.db.NewBatch() 104 defer b.Close() 105 if err := b.Delete(s.shKey(height)); err != nil { 106 return err 107 } 108 if err := b.Delete(s.vsKey(height)); err != nil { 109 return err 110 } 111 if err := b.Set(sizeKey, marshalSize(s.size-1)); err != nil { 112 return err 113 } 114 if err := b.WriteSync(); err != nil { 115 return err 116 } 117 s.size-- 118 119 return nil 120 } 121 122 // SignedHeader loads SignedHeader at the given height. 123 // 124 // Safe for concurrent use by multiple goroutines. 125 func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) { 126 if height <= 0 { 127 panic("negative or zero height") 128 } 129 130 bz, err := s.db.Get(s.shKey(height)) 131 if err != nil { 132 panic(err) 133 } 134 if len(bz) == 0 { 135 return nil, store.ErrSignedHeaderNotFound 136 } 137 138 var pbsh tmproto.SignedHeader 139 err = proto.Unmarshal(bz, &pbsh) 140 if err != nil { 141 return nil, err 142 } 143 144 signedHeader, err := types.SignedHeaderFromProto(&pbsh) 145 if err != nil { 146 return nil, err 147 } 148 149 return signedHeader, err 150 } 151 152 // ValidatorSet loads ValidatorSet at the given height. 153 // 154 // Safe for concurrent use by multiple goroutines. 155 func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) { 156 if height <= 0 { 157 panic("negative or zero height") 158 } 159 160 bz, err := s.db.Get(s.vsKey(height)) 161 if err != nil { 162 panic(err) 163 } 164 if len(bz) == 0 { 165 return nil, store.ErrValidatorSetNotFound 166 } 167 168 var pbvs tmproto.ValidatorSet 169 err = proto.Unmarshal(bz, &pbvs) 170 if err != nil { 171 return nil, err 172 } 173 174 valSet, err := types.ValidatorSetFromProto(&pbvs) 175 if err != nil { 176 return nil, err 177 } 178 179 return valSet, err 180 } 181 182 // LastSignedHeaderHeight returns the last SignedHeader height stored. 183 // 184 // Safe for concurrent use by multiple goroutines. 185 func (s *dbs) LastSignedHeaderHeight() (int64, error) { 186 itr, err := s.db.ReverseIterator( 187 s.shKey(1), 188 append(s.shKey(1<<63-1), byte(0x00)), 189 ) 190 if err != nil { 191 panic(err) 192 } 193 defer itr.Close() 194 195 for itr.Valid() { 196 key := itr.Key() 197 _, height, ok := parseShKey(key) 198 if ok { 199 return height, nil 200 } 201 itr.Next() 202 } 203 204 return -1, itr.Error() 205 } 206 207 // FirstSignedHeaderHeight returns the first SignedHeader height stored. 208 // 209 // Safe for concurrent use by multiple goroutines. 210 func (s *dbs) FirstSignedHeaderHeight() (int64, error) { 211 itr, err := s.db.Iterator( 212 s.shKey(1), 213 append(s.shKey(1<<63-1), byte(0x00)), 214 ) 215 if err != nil { 216 panic(err) 217 } 218 defer itr.Close() 219 220 for itr.Valid() { 221 key := itr.Key() 222 _, height, ok := parseShKey(key) 223 if ok { 224 return height, nil 225 } 226 itr.Next() 227 } 228 229 return -1, itr.Error() 230 } 231 232 // SignedHeaderBefore iterates over headers until it finds a header before 233 // the given height. It returns ErrSignedHeaderNotFound if no such header exists. 234 // 235 // Safe for concurrent use by multiple goroutines. 236 func (s *dbs) SignedHeaderBefore(height int64) (*types.SignedHeader, error) { 237 if height <= 0 { 238 panic("negative or zero height") 239 } 240 241 itr, err := s.db.ReverseIterator( 242 s.shKey(1), 243 s.shKey(height), 244 ) 245 if err != nil { 246 panic(err) 247 } 248 defer itr.Close() 249 250 for itr.Valid() { 251 key := itr.Key() 252 _, existingHeight, ok := parseShKey(key) 253 if ok { 254 return s.SignedHeader(existingHeight) 255 } 256 itr.Next() 257 } 258 if err = itr.Error(); err != nil { 259 return nil, err 260 } 261 262 return nil, store.ErrSignedHeaderNotFound 263 } 264 265 // Prune prunes header & validator set pairs until there are only size pairs 266 // left. 267 // 268 // Safe for concurrent use by multiple goroutines. 269 func (s *dbs) Prune(size uint16) error { 270 // 1) Check how many we need to prune. 271 s.mtx.RLock() 272 sSize := s.size 273 s.mtx.RUnlock() 274 275 if sSize <= size { // nothing to prune 276 return nil 277 } 278 numToPrune := sSize - size 279 280 // 2) Iterate over headers and perform a batch operation. 281 itr, err := s.db.Iterator( 282 s.shKey(1), 283 append(s.shKey(1<<63-1), byte(0x00)), 284 ) 285 if err != nil { 286 return err 287 } 288 defer itr.Close() 289 290 b := s.db.NewBatch() 291 defer b.Close() 292 293 pruned := 0 294 for itr.Valid() && numToPrune > 0 { 295 key := itr.Key() 296 _, height, ok := parseShKey(key) 297 if ok { 298 if err = b.Delete(s.shKey(height)); err != nil { 299 return err 300 } 301 if err = b.Delete(s.vsKey(height)); err != nil { 302 return err 303 } 304 } 305 itr.Next() 306 numToPrune-- 307 pruned++ 308 } 309 if err = itr.Error(); err != nil { 310 return err 311 } 312 313 err = b.WriteSync() 314 if err != nil { 315 return err 316 } 317 318 // 3) Update size. 319 s.mtx.Lock() 320 defer s.mtx.Unlock() 321 322 s.size -= uint16(pruned) 323 324 if wErr := s.db.SetSync(sizeKey, marshalSize(s.size)); wErr != nil { 325 return fmt.Errorf("failed to persist size: %w", wErr) 326 } 327 328 return nil 329 } 330 331 // Size returns the number of header & validator set pairs. 332 // 333 // Safe for concurrent use by multiple goroutines. 334 func (s *dbs) Size() uint16 { 335 s.mtx.RLock() 336 defer s.mtx.RUnlock() 337 return s.size 338 } 339 340 func (s *dbs) shKey(height int64) []byte { 341 return []byte(fmt.Sprintf("sh/%s/%020d", s.prefix, height)) 342 } 343 344 func (s *dbs) vsKey(height int64) []byte { 345 return []byte(fmt.Sprintf("vs/%s/%020d", s.prefix, height)) 346 } 347 348 var keyPattern = regexp.MustCompile(`^(sh|vs)/([^/]*)/([0-9]+)$`) 349 350 func parseKey(key []byte) (part string, prefix string, height int64, ok bool) { 351 submatch := keyPattern.FindSubmatch(key) 352 if submatch == nil { 353 return "", "", 0, false 354 } 355 part = string(submatch[1]) 356 prefix = string(submatch[2]) 357 height, err := strconv.ParseInt(string(submatch[3]), 10, 64) 358 if err != nil { 359 return "", "", 0, false 360 } 361 ok = true // good! 362 return 363 } 364 365 func parseShKey(key []byte) (prefix string, height int64, ok bool) { 366 var part string 367 part, prefix, height, ok = parseKey(key) 368 if part != "sh" { 369 return "", 0, false 370 } 371 return 372 } 373 374 func marshalSize(size uint16) []byte { 375 bs := make([]byte, 2) 376 binary.LittleEndian.PutUint16(bs, size) 377 return bs 378 } 379 380 func unmarshalSize(bz []byte) uint16 { 381 return binary.LittleEndian.Uint16(bz) 382 }