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