From a86c2a9980ffc4fed1f8c423889e0628193ffaab Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 19 Mar 2024 10:34:14 -0400 Subject: [PATCH] feat(store/v2): support rocksdb and pebbledb as RawDB (#19607) Co-authored-by: cool-developer <51834436+cool-develope@users.noreply.github.com> Co-authored-by: Marko Co-authored-by: sontrinh16 --- store/db/db.go | 33 ++++ store/db/db_test.go | 22 +++ store/db/goleveldb.go | 10 +- store/db/pebbledb.go | 285 ++++++++++++++++++++++++++++++++ store/db/rocksdb.go | 329 +++++++++++++++++++++++++++++++++++++ store/db/rocksdb_noflag.go | 135 +++++++++++++++ store/go.mod | 2 +- store/go.sum | 2 + 8 files changed, 813 insertions(+), 5 deletions(-) create mode 100644 store/db/db.go create mode 100644 store/db/pebbledb.go create mode 100644 store/db/rocksdb.go create mode 100644 store/db/rocksdb_noflag.go diff --git a/store/db/db.go b/store/db/db.go new file mode 100644 index 0000000000..dbbe25068f --- /dev/null +++ b/store/db/db.go @@ -0,0 +1,33 @@ +package db + +import ( + "fmt" + + "cosmossdk.io/store/v2" +) + +type RawDBType string + +const ( + DBTypeGoLevelDB RawDBType = "goleveldb" + DBTypeRocksDB = "rocksdb" + DBTypePebbleDB = "pebbledb" + DBTypePrefixDB = "prefixdb" + + DBFileSuffix string = ".db" +) + +func NewRawDB(dbType RawDBType, name, dataDir string, opts store.DBOptions) (store.RawDB, error) { + switch dbType { + case DBTypeGoLevelDB: + return NewGoLevelDB(name, dataDir, opts) + + case DBTypeRocksDB: + return NewRocksDB(name, dataDir) + + case DBTypePebbleDB: + return NewPebbleDB(name, dataDir) + } + + return nil, fmt.Errorf("unsupported db type: %s", dbType) +} diff --git a/store/db/db_test.go b/store/db/db_test.go index 3c31a3fd41..39ef0403d7 100644 --- a/store/db/db_test.go +++ b/store/db/db_test.go @@ -16,6 +16,10 @@ type DBTestSuite struct { db store.RawDB } +func (s *DBTestSuite) TearDownSuite() { + s.Require().NoError(s.db.Close()) +} + func (s *DBTestSuite) TestDBOperations() { // Set b := s.db.NewBatch() @@ -93,6 +97,24 @@ func TestMemDBSuite(t *testing.T) { }) } +func TestPebbleDBSuite(t *testing.T) { + db, err := NewPebbleDB("test", t.TempDir()) + require.NoError(t, err) + + suite.Run(t, &DBTestSuite{ + db: db, + }) +} + +func TestRocksDBSuite(t *testing.T) { + db, err := NewRocksDB("test", t.TempDir()) + require.NoError(t, err) + + suite.Run(t, &DBTestSuite{ + db: db, + }) +} + func TestGoLevelDBSuite(t *testing.T) { db, err := NewGoLevelDB("test", t.TempDir(), nil) require.NoError(t, err) diff --git a/store/db/goleveldb.go b/store/db/goleveldb.go index 64f1a10384..bfdcd218f8 100644 --- a/store/db/goleveldb.go +++ b/store/db/goleveldb.go @@ -19,18 +19,20 @@ import ( storeerrors "cosmossdk.io/store/v2/errors" ) +var _ store.RawDB = (*GoLevelDB)(nil) + // GoLevelDB implements RawDB using github.com/syndtr/goleveldb/leveldb. -// It is used for only store v2 migration, since some clients use goleveldb as the iavl v0/v1 backend. +// It is used for only store v2 migration, since some clients use goleveldb as +// the IAVL v0/v1 backend. type GoLevelDB struct { db *leveldb.DB } -var _ store.RawDB = (*GoLevelDB)(nil) - func NewGoLevelDB(name, dir string, opts store.DBOptions) (*GoLevelDB, error) { defaultOpts := &opt.Options{ Filter: filter.NewBloomFilter(10), // by default, goleveldb doesn't use a bloom filter. } + if opts != nil { files := cast.ToInt(opts.Get("maxopenfiles")) if files > 0 { @@ -42,7 +44,7 @@ func NewGoLevelDB(name, dir string, opts store.DBOptions) (*GoLevelDB, error) { } func NewGoLevelDBWithOpts(name, dir string, o *opt.Options) (*GoLevelDB, error) { - dbPath := filepath.Join(dir, name+".db") + dbPath := filepath.Join(dir, name+DBFileSuffix) db, err := leveldb.OpenFile(dbPath, o) if err != nil { return nil, err diff --git a/store/db/pebbledb.go b/store/db/pebbledb.go new file mode 100644 index 0000000000..bc7d77c996 --- /dev/null +++ b/store/db/pebbledb.go @@ -0,0 +1,285 @@ +package db + +import ( + "bytes" + "errors" + "fmt" + "path/filepath" + "slices" + + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" + storeerrors "cosmossdk.io/store/v2/errors" + "github.com/cockroachdb/pebble" + "github.com/spf13/cast" +) + +var _ store.RawDB = (*PebbleDB)(nil) + +// PebbleDB implements RawDB using PebbleDB as the underlying storage engine. +// It is used for only store v2 migration, since some clients use PebbleDB as +// the IAVL v0/v1 backend. +type PebbleDB struct { + storage *pebble.DB +} + +func NewPebbleDB(name, dataDir string) (*PebbleDB, error) { + + return NewPebbleDBWithOpts(name, dataDir, nil) + +} + +func NewPebbleDBWithOpts(name, dataDir string, opts store.DBOptions) (*PebbleDB, error) { + do := &pebble.Options{ + MaxConcurrentCompactions: func() int { return 3 }, // default 1 + } + + do.EnsureDefaults() + + if opts != nil { + files := cast.ToInt(opts.Get("maxopenfiles")) + if files > 0 { + do.MaxOpenFiles = files + } + } + dbPath := filepath.Join(dataDir, name+DBFileSuffix) + db, err := pebble.Open(dbPath, do) + if err != nil { + return nil, fmt.Errorf("failed to open PebbleDB: %w", err) + } + + return &PebbleDB{storage: db}, nil +} + +func (db *PebbleDB) Close() error { + err := db.storage.Close() + db.storage = nil + return err +} + +func (db *PebbleDB) Get(key []byte) ([]byte, error) { + if len(key) == 0 { + return nil, storeerrors.ErrKeyEmpty + } + + bz, closer, err := db.storage.Get(key) + if err != nil { + if errors.Is(err, pebble.ErrNotFound) { + // in case of a fresh database + return nil, nil + } + + return nil, fmt.Errorf("failed to perform PebbleDB read: %w", err) + } + + if len(bz) == 0 { + return nil, closer.Close() + } + + return bz, closer.Close() +} + +func (db *PebbleDB) Has(key []byte) (bool, error) { + bz, err := db.Get(key) + if err != nil { + return false, err + } + + return bz != nil, nil +} + +func (db *PebbleDB) Iterator(start, end []byte) (corestore.Iterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, storeerrors.ErrKeyEmpty + } + + itr, err := db.storage.NewIter(&pebble.IterOptions{LowerBound: start, UpperBound: end}) + if err != nil { + return nil, fmt.Errorf("failed to create PebbleDB iterator: %w", err) + } + + return newPebbleDBIterator(itr, start, end, false), nil +} + +func (db *PebbleDB) ReverseIterator(start, end []byte) (corestore.Iterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, storeerrors.ErrKeyEmpty + } + + itr, err := db.storage.NewIter(&pebble.IterOptions{LowerBound: start, UpperBound: end}) + if err != nil { + return nil, fmt.Errorf("failed to create PebbleDB iterator: %w", err) + } + + return newPebbleDBIterator(itr, start, end, true), nil +} + +func (db *PebbleDB) NewBatch() store.RawBatch { + return &pebbleDBBatch{ + db: db, + batch: db.storage.NewBatch(), + } +} + +func (db *PebbleDB) NewBatchWithSize(size int) store.RawBatch { + return &pebbleDBBatch{ + db: db, + batch: db.storage.NewBatchWithSize(size), + } +} + +var _ corestore.Iterator = (*pebbleDBIterator)(nil) + +type pebbleDBIterator struct { + source *pebble.Iterator + start []byte + end []byte + valid bool + reverse bool +} + +func newPebbleDBIterator(src *pebble.Iterator, start, end []byte, reverse bool) *pebbleDBIterator { + // move the underlying PebbleDB cursor to the first key + var valid bool + if reverse { + if end == nil { + valid = src.Last() + } else { + valid = src.SeekLT(end) + } + } else { + valid = src.First() + } + + return &pebbleDBIterator{ + source: src, + start: start, + end: end, + valid: valid, + reverse: reverse, + } +} + +func (itr *pebbleDBIterator) Domain() (start, end []byte) { + return itr.start, itr.end +} + +func (itr *pebbleDBIterator) Valid() bool { + // once invalid, forever invalid + if !itr.valid || !itr.source.Valid() { + itr.valid = false + return itr.valid + } + + // if source has error, consider it invalid + if err := itr.source.Error(); err != nil { + itr.valid = false + return itr.valid + } + + // if key is at the end or past it, consider it invalid + if end := itr.end; end != nil { + if bytes.Compare(end, itr.Key()) <= 0 { + itr.valid = false + return itr.valid + } + } + + return true +} + +func (itr *pebbleDBIterator) Key() []byte { + itr.assertIsValid() + return slices.Clone(itr.source.Key()) +} + +func (itr *pebbleDBIterator) Value() []byte { + itr.assertIsValid() + return slices.Clone(itr.source.Value()) +} + +func (itr *pebbleDBIterator) Next() { + itr.assertIsValid() + + if itr.reverse { + itr.valid = itr.source.Prev() + } else { + itr.valid = itr.source.Next() + } +} + +func (itr *pebbleDBIterator) Error() error { + return itr.source.Error() +} + +func (itr *pebbleDBIterator) Close() error { + err := itr.source.Close() + itr.source = nil + itr.valid = false + + return err +} + +func (itr *pebbleDBIterator) assertIsValid() { + if !itr.valid { + panic("pebbleDB iterator is invalid") + } +} + +var _ store.RawBatch = (*pebbleDBBatch)(nil) + +type pebbleDBBatch struct { + db *PebbleDB + batch *pebble.Batch +} + +func (b *pebbleDBBatch) Set(key, value []byte) error { + if len(key) == 0 { + return storeerrors.ErrKeyEmpty + } + if value == nil { + return storeerrors.ErrValueNil + } + if b.batch == nil { + return storeerrors.ErrBatchClosed + } + + return b.batch.Set(key, value, nil) +} + +func (b *pebbleDBBatch) Delete(key []byte) error { + if len(key) == 0 { + return storeerrors.ErrKeyEmpty + } + if b.batch == nil { + return storeerrors.ErrBatchClosed + } + + return b.batch.Delete(key, nil) +} + +func (b *pebbleDBBatch) Write() error { + err := b.batch.Commit(&pebble.WriteOptions{Sync: false}) + if err != nil { + return fmt.Errorf("failed to write PebbleDB batch: %w", err) + } + + return nil +} + +func (b *pebbleDBBatch) WriteSync() error { + err := b.batch.Commit(&pebble.WriteOptions{Sync: true}) + if err != nil { + return fmt.Errorf("failed to write PebbleDB batch: %w", err) + } + + return nil +} + +func (b *pebbleDBBatch) Close() error { + return b.batch.Close() +} + +func (b *pebbleDBBatch) GetByteSize() (int, error) { + return b.batch.Len(), nil +} diff --git a/store/db/rocksdb.go b/store/db/rocksdb.go new file mode 100644 index 0000000000..c95f5a13de --- /dev/null +++ b/store/db/rocksdb.go @@ -0,0 +1,329 @@ +//go:build rocksdb +// +build rocksdb + +package db + +import ( + "bytes" + "fmt" + "path/filepath" + "runtime" + "slices" + + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" + storeerrors "cosmossdk.io/store/v2/errors" + "github.com/linxGnu/grocksdb" +) + +var ( + _ store.RawDB = (*RocksDB)(nil) + + defaultReadOpts = grocksdb.NewDefaultReadOptions() +) + +// RocksDB implements RawDB using RocksDB as the underlying storage engine. +// It is used for only store v2 migration, since some clients use RocksDB as +// the IAVL v0/v1 backend. +type RocksDB struct { + storage *grocksdb.DB +} + +// defaultRocksdbOptions, good enough for most cases, including heavy workloads. +// 1GB table cache, 512MB write buffer(may use 50% more on heavy workloads). +// compression: snappy as default, need to -lsnappy to enable. +func defaultRocksdbOptions() *grocksdb.Options { + bbto := grocksdb.NewDefaultBlockBasedTableOptions() + bbto.SetBlockCache(grocksdb.NewLRUCache(1 << 30)) + bbto.SetFilterPolicy(grocksdb.NewBloomFilter(10)) + + rocksdbOpts := grocksdb.NewDefaultOptions() + rocksdbOpts.SetBlockBasedTableFactory(bbto) + // SetMaxOpenFiles to 4096 seems to provide a reliable performance boost + rocksdbOpts.SetMaxOpenFiles(4096) + rocksdbOpts.SetCreateIfMissing(true) + rocksdbOpts.IncreaseParallelism(runtime.NumCPU()) + // 1.5GB maximum memory use for writebuffer. + rocksdbOpts.OptimizeLevelStyleCompaction(512 * 1024 * 1024) + return rocksdbOpts +} + +func NewRocksDB(name, dataDir string) (*RocksDB, error) { + opts := defaultRocksdbOptions() + opts.SetCreateIfMissing(true) + + return NewRocksDBWithOpts(name, dataDir, opts) +} + +func NewRocksDBWithOpts(name, dataDir string, opts *grocksdb.Options) (*RocksDB, error) { + dbPath := filepath.Join(dataDir, name+DBFileSuffix) + storage, err := grocksdb.OpenDb(opts, dbPath) + if err != nil { + return nil, fmt.Errorf("failed to open RocksDB: %w", err) + } + + return &RocksDB{ + storage: storage, + }, nil +} + +func (db *RocksDB) Close() error { + db.storage.Close() + db.storage = nil + return nil +} + +func (db *RocksDB) Get(key []byte) ([]byte, error) { + bz, err := db.storage.GetBytes(defaultReadOpts, key) + if err != nil { + return nil, err + } + + return bz, nil +} + +func (db *RocksDB) Has(key []byte) (bool, error) { + bz, err := db.Get(key) + if err != nil { + return false, err + } + + return bz != nil, nil +} + +func (db *RocksDB) Iterator(start, end []byte) (corestore.Iterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, storeerrors.ErrKeyEmpty + } + + itr := db.storage.NewIterator(defaultReadOpts) + return newRocksDBIterator(itr, start, end, false), nil +} + +func (db *RocksDB) ReverseIterator(start, end []byte) (corestore.Iterator, error) { + if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { + return nil, storeerrors.ErrKeyEmpty + } + + itr := db.storage.NewIterator(defaultReadOpts) + return newRocksDBIterator(itr, start, end, true), nil +} + +func (db *RocksDB) NewBatch() store.RawBatch { + return &rocksDBBatch{ + db: db, + batch: grocksdb.NewWriteBatch(), + } +} + +func (db *RocksDB) NewBatchWithSize(_ int) store.RawBatch { + return db.NewBatch() +} + +var _ corestore.Iterator = (*rocksDBIterator)(nil) + +type rocksDBIterator struct { + source *grocksdb.Iterator + start []byte + end []byte + valid bool + reverse bool +} + +func newRocksDBIterator(src *grocksdb.Iterator, start, end []byte, reverse bool) *rocksDBIterator { + if reverse { + if end == nil { + src.SeekToLast() + } else { + src.Seek(end) + + if src.Valid() { + eoaKey := readOnlySlice(src.Key()) // end or after key + if bytes.Compare(end, eoaKey) <= 0 { + src.Prev() + } + } else { + src.SeekToLast() + } + } + } else { + if start == nil { + src.SeekToFirst() + } else { + src.Seek(start) + } + } + + return &rocksDBIterator{ + source: src, + start: start, + end: end, + reverse: reverse, + valid: src.Valid(), + } +} + +func (itr *rocksDBIterator) Domain() (start, end []byte) { + return itr.start, itr.end +} + +func (itr *rocksDBIterator) Valid() bool { + // once invalid, forever invalid + if !itr.valid { + return false + } + + // if source has error, consider it invalid + if err := itr.source.Err(); err != nil { + itr.valid = false + return false + } + + // if source is invalid, consider it invalid + if !itr.source.Valid() { + itr.valid = false + return false + } + + // if key is at the end or past it, consider it invalid + start := itr.start + end := itr.end + key := readOnlySlice(itr.source.Key()) + + if itr.reverse { + if start != nil && bytes.Compare(key, start) < 0 { + itr.valid = false + return false + } + } else { + if end != nil && bytes.Compare(end, key) <= 0 { + itr.valid = false + return false + } + } + + return true +} + +func (itr *rocksDBIterator) Key() []byte { + itr.assertIsValid() + return copyAndFreeSlice(itr.source.Key()) +} + +func (itr *rocksDBIterator) Value() []byte { + itr.assertIsValid() + return copyAndFreeSlice(itr.source.Value()) +} + +func (itr *rocksDBIterator) Next() { + if !itr.valid { + return + } + + if itr.reverse { + itr.source.Prev() + } else { + itr.source.Next() + } +} + +func (itr *rocksDBIterator) Error() error { + return itr.source.Err() +} + +func (itr *rocksDBIterator) Close() error { + itr.source.Close() + itr.source = nil + itr.valid = false + + return nil +} + +func (itr *rocksDBIterator) assertIsValid() { + if !itr.valid { + panic("rocksDB iterator is invalid") + } +} + +type rocksDBBatch struct { + db *RocksDB + batch *grocksdb.WriteBatch +} + +func (b *rocksDBBatch) Set(key, value []byte) error { + if len(key) == 0 { + return storeerrors.ErrKeyEmpty + } + if value == nil { + return storeerrors.ErrValueNil + } + if b.batch == nil { + return storeerrors.ErrBatchClosed + } + + b.batch.Put(key, value) + return nil +} + +func (b *rocksDBBatch) Delete(key []byte) error { + if len(key) == 0 { + return storeerrors.ErrKeyEmpty + } + if b.batch == nil { + return storeerrors.ErrBatchClosed + } + + b.batch.Delete(key) + return nil +} + +func (b *rocksDBBatch) Write() error { + writeOpts := grocksdb.NewDefaultWriteOptions() + writeOpts.SetSync(false) + + if err := b.db.storage.Write(writeOpts, b.batch); err != nil { + return fmt.Errorf("failed to write RocksDB batch: %w", err) + } + + return nil +} + +func (b *rocksDBBatch) WriteSync() error { + writeOpts := grocksdb.NewDefaultWriteOptions() + writeOpts.SetSync(true) + + if err := b.db.storage.Write(writeOpts, b.batch); err != nil { + return fmt.Errorf("failed to write RocksDB batch: %w", err) + } + + return nil +} + +func (b *rocksDBBatch) Close() error { + b.batch.Destroy() + return nil +} + +func (b *rocksDBBatch) GetByteSize() (int, error) { + return len(b.batch.Data()), nil +} + +func readOnlySlice(s *grocksdb.Slice) []byte { + if !s.Exists() { + return nil + } + + return s.Data() +} + +// copyAndFreeSlice will copy a given RocksDB slice and free it. If the slice +// does not exist, will be returned. +func copyAndFreeSlice(s *grocksdb.Slice) []byte { + defer s.Free() + + if !s.Exists() { + return nil + } + + return slices.Clone(s.Data()) +} diff --git a/store/db/rocksdb_noflag.go b/store/db/rocksdb_noflag.go new file mode 100644 index 0000000000..f5b8f98df5 --- /dev/null +++ b/store/db/rocksdb_noflag.go @@ -0,0 +1,135 @@ +//go:build !rocksdb +// +build !rocksdb + +package db + +import ( + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2" + "github.com/linxGnu/grocksdb" +) + +var ( + _ store.RawDB = (*RocksDB)(nil) +) + +// RocksDB implements RawDB using RocksDB as the underlying storage engine. +// It is used for only store v2 migration, since some clients use RocksDB as +// the IAVL v0/v1 backend. +type RocksDB struct { +} + +func NewRocksDB(dataDir string) (*RocksDB, error) { + panic("rocksdb must be built with -tags rocksdb") + +} + +func NewRocksDBWithOpts(dataDir string, opts store.DBOptions) (*RocksDB, error) { + panic("rocksdb must be built with -tags rocksdb") +} + +func (db *RocksDB) Close() error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (db *RocksDB) Get(key []byte) ([]byte, error) { + panic("rocksdb must be built with -tags rocksdb") +} + +func (db *RocksDB) Has(key []byte) (bool, error) { + panic("rocksdb must be built with -tags rocksdb") +} + +func (db *RocksDB) Iterator(start, end []byte) (corestore.Iterator, error) { + panic("rocksdb must be built with -tags rocksdb") +} + +func (db *RocksDB) ReverseIterator(start, end []byte) (corestore.Iterator, error) { + panic("rocksdb must be built with -tags rocksdb") +} + +func (db *RocksDB) NewBatch() store.RawBatch { + panic("rocksdb must be built with -tags rocksdb") +} + +func (db *RocksDB) NewBatchWithSize(_ int) store.RawBatch { + return db.NewBatch() +} + +var _ corestore.Iterator = (*rocksDBIterator)(nil) + +type rocksDBIterator struct { +} + +func newRocksDBIterator(src *grocksdb.Iterator, start, end []byte, reverse bool) *rocksDBIterator { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) Domain() (start, end []byte) { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) Valid() bool { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) Key() []byte { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) Value() []byte { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) Next() { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) Error() error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) Close() error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (itr *rocksDBIterator) assertIsValid() { + panic("rocksdb must be built with -tags rocksdb") +} + +type rocksDBBatch struct { +} + +func (b *rocksDBBatch) Set(key, value []byte) error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (b *rocksDBBatch) Delete(key []byte) error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (b *rocksDBBatch) Write() error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (b *rocksDBBatch) WriteSync() error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (b *rocksDBBatch) Close() error { + panic("rocksdb must be built with -tags rocksdb") +} + +func (b *rocksDBBatch) GetByteSize() (int, error) { + panic("rocksdb must be built with -tags rocksdb") +} + +func readOnlySlice(s *grocksdb.Slice) []byte { + panic("rocksdb must be built with -tags rocksdb") +} + +// copyAndFreeSlice will copy a given RocksDB slice and free it. If the slice +// does not exist, will be returned. +func copyAndFreeSlice(s *grocksdb.Slice) []byte { + panic("rocksdb must be built with -tags rocksdb") +} diff --git a/store/go.mod b/store/go.mod index fbc8236e91..2a29bf6bd8 100644 --- a/store/go.mod +++ b/store/go.mod @@ -10,7 +10,7 @@ require ( github.com/cockroachdb/pebble v1.1.0 github.com/cometbft/cometbft v0.38.5 github.com/cosmos/gogoproto v1.4.11 - github.com/cosmos/iavl v1.1.0 + github.com/cosmos/iavl v1.1.1 github.com/cosmos/ics23/go v0.10.0 github.com/google/btree v1.1.2 github.com/hashicorp/go-metrics v0.5.3 diff --git a/store/go.sum b/store/go.sum index 4e25aa335a..ab1fec1cae 100644 --- a/store/go.sum +++ b/store/go.sum @@ -48,6 +48,8 @@ github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= github.com/cosmos/iavl v1.1.0 h1:YRdPLKCPoJQDXoZu0BToQ6PADswLkhNzyt7DZLgK0xY= github.com/cosmos/iavl v1.1.0/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= +github.com/cosmos/iavl v1.1.1 h1:64nTi8s3gEoGqhA8TyAWFWfz7/pg0anKzHNSc1ETc7Q= +github.com/cosmos/iavl v1.1.1/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=