cosmos-sdk/store/v2/commitment/iavlv2/tree.go
2025-01-20 10:28:33 +00:00

204 lines
4.5 KiB
Go

package iavlv2
import (
"fmt"
"github.com/cosmos/iavl/v2"
ics23 "github.com/cosmos/ics23/go"
"cosmossdk.io/core/log"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/commitment"
)
var (
_ commitment.Tree = (*Tree)(nil)
_ commitment.Reader = (*Tree)(nil)
_ store.PausablePruner = (*Tree)(nil)
)
type Tree struct {
tree *iavl.Tree
log log.Logger
path string
}
func NewTree(
cfg Config,
dbOptions iavl.SqliteDbOptions,
log log.Logger,
) (*Tree, error) {
pool := iavl.NewNodePool()
sql, err := iavl.NewSqliteDb(pool, dbOptions)
if err != nil {
return nil, err
}
tree := iavl.NewTree(sql, pool, cfg.ToTreeOptions())
return &Tree{tree: tree, log: log, path: dbOptions.Path}, nil
}
func (t *Tree) Set(key, value []byte) error {
_, err := t.tree.Set(key, value)
return err
}
func (t *Tree) Remove(key []byte) error {
_, _, err := t.tree.Remove(key)
return err
}
func (t *Tree) GetLatestVersion() (uint64, error) {
return uint64(t.tree.Version()), nil
}
func (t *Tree) Hash() []byte {
return t.tree.Hash()
}
func (t *Tree) Version() uint64 {
return uint64(t.tree.Version())
}
func (t *Tree) LoadVersion(version uint64) error {
if err := isHighBitSet(version); err != nil {
return err
}
return t.tree.LoadVersion(int64(version))
}
func (t *Tree) LoadVersionForOverwriting(version uint64) error {
return t.LoadVersion(version)
}
func (t *Tree) Commit() ([]byte, uint64, error) {
h, v, err := t.tree.SaveVersion()
return h, uint64(v), err
}
func (t *Tree) SetInitialVersion(version uint64) error {
if err := isHighBitSet(version); err != nil {
return err
}
t.tree.SetShouldCheckpoint()
return t.tree.SetInitialVersion(int64(version))
}
func (t *Tree) GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error) {
if err := isHighBitSet(version); err != nil {
return nil, err
}
return t.tree.GetProof(int64(version), key)
}
func (t *Tree) Get(version uint64, key []byte) ([]byte, error) {
if err := isHighBitSet(version); err != nil {
return nil, err
}
v := int64(version)
h := t.tree.Version()
if v > h {
return nil, fmt.Errorf("get: cannot read future version %d; h: %d path=%s", v, h, t.path)
}
versionFound, val, err := t.tree.GetRecent(v, key)
if versionFound {
return val, err
}
cloned, err := t.tree.ReadonlyClone()
if err != nil {
return nil, err
}
if err = cloned.LoadVersion(int64(version)); err != nil {
return nil, err
}
return cloned.Get(key)
}
func (t *Tree) Has(version uint64, key []byte) (bool, error) {
res, err := t.Get(version, key)
return res != nil, err
}
func (t *Tree) Iterator(version uint64, start, end []byte, ascending bool) (corestore.Iterator, error) {
if err := isHighBitSet(version); err != nil {
return nil, err
}
h := t.tree.Version()
v := int64(version)
if v > h {
return nil, fmt.Errorf("iterator: cannot read future version %d; h: %d", v, h)
}
ok, itr := t.tree.IterateRecent(v, start, end, ascending)
if ok {
return itr, nil
}
cloned, err := t.tree.ReadonlyClone()
if err != nil {
return nil, err
}
if err = cloned.LoadVersion(int64(version)); err != nil {
return nil, err
}
if ascending {
// inclusive = false is IAVL v1's default behavior.
// the read expectations of certain modules (like x/staking) will cause a panic if this is changed.
return t.tree.Iterator(start, end, false)
} else {
return t.tree.ReverseIterator(start, end)
}
}
func (t *Tree) Export(version uint64) (commitment.Exporter, error) {
if err := isHighBitSet(version); err != nil {
return nil, err
}
e, err := t.tree.Export(int64(version), iavl.PostOrder)
if err != nil {
return nil, err
}
return &Exporter{e}, nil
}
func (t *Tree) Import(version uint64) (commitment.Importer, error) {
if err := isHighBitSet(version); err != nil {
return nil, err
}
importer, err := t.tree.Import(int64(version))
if err != nil {
return nil, err
}
return &Importer{importer}, nil
}
func (t *Tree) Close() error {
return t.tree.Close()
}
func (t *Tree) Prune(version uint64) error {
// do nothing, IAVL v2 has its own advanced pruning mechanism
return nil
}
// PausePruning is unnecessary in IAVL v2 due to the advanced pruning mechanism
func (t *Tree) PausePruning(bool) {}
func (t *Tree) IsConcurrentSafe() bool {
return true
}
func (t *Tree) WorkingHash() []byte {
return t.tree.Hash()
}
func isHighBitSet(version uint64) error {
if version&(1<<63) != 0 {
return fmt.Errorf("%d too large; uint64 with the highest bit set are not supported", version)
}
return nil
}
func SetGlobalPruneLimit(limit int) {
iavl.SetGlobalPruneLimit(limit)
}