204 lines
4.5 KiB
Go
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)
|
|
}
|