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