refactor(store): fetch store keys from commit store if not provided (#20801)
This commit is contained in:
parent
07a50eefe2
commit
7c0ff0774f
96
store/v2/commitment/metadata.go
Normal file
96
store/v2/commitment/metadata.go
Normal file
@ -0,0 +1,96 @@
|
||||
package commitment
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
corestore "cosmossdk.io/core/store"
|
||||
"cosmossdk.io/store/v2/internal/encoding"
|
||||
"cosmossdk.io/store/v2/proof"
|
||||
)
|
||||
|
||||
const (
|
||||
commitInfoKeyFmt = "c/%d" // c/<version>
|
||||
latestVersionKey = "c/latest"
|
||||
)
|
||||
|
||||
type MetadataStore struct {
|
||||
kv corestore.KVStoreWithBatch
|
||||
}
|
||||
|
||||
func NewMetadataStore(kv corestore.KVStoreWithBatch) *MetadataStore {
|
||||
return &MetadataStore{
|
||||
kv: kv,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MetadataStore) GetLatestVersion() (uint64, error) {
|
||||
value, err := m.kv.Get([]byte(latestVersionKey))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if value == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
version, _, err := encoding.DecodeUvarint(value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func (m *MetadataStore) GetCommitInfo(version uint64) (*proof.CommitInfo, error) {
|
||||
key := []byte(fmt.Sprintf(commitInfoKeyFmt, version))
|
||||
value, err := m.kv.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cInfo := &proof.CommitInfo{}
|
||||
if err := cInfo.Unmarshal(value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cInfo, nil
|
||||
}
|
||||
|
||||
func (m *MetadataStore) flushCommitInfo(version uint64, cInfo *proof.CommitInfo) error {
|
||||
// do nothing if commit info is nil, as will be the case for an empty, initializing store
|
||||
if cInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
batch := m.kv.NewBatch()
|
||||
cInfoKey := []byte(fmt.Sprintf(commitInfoKeyFmt, version))
|
||||
value, err := cInfo.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.Set(cInfoKey, value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Grow(encoding.EncodeUvarintSize(version))
|
||||
if err := encoding.EncodeUvarint(&buf, version); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.Set([]byte(latestVersionKey), buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := batch.WriteSync(); err != nil {
|
||||
return err
|
||||
}
|
||||
return batch.Close()
|
||||
}
|
||||
|
||||
func (m *MetadataStore) deleteCommitInfo(version uint64) error {
|
||||
cInfoKey := []byte(fmt.Sprintf(commitInfoKeyFmt, version))
|
||||
return m.kv.Delete(cInfoKey)
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
package commitment
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -14,17 +13,11 @@ import (
|
||||
"cosmossdk.io/store/v2"
|
||||
"cosmossdk.io/store/v2/internal"
|
||||
"cosmossdk.io/store/v2/internal/conv"
|
||||
"cosmossdk.io/store/v2/internal/encoding"
|
||||
"cosmossdk.io/store/v2/proof"
|
||||
"cosmossdk.io/store/v2/snapshots"
|
||||
snapshotstypes "cosmossdk.io/store/v2/snapshots/types"
|
||||
)
|
||||
|
||||
const (
|
||||
commitInfoKeyFmt = "c/%d" // c/<version>
|
||||
latestVersionKey = "c/latest"
|
||||
)
|
||||
|
||||
var (
|
||||
_ store.Committer = (*CommitStore)(nil)
|
||||
_ snapshots.CommitSnapshotter = (*CommitStore)(nil)
|
||||
@ -38,7 +31,7 @@ var (
|
||||
// and trees.
|
||||
type CommitStore struct {
|
||||
logger log.Logger
|
||||
db corestore.KVStoreWithBatch
|
||||
metadata *MetadataStore
|
||||
multiTrees map[string]Tree
|
||||
}
|
||||
|
||||
@ -46,8 +39,8 @@ type CommitStore struct {
|
||||
func NewCommitStore(trees map[string]Tree, db corestore.KVStoreWithBatch, logger log.Logger) (*CommitStore, error) {
|
||||
return &CommitStore{
|
||||
logger: logger,
|
||||
db: db,
|
||||
multiTrees: trees,
|
||||
metadata: NewMetadataStore(db),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -96,23 +89,6 @@ func (c *CommitStore) WorkingCommitInfo(version uint64) *proof.CommitInfo {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommitStore) GetLatestVersion() (uint64, error) {
|
||||
value, err := c.db.Get([]byte(latestVersionKey))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if value == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
version, _, err := encoding.DecodeUvarint(value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func (c *CommitStore) LoadVersion(targetVersion uint64) error {
|
||||
// Rollback the metadata to the target version.
|
||||
latestVersion, err := c.GetLatestVersion()
|
||||
@ -120,17 +96,11 @@ func (c *CommitStore) LoadVersion(targetVersion uint64) error {
|
||||
return err
|
||||
}
|
||||
if targetVersion < latestVersion {
|
||||
batch := c.db.NewBatch()
|
||||
defer batch.Close()
|
||||
for version := latestVersion; version > targetVersion; version-- {
|
||||
cInfoKey := []byte(fmt.Sprintf(commitInfoKeyFmt, version))
|
||||
if err := batch.Delete(cInfoKey); err != nil {
|
||||
if err = c.metadata.deleteCommitInfo(version); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := batch.WriteSync(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, tree := range c.multiTrees {
|
||||
@ -146,54 +116,7 @@ func (c *CommitStore) LoadVersion(targetVersion uint64) error {
|
||||
cInfo = c.WorkingCommitInfo(targetVersion)
|
||||
}
|
||||
|
||||
return c.flushCommitInfo(targetVersion, cInfo)
|
||||
}
|
||||
|
||||
func (c *CommitStore) GetCommitInfo(version uint64) (*proof.CommitInfo, error) {
|
||||
key := []byte(fmt.Sprintf(commitInfoKeyFmt, version))
|
||||
value, err := c.db.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cInfo := &proof.CommitInfo{}
|
||||
if err := cInfo.Unmarshal(value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cInfo, nil
|
||||
}
|
||||
|
||||
func (c *CommitStore) flushCommitInfo(version uint64, cInfo *proof.CommitInfo) error {
|
||||
// do nothing if commit info is nil, as will be the case for an empty, initializing store
|
||||
if cInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
batch := c.db.NewBatch()
|
||||
defer batch.Close()
|
||||
cInfoKey := []byte(fmt.Sprintf(commitInfoKeyFmt, version))
|
||||
value, err := cInfo.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.Set(cInfoKey, value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Grow(encoding.EncodeUvarintSize(version))
|
||||
if err := encoding.EncodeUvarint(&buf, version); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := batch.Set([]byte(latestVersionKey), buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return batch.WriteSync()
|
||||
return c.metadata.flushCommitInfo(targetVersion, cInfo)
|
||||
}
|
||||
|
||||
func (c *CommitStore) Commit(version uint64) (*proof.CommitInfo, error) {
|
||||
@ -234,7 +157,7 @@ func (c *CommitStore) Commit(version uint64) (*proof.CommitInfo, error) {
|
||||
StoreInfos: storeInfos,
|
||||
}
|
||||
|
||||
if err := c.flushCommitInfo(version, cInfo); err != nil {
|
||||
if err := c.metadata.flushCommitInfo(version, cInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -261,7 +184,7 @@ func (c *CommitStore) GetProof(storeKey []byte, version uint64, key []byte) ([]p
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cInfo, err := c.GetCommitInfo(version)
|
||||
cInfo, err := c.metadata.GetCommitInfo(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -294,20 +217,11 @@ func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte,
|
||||
// Prune implements store.Pruner.
|
||||
func (c *CommitStore) Prune(version uint64) (ferr error) {
|
||||
// prune the metadata
|
||||
batch := c.db.NewBatch()
|
||||
defer batch.Close()
|
||||
for v := version; v > 0; v-- {
|
||||
cInfoKey := []byte(fmt.Sprintf(commitInfoKeyFmt, v))
|
||||
if exist, _ := c.db.Has(cInfoKey); !exist {
|
||||
break
|
||||
}
|
||||
if err := batch.Delete(cInfoKey); err != nil {
|
||||
if err := c.metadata.deleteCommitInfo(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := batch.WriteSync(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, tree := range c.multiTrees {
|
||||
if err := tree.Prune(version); err != nil {
|
||||
@ -480,6 +394,14 @@ loop:
|
||||
return snapshotItem, c.LoadVersion(version)
|
||||
}
|
||||
|
||||
func (c *CommitStore) GetCommitInfo(version uint64) (*proof.CommitInfo, error) {
|
||||
return c.metadata.GetCommitInfo(version)
|
||||
}
|
||||
|
||||
func (c *CommitStore) GetLatestVersion() (uint64, error) {
|
||||
return c.metadata.GetLatestVersion()
|
||||
}
|
||||
|
||||
func (c *CommitStore) Close() (ferr error) {
|
||||
for _, tree := range c.multiTrees {
|
||||
if err := tree.Close(); err != nil {
|
||||
|
||||
@ -22,8 +22,9 @@ type (
|
||||
// StoreInfo defines store-specific commit information. It contains a reference
|
||||
// between a store name/key and the commit ID.
|
||||
StoreInfo struct {
|
||||
Name []byte
|
||||
CommitID CommitID
|
||||
Name []byte
|
||||
CommitID CommitID
|
||||
Structure string
|
||||
}
|
||||
|
||||
// CommitID defines the commitment information when a specific store is
|
||||
@ -98,6 +99,7 @@ func (ci *CommitInfo) encodedSize() int {
|
||||
for _, storeInfo := range ci.StoreInfos {
|
||||
size += encoding.EncodeBytesSize(storeInfo.Name)
|
||||
size += encoding.EncodeBytesSize(storeInfo.CommitID.Hash)
|
||||
size += encoding.EncodeBytesSize([]byte(storeInfo.Structure))
|
||||
}
|
||||
return size
|
||||
}
|
||||
@ -110,6 +112,7 @@ func (ci *CommitInfo) encodedSize() int {
|
||||
// - for each store:
|
||||
// - store name (bytes)
|
||||
// - store hash (bytes)
|
||||
// - store commit structure (bytes)
|
||||
func (ci *CommitInfo) Marshal() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.Grow(ci.encodedSize())
|
||||
@ -130,6 +133,9 @@ func (ci *CommitInfo) Marshal() ([]byte, error) {
|
||||
if err := encoding.EncodeBytes(&buf, si.CommitID.Hash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := encoding.EncodeBytes(&buf, []byte(si.Structure)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
@ -172,6 +178,14 @@ func (ci *CommitInfo) Unmarshal(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
buf = buf[n:]
|
||||
// Structure
|
||||
structure, n, err := encoding.DecodeBytes(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[n:]
|
||||
ci.StoreInfos[i].Structure = string(structure)
|
||||
|
||||
ci.StoreInfos[i].CommitID = CommitID{
|
||||
Hash: hash,
|
||||
Version: ci.Version,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package proof
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -12,48 +13,59 @@ func TestGetStoreProof(t *testing.T) {
|
||||
storeInfos []StoreInfo
|
||||
}{
|
||||
{[]StoreInfo{
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}, "iavl"},
|
||||
}},
|
||||
{[]StoreInfo{
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}},
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}, "iavl"},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}, "iavl"},
|
||||
}},
|
||||
{[]StoreInfo{
|
||||
{[]byte("key3"), CommitID{1, []byte("value3")}},
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}},
|
||||
{[]byte("key3"), CommitID{1, []byte("value3")}, "iavl"},
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}, "iavl"},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}, "iavl"},
|
||||
}},
|
||||
{[]StoreInfo{
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}},
|
||||
{[]byte("key3"), CommitID{1, []byte("value3")}},
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}, "iavl"},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}, "iavl"},
|
||||
{[]byte("key3"), CommitID{1, []byte("value3")}, "iavl"},
|
||||
}},
|
||||
{[]StoreInfo{
|
||||
{[]byte("key4"), CommitID{1, []byte("value4")}},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}},
|
||||
{[]byte("key3"), CommitID{1, []byte("value3")}},
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}},
|
||||
{[]byte("key4"), CommitID{1, []byte("value4")}, "iavl"},
|
||||
{[]byte("key1"), CommitID{1, []byte("value1")}, "iavl"},
|
||||
{[]byte("key3"), CommitID{1, []byte("value3")}, "iavl"},
|
||||
{[]byte("key2"), CommitID{1, []byte("value2")}, "iavl"},
|
||||
}},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
// create a commit info
|
||||
ci := CommitInfo{
|
||||
Version: 1,
|
||||
Timestamp: time.Now(),
|
||||
StoreInfos: tc.storeInfos,
|
||||
}
|
||||
commitHash := ci.Hash()
|
||||
// make sure the store infos are sorted
|
||||
require.Equal(t, ci.StoreInfos[0].Name, []byte("key1"))
|
||||
for _, si := range tc.storeInfos {
|
||||
// get the proof
|
||||
_, proof, err := ci.GetStoreProof(si.Name)
|
||||
require.NoError(t, err, "test case %d", i)
|
||||
// verify the proof
|
||||
expRoots, err := proof.Run([][]byte{si.CommitID.Hash})
|
||||
require.NoError(t, err, "test case %d", i)
|
||||
require.Equal(t, commitHash, expRoots[0], "test case %d", i)
|
||||
}
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
// create a commit info
|
||||
ci := CommitInfo{
|
||||
Version: 1,
|
||||
Timestamp: time.Now(),
|
||||
StoreInfos: tc.storeInfos,
|
||||
}
|
||||
commitHash := ci.Hash()
|
||||
// make sure the store infos are sorted
|
||||
require.Equal(t, ci.StoreInfos[0].Name, []byte("key1"))
|
||||
for _, si := range tc.storeInfos {
|
||||
// get the proof
|
||||
_, proof, err := ci.GetStoreProof(si.Name)
|
||||
require.NoError(t, err, "test case %d", i)
|
||||
// verify the proof
|
||||
expRoots, err := proof.Run([][]byte{si.CommitID.Hash})
|
||||
require.NoError(t, err, "test case %d", i)
|
||||
require.Equal(t, commitHash, expRoots[0], "test case %d", i)
|
||||
|
||||
bz, err := ci.Marshal()
|
||||
require.NoError(t, err)
|
||||
var ci2 CommitInfo
|
||||
err = ci2.Unmarshal(bz)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ci.Timestamp.Equal(ci2.Timestamp))
|
||||
ci2.Timestamp = ci.Timestamp
|
||||
require.Equal(t, ci, ci2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +83,24 @@ func CreateRootStore(opts *FactoryOptions) (store.RootStore, error) {
|
||||
}
|
||||
ss = storage.NewStorageStore(ssDb, opts.Logger)
|
||||
|
||||
if len(opts.StoreKeys) == 0 {
|
||||
metadata := commitment.NewMetadataStore(opts.SCRawDB)
|
||||
latestVersion, err := metadata.GetLatestVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastCommitInfo, err := metadata.GetCommitInfo(latestVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lastCommitInfo == nil {
|
||||
return nil, fmt.Errorf("tried to construct a root store with no store keys specified but no commit info found for version %d", latestVersion)
|
||||
}
|
||||
for _, si := range lastCommitInfo.StoreInfos {
|
||||
opts.StoreKeys = append(opts.StoreKeys, string(si.Name))
|
||||
}
|
||||
}
|
||||
|
||||
trees := make(map[string]commitment.Tree)
|
||||
for _, key := range opts.StoreKeys {
|
||||
if internal.IsMemoryStoreKey(key) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user