From 24d456db40a84621ee60febacdbe8a04fe793a56 Mon Sep 17 00:00:00 2001 From: Matt Kocubinski Date: Fri, 29 Nov 2024 13:02:47 -0600 Subject: [PATCH 1/4] feat(store/v2): add support for iavl/v2 (#22424) --- runtime/v2/go.mod | 3 +- runtime/v2/go.sum | 6 +- server/v2/cometbft/abci_test.go | 22 +- server/v2/cometbft/go.mod | 1 - server/v2/cometbft/go.sum | 2 - .../v2/cometbft/internal/mock/mock_store.go | 4 +- .../cometbft/oe/optimistic_execution_test.go | 5 +- server/v2/cometbft/server.go | 2 +- server/v2/go.mod | 3 +- server/v2/go.sum | 6 +- server/v2/testdata/app.toml | 4 +- simapp/v2/go.mod | 1 - simapp/v2/go.sum | 2 - store/v2/commitment/iavl/tree_test.go | 7 +- store/v2/commitment/iavlv2/tree.go | 147 +++++++ store/v2/commitment/iavlv2/tree_test.go | 46 +++ store/v2/commitment/store_test_suite.go | 51 ++- store/v2/go.mod | 10 +- store/v2/go.sum | 26 +- store/v2/pruning/manager_test.go | 6 +- store/v2/root/factory.go | 12 +- store/v2/root/migrate_test.go | 6 +- store/v2/root/store_test.go | 22 +- store/v2/root/upgrade_test.go | 6 +- store/v2/storage/pebbledb/batch.go | 1 + store/v2/storage/sqlite/batch.go | 104 ----- store/v2/storage/sqlite/db.go | 360 ------------------ store/v2/storage/sqlite/db_test.go | 200 ---------- store/v2/storage/sqlite/iterator.go | 183 --------- store/v2/storage/storage_bench_test.go | 5 - store/v2/storage/store.go | 3 +- tests/go.mod | 1 - tests/go.sum | 2 - tools/confix/data/v2-app.toml | 4 +- 34 files changed, 310 insertions(+), 953 deletions(-) create mode 100644 store/v2/commitment/iavlv2/tree.go create mode 100644 store/v2/commitment/iavlv2/tree_test.go delete mode 100644 store/v2/storage/sqlite/batch.go delete mode 100644 store/v2/storage/sqlite/db.go delete mode 100644 store/v2/storage/sqlite/db_test.go delete mode 100644 store/v2/storage/sqlite/iterator.go diff --git a/runtime/v2/go.mod b/runtime/v2/go.mod index e9653bd73ece..c02c29466ff9 100644 --- a/runtime/v2/go.mod +++ b/runtime/v2/go.mod @@ -53,7 +53,7 @@ require ( github.com/getsentry/sentry-go v0.27.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.3 // indirect @@ -65,7 +65,6 @@ require ( github.com/linxGnu/grocksdb v1.9.3 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/gomega v1.28.1 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/runtime/v2/go.sum b/runtime/v2/go.sum index ffa2e8b402ff..723b257f47e0 100644 --- a/runtime/v2/go.sum +++ b/runtime/v2/go.sum @@ -107,8 +107,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -164,8 +164,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/server/v2/cometbft/abci_test.go b/server/v2/cometbft/abci_test.go index 7b20c354d786..ab1fdc722879 100644 --- a/server/v2/cometbft/abci_test.go +++ b/server/v2/cometbft/abci_test.go @@ -2,18 +2,16 @@ package cometbft import ( "context" - "cosmossdk.io/core/server" "crypto/sha256" "encoding/json" "errors" - abci "github.com/cometbft/cometbft/abci/types" "io" "strings" "sync" "testing" "time" - "cosmossdk.io/server/v2/cometbft/oe" + abci "github.com/cometbft/cometbft/abci/types" abciproto "github.com/cometbft/cometbft/api/cometbft/abci/v1" v1 "github.com/cometbft/cometbft/api/cometbft/types/v1" "github.com/cosmos/gogoproto/proto" @@ -21,6 +19,7 @@ import ( "github.com/stretchr/testify/require" appmodulev2 "cosmossdk.io/core/appmodule/v2" + "cosmossdk.io/core/server" "cosmossdk.io/core/store" "cosmossdk.io/core/transaction" "cosmossdk.io/log" @@ -28,6 +27,7 @@ import ( "cosmossdk.io/server/v2/cometbft/handlers" cometmock "cosmossdk.io/server/v2/cometbft/internal/mock" "cosmossdk.io/server/v2/cometbft/mempool" + "cosmossdk.io/server/v2/cometbft/oe" "cosmossdk.io/server/v2/cometbft/types" "cosmossdk.io/server/v2/stf" "cosmossdk.io/server/v2/stf/branch" @@ -60,10 +60,10 @@ func getQueryRouterBuilder[T any, PT interface { *T proto.Message }, -U any, UT interface { - *U - proto.Message -}]( + U any, UT interface { + *U + proto.Message + }]( t *testing.T, handler func(ctx context.Context, msg PT) (UT, error), ) *stf.MsgRouterBuilder { @@ -90,10 +90,10 @@ func getMsgRouterBuilder[T any, PT interface { *T transaction.Msg }, -U any, UT interface { - *U - transaction.Msg -}]( + U any, UT interface { + *U + transaction.Msg + }]( t *testing.T, handler func(ctx context.Context, msg PT) (UT, error), ) *stf.MsgRouterBuilder { diff --git a/server/v2/cometbft/go.mod b/server/v2/cometbft/go.mod index b90fc385e7d1..22a91f18d97f 100644 --- a/server/v2/cometbft/go.mod +++ b/server/v2/cometbft/go.mod @@ -132,7 +132,6 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect diff --git a/server/v2/cometbft/go.sum b/server/v2/cometbft/go.sum index 68cadeb0d9ea..1999a87be3ed 100644 --- a/server/v2/cometbft/go.sum +++ b/server/v2/cometbft/go.sum @@ -345,8 +345,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= diff --git a/server/v2/cometbft/internal/mock/mock_store.go b/server/v2/cometbft/internal/mock/mock_store.go index e9f7be3edcb3..b485a75d876b 100644 --- a/server/v2/cometbft/internal/mock/mock_store.go +++ b/server/v2/cometbft/internal/mock/mock_store.go @@ -12,7 +12,7 @@ import ( dbm "cosmossdk.io/store/v2/db" "cosmossdk.io/store/v2/proof" "cosmossdk.io/store/v2/storage" - "cosmossdk.io/store/v2/storage/sqlite" + "cosmossdk.io/store/v2/storage/pebbledb" ) type MockStore struct { @@ -21,7 +21,7 @@ type MockStore struct { } func NewMockStorage(logger log.Logger, dir string) storev2.VersionedWriter { - storageDB, _ := sqlite.New(dir) + storageDB, _ := pebbledb.New(dir) ss := storage.NewStorageStore(storageDB, logger) return ss } diff --git a/server/v2/cometbft/oe/optimistic_execution_test.go b/server/v2/cometbft/oe/optimistic_execution_test.go index c0eb28c2a5e9..cd3c9263c784 100644 --- a/server/v2/cometbft/oe/optimistic_execution_test.go +++ b/server/v2/cometbft/oe/optimistic_execution_test.go @@ -5,12 +5,13 @@ import ( "errors" "testing" + abci "github.com/cometbft/cometbft/api/cometbft/abci/v1" + "github.com/stretchr/testify/assert" + "cosmossdk.io/core/server" "cosmossdk.io/core/store" "cosmossdk.io/core/transaction" "cosmossdk.io/log" - abci "github.com/cometbft/cometbft/api/cometbft/abci/v1" - "github.com/stretchr/testify/assert" ) func testFinalizeBlock[T transaction.Tx](context.Context, *abci.FinalizeBlockRequest) (*server.BlockResponse, store.WriterMap, []T, error) { diff --git a/server/v2/cometbft/server.go b/server/v2/cometbft/server.go index 6567e01caef0..55a38b5e9646 100644 --- a/server/v2/cometbft/server.go +++ b/server/v2/cometbft/server.go @@ -2,7 +2,6 @@ package cometbft import ( "context" - "cosmossdk.io/server/v2/cometbft/oe" "crypto/sha256" "encoding/json" "fmt" @@ -35,6 +34,7 @@ import ( "cosmossdk.io/server/v2/appmanager" cometlog "cosmossdk.io/server/v2/cometbft/log" "cosmossdk.io/server/v2/cometbft/mempool" + "cosmossdk.io/server/v2/cometbft/oe" "cosmossdk.io/server/v2/cometbft/types" "cosmossdk.io/store/v2/snapshots" diff --git a/server/v2/go.mod b/server/v2/go.mod index 2905d7e23c1c..0f84cf2ef70e 100644 --- a/server/v2/go.mod +++ b/server/v2/go.mod @@ -67,7 +67,7 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect @@ -83,7 +83,6 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.1.0 // indirect github.com/onsi/gomega v1.28.1 // indirect diff --git a/server/v2/go.sum b/server/v2/go.sum index e494b8b56be5..dcd56eab1e9b 100644 --- a/server/v2/go.sum +++ b/server/v2/go.sum @@ -146,8 +146,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -227,8 +227,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= diff --git a/server/v2/testdata/app.toml b/server/v2/testdata/app.toml index 3d32be7e57ef..3f0e640a8c3e 100644 --- a/server/v2/testdata/app.toml +++ b/server/v2/testdata/app.toml @@ -25,8 +25,8 @@ minimum-gas-prices = '0stake' app-db-backend = 'goleveldb' [store.options] -# State storage database type. Currently we support: "sqlite", "pebble" and "rocksdb" -ss-type = 'sqlite' +# State storage database type. Currently we support: "pebble" and "rocksdb" +ss-type = 'pebble' # State commitment database type. Currently we support: "iavl" and "iavl-v2" sc-type = 'iavl' diff --git a/simapp/v2/go.mod b/simapp/v2/go.mod index 8fdb40c14bf9..a42a48e4410f 100644 --- a/simapp/v2/go.mod +++ b/simapp/v2/go.mod @@ -170,7 +170,6 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mdp/qrterminal/v3 v3.2.0 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect diff --git a/simapp/v2/go.sum b/simapp/v2/go.sum index 49ea9257ac44..f202fc16b0ed 100644 --- a/simapp/v2/go.sum +++ b/simapp/v2/go.sum @@ -662,8 +662,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdp/qrterminal/v3 v3.2.0 h1:qteQMXO3oyTK4IHwj2mWsKYYRBOp1Pj2WRYFYYNTCdk= github.com/mdp/qrterminal/v3 v3.2.0/go.mod h1:XGGuua4Lefrl7TLEsSONiD+UEjQXJZ4mPzF+gWYIJkk= diff --git a/store/v2/commitment/iavl/tree_test.go b/store/v2/commitment/iavl/tree_test.go index 65503190b943..728347999a31 100644 --- a/store/v2/commitment/iavl/tree_test.go +++ b/store/v2/commitment/iavl/tree_test.go @@ -16,7 +16,12 @@ import ( func TestCommitterSuite(t *testing.T) { s := &commitment.CommitStoreTestSuite{ - NewStore: func(db corestore.KVStoreWithBatch, storeKeys, oldStoreKeys []string, logger corelog.Logger) (*commitment.CommitStore, error) { + NewStore: func( + db corestore.KVStoreWithBatch, + _ string, + storeKeys, oldStoreKeys []string, + logger corelog.Logger, + ) (*commitment.CommitStore, error) { multiTrees := make(map[string]commitment.Tree) cfg := DefaultConfig() mountTreeFn := func(storeKey string) (commitment.Tree, error) { diff --git a/store/v2/commitment/iavlv2/tree.go b/store/v2/commitment/iavlv2/tree.go new file mode 100644 index 000000000000..997a0a60cc1a --- /dev/null +++ b/store/v2/commitment/iavlv2/tree.go @@ -0,0 +1,147 @@ +package iavlv2 + +import ( + "errors" + "fmt" + + "github.com/cosmos/iavl/v2" + ics23 "github.com/cosmos/ics23/go" + + 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 +} + +func NewTree(treeOptions iavl.TreeOptions, dbOptions iavl.SqliteDbOptions, pool *iavl.NodePool) (*Tree, error) { + sql, err := iavl.NewSqliteDb(pool, dbOptions) + if err != nil { + return nil, err + } + tree := iavl.NewTree(sql, pool, treeOptions) + return &Tree{tree: tree}, 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 + } + + if version == 0 { + return nil + } + return t.tree.LoadVersion(int64(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 + } + if int64(version) != t.tree.Version() { + 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) + } else { + return t.tree.Get(key) + } +} + +func (t *Tree) Iterator(version uint64, start, end []byte, ascending bool) (corestore.Iterator, error) { + if err := isHighBitSet(version); err != nil { + return nil, err + } + if int64(version) != t.tree.Version() { + return nil, fmt.Errorf("loading past version not yet supported") + } + return t.tree.Iterator(start, end, ascending) +} + +func (t *Tree) Export(version uint64) (commitment.Exporter, error) { + return nil, errors.New("snapshot import/export not yet supported") +} + +func (t *Tree) Import(version uint64) (commitment.Importer, error) { + return nil, errors.New("snapshot import/export not yet supported") +} + +func (t *Tree) Close() error { + return t.tree.Close() +} + +func (t *Tree) Prune(version uint64) error { + if err := isHighBitSet(version); err != nil { + return err + } + + return t.tree.DeleteVersionsTo(int64(version)) +} + +// PausePruning is unnecessary in IAVL v2 due to the advanced pruning mechanism +func (t *Tree) PausePruning(bool) {} + +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 +} diff --git a/store/v2/commitment/iavlv2/tree_test.go b/store/v2/commitment/iavlv2/tree_test.go new file mode 100644 index 000000000000..a173c453c976 --- /dev/null +++ b/store/v2/commitment/iavlv2/tree_test.go @@ -0,0 +1,46 @@ +package iavlv2 + +import ( + "fmt" + "testing" + + "github.com/cosmos/iavl/v2" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + corelog "cosmossdk.io/core/log" + corestore "cosmossdk.io/core/store" + "cosmossdk.io/store/v2/commitment" +) + +func TestCommitterSuite(t *testing.T) { + nodePool := iavl.NewNodePool() + s := &commitment.CommitStoreTestSuite{ + TreeType: "iavlv2", + NewStore: func( + db corestore.KVStoreWithBatch, + dbDir string, + storeKeys, oldStoreKeys []string, + logger corelog.Logger, + ) (*commitment.CommitStore, error) { + multiTrees := make(map[string]commitment.Tree) + mountTreeFn := func(storeKey string) (commitment.Tree, error) { + path := fmt.Sprintf("%s/%s", dbDir, storeKey) + tree, err := NewTree(iavl.DefaultTreeOptions(), iavl.SqliteDbOptions{Path: path}, nodePool) + require.NoError(t, err) + return tree, nil + } + for _, storeKey := range storeKeys { + multiTrees[storeKey], _ = mountTreeFn(storeKey) + } + oldTrees := make(map[string]commitment.Tree) + for _, storeKey := range oldStoreKeys { + oldTrees[storeKey], _ = mountTreeFn(storeKey) + } + + return commitment.NewCommitStore(multiTrees, oldTrees, db, logger) + }, + } + + suite.Run(t, s) +} diff --git a/store/v2/commitment/store_test_suite.go b/store/v2/commitment/store_test_suite.go index afb468de8595..c41a2540a070 100644 --- a/store/v2/commitment/store_test_suite.go +++ b/store/v2/commitment/store_test_suite.go @@ -28,12 +28,16 @@ const ( type CommitStoreTestSuite struct { suite.Suite - NewStore func(db corestore.KVStoreWithBatch, storeKeys, oldStoreKeys []string, logger corelog.Logger) (*CommitStore, error) + NewStore func(db corestore.KVStoreWithBatch, dbDir string, storeKeys, oldStoreKeys []string, logger corelog.Logger) (*CommitStore, error) + TreeType string } func (s *CommitStoreTestSuite) TestStore_Snapshotter() { + if s.TreeType == "iavlv2" { + s.T().Skip("FIXME: iavlv2 does not yet support snapshots") + } storeKeys := []string{storeKey1, storeKey2} - commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + commitStore, err := s.NewStore(dbm.NewMemDB(), s.T().TempDir(), storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) latestVersion := uint64(10) @@ -67,7 +71,7 @@ func (s *CommitStoreTestSuite) TestStore_Snapshotter() { }, } - targetStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + targetStore, err := s.NewStore(dbm.NewMemDB(), s.T().TempDir(), storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) chunks := make(chan io.ReadCloser, kvCount*int(latestVersion)) @@ -130,7 +134,8 @@ func (s *CommitStoreTestSuite) TestStore_Snapshotter() { func (s *CommitStoreTestSuite) TestStore_LoadVersion() { storeKeys := []string{storeKey1, storeKey2} mdb := dbm.NewMemDB() - commitStore, err := s.NewStore(mdb, storeKeys, nil, coretesting.NewNopLogger()) + dbDir := s.T().TempDir() + commitStore, err := s.NewStore(mdb, dbDir, storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) latestVersion := uint64(10) @@ -151,7 +156,7 @@ func (s *CommitStoreTestSuite) TestStore_LoadVersion() { } // load the store with the latest version - targetStore, err := s.NewStore(mdb, storeKeys, nil, coretesting.NewNopLogger()) + targetStore, err := s.NewStore(mdb, dbDir, storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) err = targetStore.LoadVersion(latestVersion) s.Require().NoError(err) @@ -164,7 +169,7 @@ func (s *CommitStoreTestSuite) TestStore_LoadVersion() { // rollback to a previous version rollbackVersion := uint64(5) - rollbackStore, err := s.NewStore(mdb, storeKeys, nil, coretesting.NewNopLogger()) + rollbackStore, err := s.NewStore(mdb, dbDir, storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) err = rollbackStore.LoadVersion(rollbackVersion) s.Require().NoError(err) @@ -185,7 +190,7 @@ func (s *CommitStoreTestSuite) TestStore_LoadVersion() { func (s *CommitStoreTestSuite) TestStore_Pruning() { storeKeys := []string{storeKey1, storeKey2} pruneOpts := store.NewPruningOptionWithCustom(10, 5) - commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + commitStore, err := s.NewStore(dbm.NewMemDB(), s.T().TempDir(), storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) latestVersion := uint64(100) @@ -225,7 +230,7 @@ func (s *CommitStoreTestSuite) TestStore_Pruning() { func (s *CommitStoreTestSuite) TestStore_GetProof() { storeKeys := []string{storeKey1, storeKey2} - commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + commitStore, err := s.NewStore(dbm.NewMemDB(), s.T().TempDir(), storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) toVersion := uint64(10) @@ -268,7 +273,7 @@ func (s *CommitStoreTestSuite) TestStore_GetProof() { func (s *CommitStoreTestSuite) TestStore_Get() { storeKeys := []string{storeKey1, storeKey2} - commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger()) + commitStore, err := s.NewStore(dbm.NewMemDB(), s.T().TempDir(), storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) toVersion := uint64(10) @@ -303,7 +308,8 @@ func (s *CommitStoreTestSuite) TestStore_Get() { func (s *CommitStoreTestSuite) TestStore_Upgrades() { storeKeys := []string{storeKey1, storeKey2, storeKey3} commitDB := dbm.NewMemDB() - commitStore, err := s.NewStore(commitDB, storeKeys, nil, coretesting.NewNopLogger()) + commitDir := s.T().TempDir() + commitStore, err := s.NewStore(commitDB, commitDir, storeKeys, nil, coretesting.NewNopLogger()) s.Require().NoError(err) latestVersion := uint64(10) @@ -330,14 +336,14 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() { } newStoreKeys := []string{storeKey1, storeKey2, storeKey3, "newStore1", "newStore2"} realStoreKeys := []string{storeKey1, storeKey2, "newStore1", "newStore2"} - oldStoreKeys := []string{storeKey1, storeKey3} - commitStore, err = s.NewStore(commitDB, newStoreKeys, oldStoreKeys, coretesting.NewNopLogger()) + oldStoreKeys := []string{storeKey3} + commitStore, err = s.NewStore(commitDB, commitDir, newStoreKeys, oldStoreKeys, coretesting.NewNopLogger()) s.Require().NoError(err) err = commitStore.LoadVersionAndUpgrade(latestVersion, upgrades) s.Require().NoError(err) // GetProof should work for the old stores - for _, storeKey := range []string{storeKey1, storeKey3} { + for _, storeKey := range []string{storeKey3} { for i := uint64(1); i <= latestVersion; i++ { for j := 0; j < kvCount; j++ { proof, err := commitStore.GetProof([]byte(storeKey), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) @@ -391,20 +397,20 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() { // verify existing store for i := uint64(1); i < latestVersion*2; i++ { for j := 0; j < kvCount; j++ { - proof, err := commitStore.GetProof([]byte(storeKey2), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + prf, err := commitStore.GetProof([]byte(storeKey2), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) s.Require().NoError(err) - s.Require().NotNil(proof) + s.Require().NotNil(prf) } } // create a new commitment store with one more upgrades upgrades = &corestore.StoreUpgrades{ - Added: []string{storeKey3}, Deleted: []string{storeKey2}, + Added: []string{"newStore3"}, } - newRealStoreKeys := []string{storeKey1, storeKey3, "newStore1", "newStore2"} + newRealStoreKeys := []string{storeKey1, "newStore1", "newStore2", "newStore3"} oldStoreKeys = []string{storeKey2, storeKey3} - commitStore, err = s.NewStore(commitDB, newStoreKeys, oldStoreKeys, coretesting.NewNopLogger()) + commitStore, err = s.NewStore(commitDB, commitDir, newRealStoreKeys, oldStoreKeys, coretesting.NewNopLogger()) s.Require().NoError(err) err = commitStore.LoadVersionAndUpgrade(2*latestVersion-1, upgrades) s.Require().NoError(err) @@ -420,7 +426,8 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() { kvPairs[storeKey] = append(kvPairs[storeKey], corestore.KVPair{Key: key, Value: value}) } } - s.Require().NoError(commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs))) + err = commitStore.WriteChangeset(corestore.NewChangesetWithPairs(i, kvPairs)) + s.Require().NoError(err) commitInfo, err := commitStore.Commit(i) s.Require().NoError(err) s.Require().NotNil(commitInfo) @@ -432,6 +439,7 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() { // prune the old stores s.Require().NoError(commitStore.Prune(latestVersion)) + s.T().Logf("prune to version %d", latestVersion) // GetProof should fail for the old stores for _, storeKey := range []string{storeKey1, storeKey3} { for i := uint64(1); i <= latestVersion; i++ { @@ -441,6 +449,7 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() { } } } + s.T().Log("GetProof should work for the new stores") // GetProof should not fail for the newly removed store for i := latestVersion + 1; i < latestVersion*2; i++ { for j := 0; j < kvCount; j++ { @@ -450,6 +459,7 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() { } } + s.T().Logf("Prune to version %d", latestVersion*2) s.Require().NoError(commitStore.Prune(latestVersion * 2)) // GetProof should fail for the newly deleted stores for i := uint64(1); i < latestVersion*2; i++ { @@ -458,10 +468,11 @@ func (s *CommitStoreTestSuite) TestStore_Upgrades() { s.Require().Error(err) } } + s.T().Log("GetProof should work for the new added store") // GetProof should work for the new added store for i := latestVersion*2 + 1; i < latestVersion*3; i++ { for j := 0; j < kvCount; j++ { - proof, err := commitStore.GetProof([]byte(storeKey3), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) + proof, err := commitStore.GetProof([]byte("newStore3"), i, []byte(fmt.Sprintf("key-%d-%d", i, j))) s.Require().NoError(err) s.Require().NotNil(proof) } diff --git a/store/v2/go.mod b/store/v2/go.mod index 8c32d86dd0d7..ac07130d2e10 100644 --- a/store/v2/go.mod +++ b/store/v2/go.mod @@ -11,11 +11,11 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/iavl v1.3.1 + github.com/cosmos/iavl/v2 v2.0.0-20241128205019-1b18c0edbbd9 github.com/cosmos/ics23/go v0.11.0 - github.com/google/btree v1.1.2 + github.com/google/btree v1.1.3 github.com/hashicorp/go-metrics v0.5.3 github.com/linxGnu/grocksdb v1.9.3 - github.com/mattn/go-sqlite3 v1.14.22 github.com/spf13/cast v1.7.0 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d @@ -26,7 +26,9 @@ require ( require ( cosmossdk.io/schema v0.3.0 // indirect github.com/DataDog/zstd v1.5.5 // indirect + github.com/aybabtme/uniplot v0.0.0-20151203143629-039c559e5e7e // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bvinc/go-sqlite-lite v0.6.1 // indirect github.com/bytedance/sonic v1.12.4 // indirect github.com/bytedance/sonic/loader v0.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -37,6 +39,7 @@ require ( github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/dot v1.6.2 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -48,6 +51,7 @@ require ( github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/kocubinski/costor-api v1.1.1 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -71,3 +75,5 @@ require ( google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace cosmossdk.io/core => ../../core diff --git a/store/v2/go.sum b/store/v2/go.sum index 4a42918d7265..fa8b03c077ac 100644 --- a/store/v2/go.sum +++ b/store/v2/go.sum @@ -1,5 +1,3 @@ -cosmossdk.io/core v1.0.0-alpha.6 h1:5ukC4JcQKmemLQXcAgu/QoOvJI50hpBkIIg4ZT2EN8E= -cosmossdk.io/core v1.0.0-alpha.6/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY= cosmossdk.io/core/testing v0.0.0-20241108153815-606544c7be7e h1:F+ScucYxwrrDJU8guJXQXpGhdpziYSbxW6HMP2wCNxs= cosmossdk.io/core/testing v0.0.0-20241108153815-606544c7be7e/go.mod h1:3YvVv9aJayjPhdX0DY1IMrGse4sR63hNBWx2VtDWjGQ= cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA= @@ -15,10 +13,14 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aybabtme/uniplot v0.0.0-20151203143629-039c559e5e7e h1:dSeuFcs4WAJJnswS8vXy7YY1+fdlbVPuEVmDAfqvFOQ= +github.com/aybabtme/uniplot v0.0.0-20151203143629-039c559e5e7e/go.mod h1:uh71c5Vc3VNIplXOFXsnDy21T1BepgT32c5X/YPrOyc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bvinc/go-sqlite-lite v0.6.1 h1:JU8Rz5YAOZQiU3WEulKF084wfXpytRiqD2IaW2QjPz4= +github.com/bvinc/go-sqlite-lite v0.6.1/go.mod h1:2GiE60NUdb0aNhDdY+LXgrqAVDpi2Ijc6dB6ZMp9x6s= github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k= github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= @@ -55,6 +57,10 @@ github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fr github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= github.com/cosmos/iavl v1.3.1 h1:+W1G2uSUtJMqMGpwz/fKiwZxY2DDT/9/0hyNLm6Geu0= github.com/cosmos/iavl v1.3.1/go.mod h1:T6SfBcyhulVIY2G/ZtAtQm/QiJvsuhIos52V4dWYk88= +github.com/cosmos/iavl-bench/bench v0.0.4 h1:J6zQPiBqF4CXMM3QBsLqZgQEBGY0taX85vLIZMhmAfQ= +github.com/cosmos/iavl-bench/bench v0.0.4/go.mod h1:j2rLae77EffacWcp7mmj3Uaa4AOAmZA7ymvhsuBQKKI= +github.com/cosmos/iavl/v2 v2.0.0-20241128205019-1b18c0edbbd9 h1:H+ttW6HTzezz2l3Fp/hFNNHWA+a+7qZgNDE5OFySTiY= +github.com/cosmos/iavl/v2 v2.0.0-20241128205019-1b18c0edbbd9/go.mod h1:7RSm0aeApe3S1x4TrLffvUL6pjOtMYV4glYnpAhr2lw= github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -62,6 +68,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -102,8 +110,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -127,6 +135,8 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -138,6 +148,8 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kocubinski/costor-api v1.1.1 h1:sgfJA7T/8IfZ59zxiMrED0xdjerAFuPNBTqyO90GiEE= +github.com/kocubinski/costor-api v1.1.1/go.mod h1:ESMBMDkKfN+9vvvhhNVdKLhbOmzI3O/i16iXvRM9Tuc= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -155,8 +167,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -221,6 +231,10 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= diff --git a/store/v2/pruning/manager_test.go b/store/v2/pruning/manager_test.go index 0a0333451ae5..3dd7ec5fd336 100644 --- a/store/v2/pruning/manager_test.go +++ b/store/v2/pruning/manager_test.go @@ -15,7 +15,7 @@ import ( "cosmossdk.io/store/v2/commitment/iavl" dbm "cosmossdk.io/store/v2/db" "cosmossdk.io/store/v2/storage" - "cosmossdk.io/store/v2/storage/sqlite" + "cosmossdk.io/store/v2/storage/pebbledb" ) var storeKeys = []string{"store1", "store2", "store3"} @@ -45,9 +45,9 @@ func (s *PruningManagerTestSuite) SetupTest() { s.sc, err = commitment.NewCommitStore(multiTrees, nil, mdb, nopLog) s.Require().NoError(err) - sqliteDB, err := sqlite.New(s.T().TempDir()) + pebbleDB, err := pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - s.ss = storage.NewStorageStore(sqliteDB, nopLog) + s.ss = storage.NewStorageStore(pebbleDB, nopLog) scPruningOption := store.NewPruningOptionWithCustom(0, 1) // prune all ssPruningOption := store.NewPruningOptionWithCustom(5, 10) // prune some s.manager = NewManager(s.sc, s.ss, scPruningOption, ssPruningOption) diff --git a/store/v2/root/factory.go b/store/v2/root/factory.go index 2511a53b434e..2bd710182f26 100644 --- a/store/v2/root/factory.go +++ b/store/v2/root/factory.go @@ -17,7 +17,6 @@ import ( "cosmossdk.io/store/v2/storage" "cosmossdk.io/store/v2/storage/pebbledb" "cosmossdk.io/store/v2/storage/rocksdb" - "cosmossdk.io/store/v2/storage/sqlite" ) type ( @@ -26,7 +25,6 @@ type ( ) const ( - SSTypeSQLite SSType = "sqlite" SSTypePebble SSType = "pebble" SSTypeRocks SSType = "rocksdb" SCTypeIavl SCType = "iavl" @@ -35,7 +33,7 @@ const ( // Options are the options for creating a root store. type Options struct { - SSType SSType `mapstructure:"ss-type" toml:"ss-type" comment:"State storage database type. Currently we support: \"sqlite\", \"pebble\" and \"rocksdb\""` + SSType SSType `mapstructure:"ss-type" toml:"ss-type" comment:"State storage database type. Currently we support: \"pebble\" and \"rocksdb\""` SCType SCType `mapstructure:"sc-type" toml:"sc-type" comment:"State commitment database type. Currently we support: \"iavl\" and \"iavl-v2\""` SSPruningOption *store.PruningOption `mapstructure:"ss-pruning-option" toml:"ss-pruning-option" comment:"Pruning options for state storage"` SCPruningOption *store.PruningOption `mapstructure:"sc-pruning-option" toml:"sc-pruning-option" comment:"Pruning options for state commitment"` @@ -54,7 +52,7 @@ type FactoryOptions struct { // DefaultStoreOptions returns the default options for creating a root store. func DefaultStoreOptions() Options { return Options{ - SSType: SSTypeSQLite, + SSType: SSTypePebble, SCType: SCTypeIavl, SCPruningOption: &store.PruningOption{ KeepRecent: 2, @@ -91,12 +89,6 @@ func CreateRootStore(opts *FactoryOptions) (store.RootStore, error) { storeOpts := opts.Options switch storeOpts.SSType { - case SSTypeSQLite: - dir := fmt.Sprintf("%s/data/ss/sqlite", opts.RootDir) - if err = ensureDir(dir); err != nil { - return nil, err - } - ssDb, err = sqlite.New(dir) case SSTypePebble: dir := fmt.Sprintf("%s/data/ss/pebble", opts.RootDir) if err = ensureDir(dir); err != nil { diff --git a/store/v2/root/migrate_test.go b/store/v2/root/migrate_test.go index 2d83038b812e..82a53919d93f 100644 --- a/store/v2/root/migrate_test.go +++ b/store/v2/root/migrate_test.go @@ -18,7 +18,7 @@ import ( "cosmossdk.io/store/v2/pruning" "cosmossdk.io/store/v2/snapshots" "cosmossdk.io/store/v2/storage" - "cosmossdk.io/store/v2/storage/sqlite" + "cosmossdk.io/store/v2/storage/pebbledb" ) var storeKeys = []string{"store1", "store2", "store3"} @@ -62,9 +62,9 @@ func (s *MigrateStoreTestSuite) SetupTest() { } // create a new storage and commitment stores - sqliteDB, err := sqlite.New(s.T().TempDir()) + pebbleDB, err := pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - ss := storage.NewStorageStore(sqliteDB, testLog) + ss := storage.NewStorageStore(pebbleDB, testLog) multiTrees1 := make(map[string]commitment.Tree) for _, storeKey := range storeKeys { diff --git a/store/v2/root/store_test.go b/store/v2/root/store_test.go index 10a3a049d3c0..59a490b11b00 100644 --- a/store/v2/root/store_test.go +++ b/store/v2/root/store_test.go @@ -17,7 +17,7 @@ import ( "cosmossdk.io/store/v2/proof" "cosmossdk.io/store/v2/pruning" "cosmossdk.io/store/v2/storage" - "cosmossdk.io/store/v2/storage/sqlite" + "cosmossdk.io/store/v2/storage/pebbledb" ) const ( @@ -47,9 +47,9 @@ func TestStorageTestSuite(t *testing.T) { func (s *RootStoreTestSuite) SetupTest() { noopLog := coretesting.NewNopLogger() - sqliteDB, err := sqlite.New(s.T().TempDir()) + pebbleDB, err := pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - ss := storage.NewStorageStore(sqliteDB, noopLog) + ss := storage.NewStorageStore(pebbleDB, noopLog) tree := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig()) tree2 := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig()) @@ -67,9 +67,9 @@ func (s *RootStoreTestSuite) SetupTest() { func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruningOption) { noopLog := coretesting.NewNopLogger() - sqliteDB, err := sqlite.New(s.T().TempDir()) + pebbleDB, err := pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - ss := storage.NewStorageStore(sqliteDB, noopLog) + ss := storage.NewStorageStore(pebbleDB, noopLog) mdb := dbm.NewMemDB() multiTrees := make(map[string]commitment.Tree) @@ -535,9 +535,9 @@ func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() { mdb1 := dbm.NewMemDB() mdb2 := dbm.NewMemDB() - sqliteDB, err := sqlite.New(s.T().TempDir()) + pebbleDB, err := pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - ss := storage.NewStorageStore(sqliteDB, noopLog) + ss := storage.NewStorageStore(pebbleDB, noopLog) tree := iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig()) sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, nil, mdb2, noopLog) @@ -566,9 +566,9 @@ func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() { s.Require().Equal(uint64(0), actualHeightToPrune) // "restart" - sqliteDB, err = sqlite.New(s.T().TempDir()) + pebbleDB, err = pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - ss = storage.NewStorageStore(sqliteDB, noopLog) + ss = storage.NewStorageStore(pebbleDB, noopLog) tree = iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig()) sc, err = commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, nil, mdb2, noopLog) @@ -616,10 +616,10 @@ func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() { func (s *RootStoreTestSuite) TestMultiStoreRestart() { noopLog := coretesting.NewNopLogger() - sqliteDB, err := sqlite.New(s.T().TempDir()) + pebbleDB, err := pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - ss := storage.NewStorageStore(sqliteDB, noopLog) + ss := storage.NewStorageStore(pebbleDB, noopLog) mdb1 := dbm.NewMemDB() mdb2 := dbm.NewMemDB() diff --git a/store/v2/root/upgrade_test.go b/store/v2/root/upgrade_test.go index 400ddb2c4d65..0e9da266a355 100644 --- a/store/v2/root/upgrade_test.go +++ b/store/v2/root/upgrade_test.go @@ -15,7 +15,7 @@ import ( dbm "cosmossdk.io/store/v2/db" "cosmossdk.io/store/v2/pruning" "cosmossdk.io/store/v2/storage" - "cosmossdk.io/store/v2/storage/sqlite" + "cosmossdk.io/store/v2/storage/pebbledb" ) type UpgradeStoreTestSuite struct { @@ -44,9 +44,9 @@ func (s *UpgradeStoreTestSuite) SetupTest() { } // create storage and commitment stores - sqliteDB, err := sqlite.New(s.T().TempDir()) + pebbleDB, err := pebbledb.New(s.T().TempDir()) s.Require().NoError(err) - ss := storage.NewStorageStore(sqliteDB, testLog) + ss := storage.NewStorageStore(pebbleDB, testLog) sc, err := commitment.NewCommitStore(multiTrees, nil, s.commitDB, testLog) s.Require().NoError(err) pm := pruning.NewManager(sc, ss, nil, nil) diff --git a/store/v2/storage/pebbledb/batch.go b/store/v2/storage/pebbledb/batch.go index fdd58f447435..7e9f7a347f98 100644 --- a/store/v2/storage/pebbledb/batch.go +++ b/store/v2/storage/pebbledb/batch.go @@ -88,6 +88,7 @@ func (b *Batch) Delete(storeKey, key []byte) error { return b.set(storeKey, b.version, key, []byte(tombstoneVal)) } +// Write flushes any accumulated data to disk and closes the batch. func (b *Batch) Write() (err error) { defer func() { err = errors.Join(err, b.batch.Close()) diff --git a/store/v2/storage/sqlite/batch.go b/store/v2/storage/sqlite/batch.go deleted file mode 100644 index 783b597e04af..000000000000 --- a/store/v2/storage/sqlite/batch.go +++ /dev/null @@ -1,104 +0,0 @@ -package sqlite - -import ( - "database/sql" - "fmt" - - "cosmossdk.io/store/v2" -) - -var _ store.Batch = (*Batch)(nil) - -type batchAction int - -const ( - batchActionSet batchAction = 0 - batchActionDel batchAction = 1 -) - -type batchOp struct { - action batchAction - storeKey []byte - key, value []byte -} - -type Batch struct { - db *sql.DB - tx *sql.Tx - ops []batchOp - size int - version uint64 -} - -func NewBatch(db *sql.DB, version uint64) (*Batch, error) { - tx, err := db.Begin() - if err != nil { - return nil, fmt.Errorf("failed to create SQL transaction: %w", err) - } - - return &Batch{ - db: db, - tx: tx, - ops: make([]batchOp, 0), - version: version, - }, nil -} - -func (b *Batch) Size() int { - return b.size -} - -func (b *Batch) Reset() error { - b.ops = nil - b.ops = make([]batchOp, 0) - b.size = 0 - - tx, err := b.db.Begin() - if err != nil { - return err - } - - b.tx = tx - return nil -} - -func (b *Batch) Set(storeKey, key, value []byte) error { - b.size += len(key) + len(value) - b.ops = append(b.ops, batchOp{action: batchActionSet, storeKey: storeKey, key: key, value: value}) - return nil -} - -func (b *Batch) Delete(storeKey, key []byte) error { - b.size += len(key) - b.ops = append(b.ops, batchOp{action: batchActionDel, storeKey: storeKey, key: key}) - return nil -} - -func (b *Batch) Write() error { - _, err := b.tx.Exec(reservedUpsertStmt, reservedStoreKey, keyLatestHeight, b.version, 0, b.version) - if err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - - for _, op := range b.ops { - switch op.action { - case batchActionSet: - _, err := b.tx.Exec(upsertStmt, op.storeKey, op.key, op.value, b.version, op.value) - if err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - - case batchActionDel: - _, err := b.tx.Exec(delStmt, b.version, op.storeKey, op.key, b.version) - if err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - } - } - - if err := b.tx.Commit(); err != nil { - return fmt.Errorf("failed to write SQL transaction: %w", err) - } - - return nil -} diff --git a/store/v2/storage/sqlite/db.go b/store/v2/storage/sqlite/db.go deleted file mode 100644 index 925648928601..000000000000 --- a/store/v2/storage/sqlite/db.go +++ /dev/null @@ -1,360 +0,0 @@ -package sqlite - -import ( - "bytes" - "database/sql" - "errors" - "fmt" - "path/filepath" - "strings" - - _ "github.com/mattn/go-sqlite3" - - corestore "cosmossdk.io/core/store" - "cosmossdk.io/store/v2" - storeerrors "cosmossdk.io/store/v2/errors" - "cosmossdk.io/store/v2/storage" -) - -const ( - driverName = "sqlite3" - dbName = "ss.db?cache=shared&mode=rwc&_journal_mode=WAL" - reservedStoreKey = "_RESERVED_" - keyLatestHeight = "latest_height" - keyPruneHeight = "prune_height" - valueRemovedStore = "removed_store" - - reservedUpsertStmt = ` - INSERT INTO state_storage(store_key, key, value, version) - VALUES(?, ?, ?, ?) - ON CONFLICT(store_key, key, version) DO UPDATE SET - value = ?; - ` - upsertStmt = ` - INSERT INTO state_storage(store_key, key, value, version) - VALUES(?, ?, ?, ?) - ON CONFLICT(store_key, key, version) DO UPDATE SET - value = ?; - ` - delStmt = ` - UPDATE state_storage SET tombstone = ? - WHERE id = ( - SELECT id FROM state_storage WHERE store_key = ? AND key = ? AND version <= ? ORDER BY version DESC LIMIT 1 - ) AND tombstone = 0; - ` -) - -var ( - _ storage.Database = (*Database)(nil) - _ store.UpgradableDatabase = (*Database)(nil) -) - -type Database struct { - storage *sql.DB - - // earliestVersion defines the earliest version set in the database, which is - // only updated when the database is pruned. - earliestVersion uint64 -} - -func New(dataDir string) (*Database, error) { - storage, err := sql.Open(driverName, filepath.Join(dataDir, dbName)) - if err != nil { - return nil, fmt.Errorf("failed to open sqlite DB: %w", err) - } - - stmt := ` - CREATE TABLE IF NOT EXISTS state_storage ( - id integer not null primary key, - store_key varchar not null, - key varchar not null, - value varchar not null, - version integer unsigned not null, - tombstone integer unsigned default 0, - unique (store_key, key, version) - ); - - CREATE UNIQUE INDEX IF NOT EXISTS idx_store_key_version ON state_storage (store_key, key, version); - ` - _, err = storage.Exec(stmt) - if err != nil { - return nil, fmt.Errorf("failed to exec SQL statement: %w", err) - } - - pruneHeight, err := getPruneHeight(storage) - if err != nil { - return nil, fmt.Errorf("failed to get prune height: %w", err) - } - - return &Database{ - storage: storage, - earliestVersion: pruneHeight, - }, nil -} - -func (db *Database) Close() error { - err := db.storage.Close() - db.storage = nil - return err -} - -func (db *Database) NewBatch(version uint64) (store.Batch, error) { - return NewBatch(db.storage, version) -} - -func (db *Database) GetLatestVersion() (uint64, error) { - stmt, err := db.storage.Prepare(` - SELECT value - FROM state_storage - WHERE store_key = ? AND key = ? - `) - if err != nil { - return 0, fmt.Errorf("failed to prepare SQL statement: %w", err) - } - - defer stmt.Close() - - var latestHeight uint64 - if err := stmt.QueryRow(reservedStoreKey, keyLatestHeight).Scan(&latestHeight); err != nil { - if errors.Is(err, sql.ErrNoRows) { - // in case of a fresh database - return 0, nil - } - - return 0, fmt.Errorf("failed to query row: %w", err) - } - - return latestHeight, nil -} - -func (db *Database) VersionExists(v uint64) (bool, error) { - latestVersion, err := db.GetLatestVersion() - if err != nil { - return false, err - } - - return latestVersion >= v && v >= db.earliestVersion, nil -} - -func (db *Database) SetLatestVersion(version uint64) error { - _, err := db.storage.Exec(reservedUpsertStmt, reservedStoreKey, keyLatestHeight, version, 0, version) - if err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - - return nil -} - -func (db *Database) Has(storeKey []byte, version uint64, key []byte) (bool, error) { - val, err := db.Get(storeKey, version, key) - if err != nil { - return false, err - } - - return val != nil, nil -} - -func (db *Database) Get(storeKey []byte, targetVersion uint64, key []byte) ([]byte, error) { - if targetVersion < db.earliestVersion { - return nil, storeerrors.ErrVersionPruned{EarliestVersion: db.earliestVersion, RequestedVersion: targetVersion} - } - - stmt, err := db.storage.Prepare(` - SELECT value, tombstone FROM state_storage - WHERE store_key = ? AND key = ? AND version <= ? - ORDER BY version DESC LIMIT 1; - `) - if err != nil { - return nil, fmt.Errorf("failed to prepare SQL statement: %w", err) - } - - defer stmt.Close() - - var ( - value []byte - tomb uint64 - ) - if err := stmt.QueryRow(storeKey, key, targetVersion).Scan(&value, &tomb); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, nil - } - - return nil, fmt.Errorf("failed to query row: %w", err) - } - - // A tombstone of zero or a target version that is less than the tombstone - // version means the key is not deleted at the target version. - if tomb == 0 || targetVersion < tomb { - return value, nil - } - - // the value is considered deleted - return nil, nil -} - -// Prune removes all versions of all keys that are <= the given version. It keeps -// the latest (non-tombstoned) version of each key/value tuple to handle queries -// above the prune version. This is analogous to RocksDB full_history_ts_low. -// -// We perform the prune by deleting all versions of a key, excluding reserved keys, -// that are <= the given version, except for the latest version of the key. -func (db *Database) Prune(version uint64) error { - tx, err := db.storage.Begin() - if err != nil { - return fmt.Errorf("failed to create SQL transaction: %w", err) - } - defer func() { - if err != nil { - err = tx.Rollback() - } - }() - - // prune all keys of old versions - pruneStmt := `DELETE FROM state_storage - WHERE version < ( - SELECT max(version) FROM state_storage t2 WHERE - t2.store_key = state_storage.store_key AND - t2.key = state_storage.key AND - t2.version <= ? - ) AND store_key != ?; - ` - if _, err := tx.Exec(pruneStmt, version, reservedStoreKey); err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - - // prune removed stores - pruneRemovedStoreKeysStmt := `DELETE FROM state_storage AS s - WHERE EXISTS ( - SELECT 1 FROM - ( - SELECT key, MAX(version) AS max_version - FROM state_storage - WHERE store_key = ? AND value = ? AND version <= ? - GROUP BY key - ) AS t - WHERE s.store_key = t.key AND s.version <= t.max_version LIMIT 1 - ); - ` - if _, err := tx.Exec(pruneRemovedStoreKeysStmt, reservedStoreKey, valueRemovedStore, version, version); err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - - // delete the removedKeys - if _, err := tx.Exec("DELETE FROM state_storage WHERE store_key = ? AND value = ? AND version <= ?", reservedStoreKey, valueRemovedStore, version); err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - - // set the prune height so we can return for queries below this height - if _, err := tx.Exec(reservedUpsertStmt, reservedStoreKey, keyPruneHeight, version, 0, version); err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - - if err := tx.Commit(); err != nil { - return fmt.Errorf("failed to write SQL transaction: %w", err) - } - - db.earliestVersion = version + 1 - return nil -} - -func (db *Database) Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { - if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, storeerrors.ErrKeyEmpty - } - - if start != nil && end != nil && bytes.Compare(start, end) > 0 { - return nil, storeerrors.ErrStartAfterEnd - } - - return newIterator(db, storeKey, version, start, end, false) -} - -func (db *Database) ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { - if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) { - return nil, storeerrors.ErrKeyEmpty - } - - if start != nil && end != nil && bytes.Compare(start, end) > 0 { - return nil, storeerrors.ErrStartAfterEnd - } - - return newIterator(db, storeKey, version, start, end, true) -} - -func (db *Database) PruneStoreKeys(storeKeys []string, version uint64) (err error) { - tx, err := db.storage.Begin() - if err != nil { - return fmt.Errorf("failed to create SQL transaction: %w", err) - } - defer func() { - if err != nil { - err = tx.Rollback() - } - }() - - // flush removed store keys - flushRemovedStoreKeyStmt := `INSERT INTO state_storage(store_key, key, value, version) - VALUES (?, ?, ?, ?)` - for _, storeKey := range storeKeys { - if _, err := tx.Exec(flushRemovedStoreKeyStmt, reservedStoreKey, []byte(storeKey), valueRemovedStore, version); err != nil { - return fmt.Errorf("failed to exec SQL statement: %w", err) - } - } - - return tx.Commit() -} - -func (db *Database) PrintRowsDebug() { - stmt, err := db.storage.Prepare("SELECT store_key, key, value, version, tombstone FROM state_storage") - if err != nil { - panic(fmt.Errorf("failed to prepare SQL statement: %w", err)) - } - - defer stmt.Close() - - rows, err := stmt.Query() - if err != nil { - panic(fmt.Errorf("failed to execute SQL query: %w", err)) - } - - var sb strings.Builder - for rows.Next() { - var ( - storeKey []byte - key []byte - value []byte - version uint64 - tomb uint64 - ) - if err := rows.Scan(&storeKey, &key, &value, &version, &tomb); err != nil { - panic(fmt.Sprintf("failed to scan row: %s", err)) - } - - sb.WriteString(fmt.Sprintf("STORE_KEY: %s, KEY: %s, VALUE: %s, VERSION: %d, TOMBSTONE: %d\n", storeKey, key, value, version, tomb)) - } - if err := rows.Err(); err != nil { - panic(fmt.Errorf("received unexpected error: %w", err)) - } - - fmt.Println(strings.TrimSpace(sb.String())) -} - -func getPruneHeight(storage *sql.DB) (uint64, error) { - stmt, err := storage.Prepare(`SELECT value FROM state_storage WHERE store_key = ? AND key = ?`) - if err != nil { - return 0, fmt.Errorf("failed to prepare SQL statement: %w", err) - } - - defer stmt.Close() - - var value uint64 - if err := stmt.QueryRow(reservedStoreKey, keyPruneHeight).Scan(&value); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return 0, nil - } - - return 0, fmt.Errorf("failed to query row: %w", err) - } - - return value, nil -} diff --git a/store/v2/storage/sqlite/db_test.go b/store/v2/storage/sqlite/db_test.go deleted file mode 100644 index fc4c27e6bfd9..000000000000 --- a/store/v2/storage/sqlite/db_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package sqlite - -import ( - "fmt" - "sync" - "testing" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - coretesting "cosmossdk.io/core/testing" - "cosmossdk.io/store/v2/storage" -) - -var storeKey1 = []byte("store1") - -func TestStorageTestSuite(t *testing.T) { - s := &storage.StorageTestSuite{ - NewDB: func(dir string) (*storage.StorageStore, error) { - db, err := New(dir) - return storage.NewStorageStore(db, coretesting.NewNopLogger()), err - }, - EmptyBatchSize: 0, - } - suite.Run(t, s) -} - -func TestDatabase_ReverseIterator(t *testing.T) { - db, err := New(t.TempDir()) - require.NoError(t, err) - defer db.Close() - - batch, err := db.NewBatch(1) - require.NoError(t, err) - for i := 0; i < 100; i++ { - key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 - val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 - - require.NoError(t, batch.Set(storeKey1, []byte(key), []byte(val))) - } - - require.NoError(t, batch.Write()) - - // reverse iterator without an end key - iter, err := db.ReverseIterator(storeKey1, 1, []byte("key000"), nil) - require.NoError(t, err) - - defer iter.Close() - - i, count := 99, 0 - for ; iter.Valid(); iter.Next() { - require.Equal(t, []byte(fmt.Sprintf("key%03d", i)), iter.Key()) - require.Equal(t, []byte(fmt.Sprintf("val%03d", i)), iter.Value()) - - i-- - count++ - } - require.Equal(t, 100, count) - require.NoError(t, iter.Error()) - - // seek past domain, which should make the iterator invalid and produce an error - require.False(t, iter.Valid()) - - // reverse iterator with a start and end domain - iter2, err := db.ReverseIterator(storeKey1, 1, []byte("key010"), []byte("key019")) - require.NoError(t, err) - - defer iter2.Close() - - i, count = 18, 0 - for ; iter2.Valid(); iter2.Next() { - require.Equal(t, []byte(fmt.Sprintf("key%03d", i)), iter2.Key()) - require.Equal(t, []byte(fmt.Sprintf("val%03d", i)), iter2.Value()) - - i-- - count++ - } - require.Equal(t, 9, count) - require.NoError(t, iter2.Error()) - - // seek past domain, which should make the iterator invalid and produce an error - require.False(t, iter2.Valid()) - - // start must be <= end - iter3, err := db.ReverseIterator(storeKey1, 1, []byte("key020"), []byte("key019")) - require.Error(t, err) - require.Nil(t, iter3) -} - -func TestParallelWrites(t *testing.T) { - db, err := New(t.TempDir()) - require.NoError(t, err) - defer db.Close() - - latestVersion := 10 - kvCount := 100 - - wg := sync.WaitGroup{} - triggerStartCh := make(chan bool) - - // start 10 goroutines that write to the database - for i := 0; i < latestVersion; i++ { - wg.Add(1) - go func(i int) { - <-triggerStartCh - defer wg.Done() - batch, err := db.NewBatch(uint64(i + 1)) - require.NoError(t, err) - for j := 0; j < kvCount; j++ { - key := fmt.Sprintf("key-%d-%03d", i, j) - val := fmt.Sprintf("val-%d-%03d", i, j) - - require.NoError(t, batch.Set(storeKey1, []byte(key), []byte(val))) - } - - require.NoError(t, batch.Write()) - }(i) - - } - - // start the goroutines - close(triggerStartCh) - wg.Wait() - - // check that all the data is there - for i := 0; i < latestVersion; i++ { - for j := 0; j < kvCount; j++ { - version := uint64(i + 1) - key := fmt.Sprintf("key-%d-%03d", i, j) - val := fmt.Sprintf("val-%d-%03d", i, j) - - v, err := db.Get(storeKey1, version, []byte(key)) - require.NoError(t, err) - require.Equal(t, []byte(val), v) - } - } -} - -func TestParallelWriteAndPruning(t *testing.T) { - db, err := New(t.TempDir()) - require.NoError(t, err) - defer db.Close() - - latestVersion := 100 - kvCount := 100 - prunePeriod := 5 - - wg := sync.WaitGroup{} - triggerStartCh := make(chan bool) - - // start a goroutine that write to the database - wg.Add(1) - go func() { - <-triggerStartCh - defer wg.Done() - for i := 0; i < latestVersion; i++ { - batch, err := db.NewBatch(uint64(i + 1)) - require.NoError(t, err) - for j := 0; j < kvCount; j++ { - key := fmt.Sprintf("key-%d-%03d", i, j) - val := fmt.Sprintf("val-%d-%03d", i, j) - - require.NoError(t, batch.Set(storeKey1, []byte(key), []byte(val))) - } - - require.NoError(t, batch.Write()) - } - }() - // start a goroutine that prunes the database - wg.Add(1) - go func() { - <-triggerStartCh - defer wg.Done() - for i := 10; i < latestVersion; i += prunePeriod { - for { - v, err := db.GetLatestVersion() - require.NoError(t, err) - if v > uint64(i) { - require.NoError(t, db.Prune(v-1)) - break - } - } - } - }() - - // start the goroutines - close(triggerStartCh) - wg.Wait() - - // check if the data is pruned - version := uint64(latestVersion - prunePeriod) - val, err := db.Get(storeKey1, version, []byte(fmt.Sprintf("key-%d-%03d", version-1, 0))) - require.Error(t, err) - require.Nil(t, val) - - version = uint64(latestVersion) - val, err = db.Get(storeKey1, version, []byte(fmt.Sprintf("key-%d-%03d", version-1, 0))) - require.NoError(t, err) - require.Equal(t, []byte(fmt.Sprintf("val-%d-%03d", version-1, 0)), val) -} diff --git a/store/v2/storage/sqlite/iterator.go b/store/v2/storage/sqlite/iterator.go deleted file mode 100644 index daf7e073db3c..000000000000 --- a/store/v2/storage/sqlite/iterator.go +++ /dev/null @@ -1,183 +0,0 @@ -package sqlite - -import ( - "bytes" - "database/sql" - "fmt" - "slices" - "strings" - - corestore "cosmossdk.io/core/store" -) - -var _ corestore.Iterator = (*iterator)(nil) - -type iterator struct { - statement *sql.Stmt - rows *sql.Rows - key, val []byte - start, end []byte - valid bool - err error -} - -func newIterator(db *Database, storeKey []byte, targetVersion uint64, start, end []byte, reverse bool) (*iterator, error) { - if targetVersion < db.earliestVersion { - return &iterator{ - start: start, - end: end, - valid: false, - }, nil - } - - var ( - keyClause = []string{"store_key = ?", "version <= ?"} - queryArgs []any - ) - - switch { - case len(start) > 0 && len(end) > 0: - keyClause = append(keyClause, "key >= ?", "key < ?") - queryArgs = []any{storeKey, targetVersion, start, end, targetVersion} - - case len(start) > 0 && len(end) == 0: - keyClause = append(keyClause, "key >= ?") - queryArgs = []any{storeKey, targetVersion, start, targetVersion} - - case len(start) == 0 && len(end) > 0: - keyClause = append(keyClause, "key < ?") - queryArgs = []any{storeKey, targetVersion, end, targetVersion} - - default: - queryArgs = []any{storeKey, targetVersion, targetVersion} - } - - orderBy := "ASC" - if reverse { - orderBy = "DESC" - } - - // Note, this is not susceptible to SQL injection because placeholders are used - // for parts of the query outside the store's direct control. - stmt, err := db.storage.Prepare(fmt.Sprintf(` - SELECT x.key, x.value - FROM ( - SELECT key, value, version, tombstone, - row_number() OVER (PARTITION BY key ORDER BY version DESC) AS _rn - FROM state_storage WHERE %s - ) x - WHERE x._rn = 1 AND (x.tombstone = 0 OR x.tombstone > ?) ORDER BY x.key %s; - `, strings.Join(keyClause, " AND "), orderBy)) - if err != nil { - return nil, fmt.Errorf("failed to prepare SQL statement: %w", err) - } - - rows, err := stmt.Query(queryArgs...) - if err != nil { - _ = stmt.Close() - return nil, fmt.Errorf("failed to execute SQL query: %w", err) - } - - itr := &iterator{ - statement: stmt, - rows: rows, - start: start, - end: end, - valid: rows.Next(), - } - if !itr.valid { - itr.err = fmt.Errorf("iterator invalid: %w", sql.ErrNoRows) - return itr, nil - } - - // read the first row - itr.parseRow() - if !itr.valid { - return itr, nil - } - - return itr, nil -} - -func (itr *iterator) Close() (err error) { - if itr.statement != nil { - err = itr.statement.Close() - } - - itr.valid = false - itr.statement = nil - itr.rows = nil - - return err -} - -// Domain returns the domain of the iterator. The caller must not modify the -// return values. -func (itr *iterator) Domain() ([]byte, []byte) { - return itr.start, itr.end -} - -func (itr *iterator) Key() []byte { - itr.assertIsValid() - return slices.Clone(itr.key) -} - -func (itr *iterator) Value() []byte { - itr.assertIsValid() - return slices.Clone(itr.val) -} - -func (itr *iterator) Valid() bool { - if !itr.valid || itr.rows.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 *iterator) Next() { - if itr.rows.Next() { - itr.parseRow() - return - } - - itr.valid = false -} - -func (itr *iterator) Error() error { - if err := itr.rows.Err(); err != nil { - return err - } - - return itr.err -} - -func (itr *iterator) parseRow() { - var ( - key []byte - value []byte - ) - if err := itr.rows.Scan(&key, &value); err != nil { - itr.err = fmt.Errorf("failed to scan row: %w", err) - itr.valid = false - return - } - - itr.key = key - itr.val = value -} - -func (itr *iterator) assertIsValid() { - if !itr.valid { - panic("iterator is invalid") - } -} diff --git a/store/v2/storage/storage_bench_test.go b/store/v2/storage/storage_bench_test.go index 2e2030bec6bb..36de5772ef6a 100644 --- a/store/v2/storage/storage_bench_test.go +++ b/store/v2/storage/storage_bench_test.go @@ -18,7 +18,6 @@ import ( "cosmossdk.io/store/v2/storage" "cosmossdk.io/store/v2/storage/pebbledb" "cosmossdk.io/store/v2/storage/rocksdb" - "cosmossdk.io/store/v2/storage/sqlite" ) var storeKey1 = []byte("store1") @@ -37,10 +36,6 @@ var ( return storage.NewStorageStore(db, coretesting.NewNopLogger()), err }, - "btree_sqlite": func(dataDir string) (store.VersionedWriter, error) { - db, err := sqlite.New(dataDir) - return storage.NewStorageStore(db, coretesting.NewNopLogger()), err - }, } rng = rand.New(rand.NewSource(567320)) ) diff --git a/store/v2/storage/store.go b/store/v2/storage/store.go index 5ca5a30132b7..d53e323774ea 100644 --- a/store/v2/storage/store.go +++ b/store/v2/storage/store.go @@ -128,7 +128,8 @@ func (ss *StorageStore) Restore(version uint64, chStorage <-chan *corestore.Stat if err := b.Write(); err != nil { return err } - if err := b.Reset(); err != nil { + b, err = ss.db.NewBatch(version) + if err != nil { return err } } diff --git a/tests/go.mod b/tests/go.mod index 7c0ce2103844..ef77ab0e0775 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -175,7 +175,6 @@ require ( github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index 0fa666f7aff1..df0a5f393011 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -660,8 +660,6 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= diff --git a/tools/confix/data/v2-app.toml b/tools/confix/data/v2-app.toml index e478eed3609a..394cc41e7b09 100644 --- a/tools/confix/data/v2-app.toml +++ b/tools/confix/data/v2-app.toml @@ -56,8 +56,8 @@ minimum-gas-prices = '0stake' app-db-backend = 'goleveldb' [store.options] -# State storage database type. Currently we support: "sqlite", "pebble" and "rocksdb" -ss-type = 'sqlite' +# State storage database type. Currently we support: "pebble" and "rocksdb" +ss-type = 'pebble' # State commitment database type. Currently we support: "iavl" and "iavl-v2" sc-type = 'iavl' From 26afbaf0d17977796355b6c63a5e10ed684eb670 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Sat, 30 Nov 2024 04:43:56 +0100 Subject: [PATCH 2/4] fix(store/v2): don't delete future version when calling LoadVersion (#22681) --- store/v2/commitment/iavl/tree.go | 7 +++ store/v2/commitment/iavlv2/tree.go | 4 ++ store/v2/commitment/mem/tree.go | 4 ++ store/v2/commitment/store.go | 25 ++++++++--- store/v2/commitment/tree.go | 1 + store/v2/database.go | 4 ++ store/v2/mock/db_mock.go | 14 ++++++ store/v2/root/store.go | 27 +++++++++--- store/v2/root/store_test.go | 68 ++++++++++++++++++++++++++++++ store/v2/store.go | 4 ++ 10 files changed, 147 insertions(+), 11 deletions(-) diff --git a/store/v2/commitment/iavl/tree.go b/store/v2/commitment/iavl/tree.go index 5503218e66a1..5047e8ef6ed4 100644 --- a/store/v2/commitment/iavl/tree.go +++ b/store/v2/commitment/iavl/tree.go @@ -65,6 +65,13 @@ func (t *IavlTree) WorkingHash() []byte { // LoadVersion loads the state at the given version. func (t *IavlTree) LoadVersion(version uint64) error { + _, err := t.tree.LoadVersion(int64(version)) + return err +} + +// LoadVersionForOverwriting loads the state at the given version. +// Any versions greater than targetVersion will be deleted. +func (t *IavlTree) LoadVersionForOverwriting(version uint64) error { return t.tree.LoadVersionForOverwriting(int64(version)) } diff --git a/store/v2/commitment/iavlv2/tree.go b/store/v2/commitment/iavlv2/tree.go index 997a0a60cc1a..14b7967a6c78 100644 --- a/store/v2/commitment/iavlv2/tree.go +++ b/store/v2/commitment/iavlv2/tree.go @@ -64,6 +64,10 @@ func (t *Tree) LoadVersion(version uint64) error { return t.tree.LoadVersion(int64(version)) } +func (t *Tree) LoadVersionForOverwriting(version uint64) error { + return t.LoadVersion(version) // TODO: implement overwriting +} + func (t *Tree) Commit() ([]byte, uint64, error) { h, v, err := t.tree.SaveVersion() return h, uint64(v), err diff --git a/store/v2/commitment/mem/tree.go b/store/v2/commitment/mem/tree.go index cbc28ce7d9ae..bf0e95bfa9c1 100644 --- a/store/v2/commitment/mem/tree.go +++ b/store/v2/commitment/mem/tree.go @@ -34,6 +34,10 @@ func (t *Tree) LoadVersion(version uint64) error { return nil } +func (t *Tree) LoadVersionForOverwriting(version uint64) error { + return nil +} + func (t *Tree) Commit() ([]byte, uint64, error) { return nil, 0, nil } diff --git a/store/v2/commitment/store.go b/store/v2/commitment/store.go index e9f2ee8379c7..5219255f95ca 100644 --- a/store/v2/commitment/store.go +++ b/store/v2/commitment/store.go @@ -87,7 +87,16 @@ func (c *CommitStore) LoadVersion(targetVersion uint64) error { for storeKey := range c.multiTrees { storeKeys = append(storeKeys, storeKey) } - return c.loadVersion(targetVersion, storeKeys) + return c.loadVersion(targetVersion, storeKeys, false) +} + +func (c *CommitStore) LoadVersionForOverwriting(targetVersion uint64) error { + storeKeys := make([]string, 0, len(c.multiTrees)) + for storeKey := range c.multiTrees { + storeKeys = append(storeKeys, storeKey) + } + + return c.loadVersion(targetVersion, storeKeys, true) } // LoadVersionAndUpgrade implements store.UpgradeableStore. @@ -133,10 +142,10 @@ func (c *CommitStore) LoadVersionAndUpgrade(targetVersion uint64, upgrades *core return err } - return c.loadVersion(targetVersion, newStoreKeys) + return c.loadVersion(targetVersion, newStoreKeys, true) } -func (c *CommitStore) loadVersion(targetVersion uint64, storeKeys []string) error { +func (c *CommitStore) loadVersion(targetVersion uint64, storeKeys []string, overrideAfter bool) error { // Rollback the metadata to the target version. latestVersion, err := c.GetLatestVersion() if err != nil { @@ -154,8 +163,14 @@ func (c *CommitStore) loadVersion(targetVersion uint64, storeKeys []string) erro } for _, storeKey := range storeKeys { - if err := c.multiTrees[storeKey].LoadVersion(targetVersion); err != nil { - return err + if overrideAfter { + if err := c.multiTrees[storeKey].LoadVersionForOverwriting(targetVersion); err != nil { + return err + } + } else { + if err := c.multiTrees[storeKey].LoadVersion(targetVersion); err != nil { + return err + } } } diff --git a/store/v2/commitment/tree.go b/store/v2/commitment/tree.go index f57eabd20724..58a8b20beff2 100644 --- a/store/v2/commitment/tree.go +++ b/store/v2/commitment/tree.go @@ -25,6 +25,7 @@ type Tree interface { Version() uint64 LoadVersion(version uint64) error + LoadVersionForOverwriting(version uint64) error Commit() ([]byte, uint64, error) SetInitialVersion(version uint64) error GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error) diff --git a/store/v2/database.go b/store/v2/database.go index 27d0973ec18e..e3361d731024 100644 --- a/store/v2/database.go +++ b/store/v2/database.go @@ -50,6 +50,10 @@ type Committer interface { // LoadVersion loads the tree at the given version. LoadVersion(targetVersion uint64) error + // LoadVersionForOverwriting loads the tree at the given version. + // Any versions greater than targetVersion will be deleted. + LoadVersionForOverwriting(targetVersion uint64) error + // Commit commits the working tree to the database. Commit(version uint64) (*proof.CommitInfo, error) diff --git a/store/v2/mock/db_mock.go b/store/v2/mock/db_mock.go index 9b962affb102..ba65f2baf243 100644 --- a/store/v2/mock/db_mock.go +++ b/store/v2/mock/db_mock.go @@ -158,6 +158,20 @@ func (mr *MockStateCommitterMockRecorder) LoadVersionAndUpgrade(version, upgrade return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadVersionAndUpgrade", reflect.TypeOf((*MockStateCommitter)(nil).LoadVersionAndUpgrade), version, upgrades) } +// LoadVersionForOverwriting mocks base method. +func (m *MockStateCommitter) LoadVersionForOverwriting(targetVersion uint64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadVersionForOverwriting", targetVersion) + ret0, _ := ret[0].(error) + return ret0 +} + +// LoadVersionForOverwriting indicates an expected call of LoadVersionForOverwriting. +func (mr *MockStateCommitterMockRecorder) LoadVersionForOverwriting(targetVersion any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadVersionForOverwriting", reflect.TypeOf((*MockStateCommitter)(nil).LoadVersionForOverwriting), targetVersion) +} + // PausePruning mocks base method. func (m *MockStateCommitter) PausePruning(pause bool) { m.ctrl.T.Helper() diff --git a/store/v2/root/store.go b/store/v2/root/store.go index 59363e2fb35b..b40baef6424e 100644 --- a/store/v2/root/store.go +++ b/store/v2/root/store.go @@ -250,7 +250,7 @@ func (s *Store) LoadLatestVersion() error { return err } - return s.loadVersion(lv, nil) + return s.loadVersion(lv, nil, false) } func (s *Store) LoadVersion(version uint64) error { @@ -259,7 +259,16 @@ func (s *Store) LoadVersion(version uint64) error { defer s.telemetry.MeasureSince(now, "root_store", "load_version") } - return s.loadVersion(version, nil) + return s.loadVersion(version, nil, false) +} + +func (s *Store) LoadVersionForOverwriting(version uint64) error { + if s.telemetry != nil { + now := time.Now() + defer s.telemetry.MeasureSince(now, "root_store", "load_version_for_overwriting") + } + + return s.loadVersion(version, nil, true) } // LoadVersionAndUpgrade implements the UpgradeableStore interface. @@ -278,7 +287,7 @@ func (s *Store) LoadVersionAndUpgrade(version uint64, upgrades *corestore.StoreU return errors.New("cannot upgrade while migrating") } - if err := s.loadVersion(version, upgrades); err != nil { + if err := s.loadVersion(version, upgrades, true); err != nil { return err } @@ -294,12 +303,18 @@ func (s *Store) LoadVersionAndUpgrade(version uint64, upgrades *corestore.StoreU return nil } -func (s *Store) loadVersion(v uint64, upgrades *corestore.StoreUpgrades) error { +func (s *Store) loadVersion(v uint64, upgrades *corestore.StoreUpgrades, overrideAfter bool) error { s.logger.Debug("loading version", "version", v) if upgrades == nil { - if err := s.stateCommitment.LoadVersion(v); err != nil { - return fmt.Errorf("failed to load SC version %d: %w", v, err) + if !overrideAfter { + if err := s.stateCommitment.LoadVersion(v); err != nil { + return fmt.Errorf("failed to load SC version %d: %w", v, err) + } + } else { + if err := s.stateCommitment.LoadVersionForOverwriting(v); err != nil { + return fmt.Errorf("failed to load SC version %d: %w", v, err) + } } } else { // if upgrades are provided, we need to load the version and apply the upgrades diff --git a/store/v2/root/store_test.go b/store/v2/root/store_test.go index 59a490b11b00..8bb6b5604e2d 100644 --- a/store/v2/root/store_test.go +++ b/store/v2/root/store_test.go @@ -256,6 +256,74 @@ func (s *RootStoreTestSuite) TestLoadVersion() { s.Require().NoError(err) s.Require().Equal([]byte("val003"), val) + // attempt to write and commit a few changesets + for v := 4; v <= 5; v++ { + val := fmt.Sprintf("overwritten_val%03d", v) // overwritten_val004, overwritten_val005 + + cs := corestore.NewChangeset(uint64(v)) + cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false) + + _, err := s.rootStore.Commit(cs) + s.Require().Error(err) + } + + // ensure the latest version is correct + latest, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(3), latest) // should have stayed at 3 after failed commits + + // query state and ensure values returned are based on the loaded version + _, ro, err = s.rootStore.StateLatest() + s.Require().NoError(err) + + reader, err = ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + val, err = reader.Get([]byte("key")) + s.Require().NoError(err) + s.Require().Equal([]byte("val003"), val) +} + +func (s *RootStoreTestSuite) TestLoadVersionForOverwriting() { + // write and commit a few changesets + for v := uint64(1); v <= 5; v++ { + val := fmt.Sprintf("val%03d", v) // val001, val002, ..., val005 + + cs := corestore.NewChangeset(v) + cs.Add(testStoreKeyBytes, []byte("key"), []byte(val), false) + + commitHash, err := s.rootStore.Commit(cs) + s.Require().NoError(err) + s.Require().NotNil(commitHash) + } + + // ensure the latest version is correct + latest, err := s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(5), latest) + + // attempt to load a non-existent version + err = s.rootStore.LoadVersionForOverwriting(6) + s.Require().Error(err) + + // attempt to load a previously committed version + err = s.rootStore.LoadVersionForOverwriting(3) + s.Require().NoError(err) + + // ensure the latest version is correct + latest, err = s.rootStore.GetLatestVersion() + s.Require().NoError(err) + s.Require().Equal(uint64(3), latest) + + // query state and ensure values returned are based on the loaded version + _, ro, err := s.rootStore.StateLatest() + s.Require().NoError(err) + + reader, err := ro.GetReader(testStoreKeyBytes) + s.Require().NoError(err) + val, err := reader.Get([]byte("key")) + s.Require().NoError(err) + s.Require().Equal([]byte("val003"), val) + // attempt to write and commit a few changesets for v := 4; v <= 5; v++ { val := fmt.Sprintf("overwritten_val%03d", v) // overwritten_val004, overwritten_val005 diff --git a/store/v2/store.go b/store/v2/store.go index 124d7de579a1..bf967d0f78a6 100644 --- a/store/v2/store.go +++ b/store/v2/store.go @@ -30,6 +30,10 @@ type RootStore interface { // LoadVersion loads the RootStore to the given version. LoadVersion(version uint64) error + // LoadVersionForOverwriting loads the state at the given version. + // Any versions greater than targetVersion will be deleted. + LoadVersionForOverwriting(version uint64) error + // LoadLatestVersion behaves identically to LoadVersion except it loads the // latest version implicitly. LoadLatestVersion() error From 40430febc55b7b728cb8da65a16ba2c77cfbdafe Mon Sep 17 00:00:00 2001 From: Matt Kocubinski Date: Sun, 1 Dec 2024 12:49:31 -0600 Subject: [PATCH 3/4] fix(store/v2): iavl/v2 reverse iterator (#22699) Co-authored-by: Marko --- store/v2/commitment/iavlv2/tree.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/store/v2/commitment/iavlv2/tree.go b/store/v2/commitment/iavlv2/tree.go index 14b7967a6c78..43c25a23a2c8 100644 --- a/store/v2/commitment/iavlv2/tree.go +++ b/store/v2/commitment/iavlv2/tree.go @@ -113,7 +113,11 @@ func (t *Tree) Iterator(version uint64, start, end []byte, ascending bool) (core if int64(version) != t.tree.Version() { return nil, fmt.Errorf("loading past version not yet supported") } - return t.tree.Iterator(start, end, ascending) + if ascending { + return t.tree.Iterator(start, end, false) + } else { + return t.tree.ReverseIterator(start, end) + } } func (t *Tree) Export(version uint64) (commitment.Exporter, error) { From c207a45051b37959ca1046f31af1cf5bc12175a1 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Mon, 2 Dec 2024 15:59:29 +0800 Subject: [PATCH 4/4] fix(simapp/v2): add binary version cmd (#22705) --- scripts/build/build.mk | 4 +++- simapp/v2/simdv2/cmd/commands.go | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/build/build.mk b/scripts/build/build.mk index 3537c3f23123..92f7b6a20e9f 100644 --- a/scripts/build/build.mk +++ b/scripts/build/build.mk @@ -7,6 +7,7 @@ LEDGER_ENABLED ?= true BINDIR ?= $(GOPATH)/bin BUILDDIR ?= $(CURDIR)/build SIMAPP = simapp +APPNAME = simd MOCKS_DIR = $(CURDIR)/tests/mocks HTTPS_GIT := https://github.com/cosmos/cosmos-sdk.git DOCKER := $(shell which docker) @@ -53,6 +54,7 @@ endif ifeq (v2,$(findstring v2,$(COSMOS_BUILD_OPTIONS))) SIMAPP = simapp/v2 + APPNAME = simdv2 endif # DB backend selection @@ -86,7 +88,7 @@ build_tags_comma_sep := $(subst $(whitespace),$(comma),$(build_tags)) # process linker flags ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=sim \ - -X github.com/cosmos/cosmos-sdk/version.AppName=simd \ + -X github.com/cosmos/cosmos-sdk/version.AppName=$(APPNAME) \ -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" diff --git a/simapp/v2/simdv2/cmd/commands.go b/simapp/v2/simdv2/cmd/commands.go index 1edd3052ec4c..9b587e6dc477 100644 --- a/simapp/v2/simdv2/cmd/commands.go +++ b/simapp/v2/simdv2/cmd/commands.go @@ -26,6 +26,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/rpc" sdktelemetry "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/genutil" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" @@ -65,6 +66,7 @@ func InitRootCmd[T transaction.Tx]( txCommand(), keys.Commands(), offchain.OffChain(), + version.NewVersionCommand(), ) // build CLI skeleton for initial config parsing or a client application invocation