diff --git a/ss/pebbledb/db.go b/ss/pebbledb/db.go index 6a53665c..d802af43 100644 --- a/ss/pebbledb/db.go +++ b/ss/pebbledb/db.go @@ -222,6 +222,13 @@ func (db *Database) Get(storeKey string, targetVersion int64, key []byte) ([]byt } func (db *Database) ApplyChangeset(version int64, cs *proto.NamedChangeSet) error { + // Check if version is 0 and change it to 1 + // We do this specifically since keys written as part of genesis state come in as version 0 + // But pebbledb treats version 0 as special, so apply the changeset at version 1 instead + if version == 0 { + version = 1 + } + b, err := NewBatch(db.storage, version) if err != nil { return err diff --git a/ss/test/storage_test_suite.go b/ss/test/storage_test_suite.go index 35b52ed1..aec08f24 100644 --- a/ss/test/storage_test_suite.go +++ b/ss/test/storage_test_suite.go @@ -149,6 +149,41 @@ func (s *StorageTestSuite) TestDatabaseGetVersionedKey() { } } +func (s *StorageTestSuite) TestDatabaseVersionZero() { + // Db should write all keys at version 0 at version 1 + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + s.Require().NoError(DBApplyChangeset(db, 0, storeKey1, [][]byte{[]byte("key001")}, [][]byte{[]byte("value001")})) + s.Require().NoError(DBApplyChangeset(db, 0, storeKey1, [][]byte{[]byte("key002")}, [][]byte{[]byte("value002")})) + s.Require().NoError(DBApplyChangeset(db, 0, storeKey1, [][]byte{[]byte("key003")}, [][]byte{[]byte("value003")})) + + // Get at version 0 should return error + bz, _ := db.Get(storeKey1, 0, []byte("key001")) + s.Require().Nil(bz) + + bz, _ = db.Get(storeKey1, 0, []byte("key002")) + s.Require().Nil(bz) + + bz, _ = db.Get(storeKey1, 0, []byte("key002")) + s.Require().Nil(bz) + + // Retrieve each key at version 1 + bz, err = db.Get(storeKey1, 1, []byte("key001")) + s.Require().NoError(err) + s.Require().Equal([]byte("value001"), bz) + + bz, err = db.Get(storeKey1, 1, []byte("key002")) + s.Require().NoError(err) + s.Require().Equal([]byte("value002"), bz) + + bz, err = db.Get(storeKey1, 1, []byte("key003")) + s.Require().NoError(err) + s.Require().Equal([]byte("value003"), bz) + +} + func (s *StorageTestSuite) TestDatabaseApplyChangeset() { db, err := s.NewDB(s.T().TempDir()) s.Require().NoError(err) @@ -579,6 +614,23 @@ func (s *StorageTestSuite) TestDatabasePrune() { } } +func (s *StorageTestSuite) TestDatabasePruneAndTombstone() { + db, err := s.NewDB(s.T().TempDir()) + s.Require().NoError(err) + defer db.Close() + + // write a key at three different versions 1, 100 and 200 + s.Require().NoError(DBApplyChangeset(db, 100, storeKey1, [][]byte{[]byte("key000")}, [][]byte{[]byte("value001")})) + s.Require().NoError(DBApplyChangeset(db, 200, storeKey1, [][]byte{[]byte("key000")}, [][]byte{nil})) + + // prune version 150 + s.Require().NoError(db.Prune(150)) + + bz, err := db.Get(storeKey1, 160, []byte("key000")) + s.Require().NoError(err) + s.Require().Equal([]byte("value001"), bz) +} + func (s *StorageTestSuite) TestDatabasePruneKeepRecent() { if slices.Contains(s.SkipTests, s.T().Name()) { s.T().SkipNow() diff --git a/ss/test/utils.go b/ss/test/utils.go index 1ba6ffc7..6a3eaee6 100644 --- a/ss/test/utils.go +++ b/ss/test/utils.go @@ -42,10 +42,6 @@ func FillData(db types.StateStore, numKeys int, versions int) error { // Helper for creating the changeset and applying it to db func DBApplyChangeset(db types.StateStore, version int64, storeKey string, key, val [][]byte) error { - if version <= 0 { - panic("version must be greater than 0") - } - if len(key) != len(val) { panic("length of keys must match length of vals") }