github.com/Finschia/finschia-sdk@v0.48.1/store/iavl/store.go (about) 1 package iavl 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "time" 8 9 ics23 "github.com/confio/ics23/go" 10 "github.com/cosmos/iavl" 11 abci "github.com/tendermint/tendermint/abci/types" 12 tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" 13 dbm "github.com/tendermint/tm-db" 14 15 "github.com/Finschia/ostracon/libs/log" 16 17 "github.com/Finschia/finschia-sdk/store/cachekv" 18 "github.com/Finschia/finschia-sdk/store/listenkv" 19 "github.com/Finschia/finschia-sdk/store/tracekv" 20 "github.com/Finschia/finschia-sdk/store/types" 21 "github.com/Finschia/finschia-sdk/telemetry" 22 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 23 "github.com/Finschia/finschia-sdk/types/kv" 24 ) 25 26 const ( 27 // DefaultIAVLCacheSize is default Iavl cache units size. 1 unit is 128 byte 28 // default 64MB 29 DefaultIAVLCacheSize = 1024 * 512 30 ) 31 32 var ( 33 _ types.KVStore = (*Store)(nil) 34 _ types.CommitStore = (*Store)(nil) 35 _ types.CommitKVStore = (*Store)(nil) 36 _ types.Queryable = (*Store)(nil) 37 _ types.StoreWithInitialVersion = (*Store)(nil) 38 ) 39 40 // Store Implements types.KVStore and CommitKVStore. 41 type Store struct { 42 tree Tree 43 } 44 45 // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the 46 // store's version (id) from the provided DB. An error is returned if the version 47 // fails to load, or if called with a positive version on an empty tree. 48 func LoadStore(db dbm.DB, logger log.Logger, key types.StoreKey, id types.CommitID, lazyLoading bool, cacheSize int, disableFastNode bool) (types.CommitKVStore, error) { 49 return LoadStoreWithInitialVersion(db, logger, key, id, lazyLoading, 0, cacheSize, disableFastNode) 50 } 51 52 // LoadStoreWithInitialVersion returns an IAVL Store as a CommitKVStore setting its initialVersion 53 // to the one given. Internally, it will load the store's version (id) from the 54 // provided DB. An error is returned if the version fails to load, or if called with a positive 55 // version on an empty tree. 56 func LoadStoreWithInitialVersion(db dbm.DB, logger log.Logger, key types.StoreKey, id types.CommitID, lazyLoading bool, initialVersion uint64, cacheSize int, disableFastNode bool) (types.CommitKVStore, error) { 57 tree, err := iavl.NewMutableTreeWithOpts(db, cacheSize, &iavl.Options{InitialVersion: initialVersion}, disableFastNode) 58 if err != nil { 59 return nil, err 60 } 61 62 isUpgradeable, err := tree.IsUpgradeable() 63 if err != nil { 64 return nil, err 65 } 66 67 if isUpgradeable && logger != nil { 68 logger.Info( 69 "Upgrading IAVL storage for faster queries + execution on live state. This may take a while", 70 "store_key", key.String(), 71 "version", initialVersion, 72 "commit", fmt.Sprintf("%X", id), 73 "is_lazy", lazyLoading, 74 ) 75 } 76 77 if lazyLoading { 78 _, err = tree.LazyLoadVersion(id.Version) 79 } else { 80 _, err = tree.LoadVersion(id.Version) 81 } 82 83 if err != nil { 84 return nil, err 85 } 86 87 if logger != nil { 88 logger.Debug("Finished loading IAVL tree") 89 } 90 91 return &Store{ 92 tree: tree, 93 }, nil 94 } 95 96 // UnsafeNewStore returns a reference to a new IAVL Store with a given mutable 97 // IAVL tree reference. It should only be used for testing purposes. 98 // 99 // CONTRACT: The IAVL tree should be fully loaded. 100 // CONTRACT: PruningOptions passed in as argument must be the same as pruning options 101 // passed into iavl.MutableTree 102 func UnsafeNewStore(tree *iavl.MutableTree) *Store { 103 return &Store{ 104 tree: tree, 105 } 106 } 107 108 // GetImmutable returns a reference to a new store backed by an immutable IAVL 109 // tree at a specific version (height) without any pruning options. This should 110 // be used for querying and iteration only. If the version does not exist or has 111 // been pruned, an empty immutable IAVL tree will be used. 112 // Any mutable operations executed will result in a panic. 113 func (st *Store) GetImmutable(version int64) (*Store, error) { 114 if !st.VersionExists(version) { 115 return &Store{tree: &immutableTree{&iavl.ImmutableTree{}}}, nil 116 } 117 118 iTree, err := st.tree.GetImmutable(version) 119 if err != nil { 120 return nil, err 121 } 122 123 return &Store{ 124 tree: &immutableTree{iTree}, 125 }, nil 126 } 127 128 // Commit commits the current store state and returns a CommitID with the new 129 // version and hash. 130 func (st *Store) Commit() types.CommitID { 131 defer telemetry.MeasureSince(time.Now(), "store", "iavl", "commit") 132 133 hash, version, err := st.tree.SaveVersion() 134 if err != nil { 135 panic(err) 136 } 137 138 return types.CommitID{ 139 Version: version, 140 Hash: hash, 141 } 142 } 143 144 // LastCommitID implements Committer. 145 func (st *Store) LastCommitID() types.CommitID { 146 hash, err := st.tree.Hash() 147 if err != nil { 148 panic(err) 149 } 150 151 return types.CommitID{ 152 Version: st.tree.Version(), 153 Hash: hash, 154 } 155 } 156 157 // SetPruning panics as pruning options should be provided at initialization 158 // since IAVl accepts pruning options directly. 159 func (st *Store) SetPruning(_ types.PruningOptions) { 160 panic("cannot set pruning options on an initialized IAVL store") 161 } 162 163 // SetPruning panics as pruning options should be provided at initialization 164 // since IAVl accepts pruning options directly. 165 func (st *Store) GetPruning() types.PruningOptions { 166 panic("cannot get pruning options on an initialized IAVL store") 167 } 168 169 // VersionExists returns whether or not a given version is stored. 170 func (st *Store) VersionExists(version int64) bool { 171 return st.tree.VersionExists(version) 172 } 173 174 // GetAllVersions returns all versions in the iavl tree 175 func (st *Store) GetAllVersions() []int { 176 return st.tree.(*iavl.MutableTree).AvailableVersions() 177 } 178 179 // Implements Store. 180 func (st *Store) GetStoreType() types.StoreType { 181 return types.StoreTypeIAVL 182 } 183 184 // Implements Store. 185 func (st *Store) CacheWrap() types.CacheWrap { 186 return cachekv.NewStore(st) 187 } 188 189 // CacheWrapWithTrace implements the Store interface. 190 func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { 191 return cachekv.NewStore(tracekv.NewStore(st, w, tc)) 192 } 193 194 // CacheWrapWithListeners implements the CacheWrapper interface. 195 func (st *Store) CacheWrapWithListeners(storeKey types.StoreKey, listeners []types.WriteListener) types.CacheWrap { 196 return cachekv.NewStore(listenkv.NewStore(st, storeKey, listeners)) 197 } 198 199 // Implements types.KVStore. 200 func (st *Store) Set(key, value []byte) { 201 types.AssertValidKey(key) 202 types.AssertValidValue(value) 203 st.tree.Set(key, value) 204 } 205 206 // Implements types.KVStore. 207 func (st *Store) Get(key []byte) []byte { 208 defer telemetry.MeasureSince(time.Now(), "store", "iavl", "get") 209 value, err := st.tree.Get(key) 210 if err != nil { 211 panic(err) 212 } 213 return value 214 } 215 216 // Implements types.KVStore. 217 func (st *Store) Has(key []byte) (exists bool) { 218 defer telemetry.MeasureSince(time.Now(), "store", "iavl", "has") 219 has, err := st.tree.Has(key) 220 if err != nil { 221 panic(err) 222 } 223 return has 224 } 225 226 // Implements types.KVStore. 227 func (st *Store) Delete(key []byte) { 228 defer telemetry.MeasureSince(time.Now(), "store", "iavl", "delete") 229 st.tree.Remove(key) 230 } 231 232 // DeleteVersions deletes a series of versions from the MutableTree. An error 233 // is returned if any single version is invalid or the delete fails. All writes 234 // happen in a single batch with a single commit. 235 func (st *Store) DeleteVersions(versions ...int64) error { 236 return st.tree.DeleteVersions(versions...) 237 } 238 239 // LoadVersionForOverwriting attempts to load a tree at a previously committed 240 // version, or the latest version below it. Any versions greater than targetVersion will be deleted. 241 func (st *Store) LoadVersionForOverwriting(targetVersion int64) (int64, error) { 242 return st.tree.LoadVersionForOverwriting(targetVersion) 243 } 244 245 // Implements types.KVStore. 246 func (st *Store) Iterator(start, end []byte) types.Iterator { 247 iterator, err := st.tree.Iterator(start, end, true) 248 if err != nil { 249 panic(err) 250 } 251 return iterator 252 } 253 254 // Implements types.KVStore. 255 func (st *Store) ReverseIterator(start, end []byte) types.Iterator { 256 iterator, err := st.tree.Iterator(start, end, false) 257 if err != nil { 258 panic(err) 259 } 260 return iterator 261 } 262 263 // SetInitialVersion sets the initial version of the IAVL tree. It is used when 264 // starting a new chain at an arbitrary height. 265 func (st *Store) SetInitialVersion(version int64) { 266 st.tree.SetInitialVersion(uint64(version)) 267 } 268 269 // Exports the IAVL store at the given version, returning an iavl.Exporter for the tree. 270 func (st *Store) Export(version int64) (*iavl.Exporter, error) { 271 istore, err := st.GetImmutable(version) 272 if err != nil { 273 return nil, fmt.Errorf("iavl export failed for version %v: %w", version, err) 274 } 275 tree, ok := istore.tree.(*immutableTree) 276 if !ok || tree == nil { 277 return nil, fmt.Errorf("iavl export failed: unable to fetch tree for version %v", version) 278 } 279 return tree.Export(), nil 280 } 281 282 // Import imports an IAVL tree at the given version, returning an iavl.Importer for importing. 283 func (st *Store) Import(version int64) (*iavl.Importer, error) { 284 tree, ok := st.tree.(*iavl.MutableTree) 285 if !ok { 286 return nil, errors.New("iavl import failed: unable to find mutable tree") 287 } 288 return tree.Import(version) 289 } 290 291 // Handle gatest the latest height, if height is 0 292 func getHeight(tree Tree, req abci.RequestQuery) int64 { 293 height := req.Height 294 if height == 0 { 295 latest := tree.Version() 296 if tree.VersionExists(latest - 1) { 297 height = latest - 1 298 } else { 299 height = latest 300 } 301 } 302 return height 303 } 304 305 // Query implements ABCI interface, allows queries 306 // 307 // by default we will return from (latest height -1), 308 // as we will have merkle proofs immediately (header height = data height + 1) 309 // If latest-1 is not present, use latest (which must be present) 310 // if you care to have the latest data to see a tx results, you must 311 // explicitly set the height you want to see 312 func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { 313 defer telemetry.MeasureSince(time.Now(), "store", "iavl", "query") 314 315 if len(req.Data) == 0 { 316 return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrTxDecode, "query cannot be zero length")) 317 } 318 319 tree := st.tree 320 321 // store the height we chose in the response, with 0 being changed to the 322 // latest height 323 res.Height = getHeight(tree, req) 324 325 switch req.Path { 326 case "/key": // get by key 327 key := req.Data // data holds the key bytes 328 329 res.Key = key 330 if !st.VersionExists(res.Height) { 331 res.Log = iavl.ErrVersionDoesNotExist.Error() 332 break 333 } 334 335 value, err := tree.GetVersioned(key, res.Height) 336 if err != nil { 337 panic(err) 338 } 339 res.Value = value 340 341 if !req.Prove { 342 break 343 } 344 345 // Continue to prove existence/absence of value 346 // Must convert store.Tree to iavl.MutableTree with given version to use in CreateProof 347 iTree, err := tree.GetImmutable(res.Height) 348 if err != nil { 349 // sanity check: If value for given version was retrieved, immutable tree must also be retrievable 350 panic(fmt.Sprintf("version exists in store but could not retrieve corresponding versioned tree in store, %s", err.Error())) 351 } 352 mtree := &iavl.MutableTree{ 353 ImmutableTree: iTree, 354 } 355 356 // get proof from tree and convert to merkle.Proof before adding to result 357 res.ProofOps = getProofFromTree(mtree, req.Data, res.Value != nil) 358 359 case "/subspace": 360 pairs := kv.Pairs{ 361 Pairs: make([]kv.Pair, 0), 362 } 363 364 subspace := req.Data 365 res.Key = subspace 366 367 iterator := types.KVStorePrefixIterator(st, subspace) 368 for ; iterator.Valid(); iterator.Next() { 369 pairs.Pairs = append(pairs.Pairs, kv.Pair{Key: iterator.Key(), Value: iterator.Value()}) 370 } 371 iterator.Close() 372 373 bz, err := pairs.Marshal() 374 if err != nil { 375 panic(fmt.Errorf("failed to marshal KV pairs: %w", err)) 376 } 377 378 res.Value = bz 379 380 default: 381 return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unexpected query path: %v", req.Path)) 382 } 383 384 return res 385 } 386 387 // Takes a MutableTree, a key, and a flag for creating existence or absence proof and returns the 388 // appropriate merkle.Proof. Since this must be called after querying for the value, this function should never error 389 // Thus, it will panic on error rather than returning it 390 func getProofFromTree(tree *iavl.MutableTree, key []byte, exists bool) *tmcrypto.ProofOps { 391 var ( 392 commitmentProof *ics23.CommitmentProof 393 err error 394 ) 395 396 if exists { 397 // value was found 398 commitmentProof, err = tree.GetMembershipProof(key) 399 if err != nil { 400 // sanity check: If value was found, membership proof must be creatable 401 panic(fmt.Sprintf("unexpected value for empty proof: %s", err.Error())) 402 } 403 } else { 404 // value wasn't found 405 commitmentProof, err = tree.GetNonMembershipProof(key) 406 if err != nil { 407 // sanity check: If value wasn't found, nonmembership proof must be creatable 408 panic(fmt.Sprintf("unexpected error for nonexistence proof: %s", err.Error())) 409 } 410 } 411 412 op := types.NewIavlCommitmentOp(key, commitmentProof) 413 return &tmcrypto.ProofOps{Ops: []tmcrypto.ProofOp{op.ProofOp()}} 414 } 415 416 //---------------------------------------- 417 418 // Implements types.Iterator. 419 type iavlIterator struct { 420 dbm.Iterator 421 } 422 423 var _ types.Iterator = (*iavlIterator)(nil)