cosmos-sdk/store/multistore.go

308 lines
7.0 KiB
Go
Raw Normal View History

2017-10-31 13:45:57 -07:00
package store
import (
"fmt"
"sort"
"github.com/tendermint/go-wire"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/merkle"
"golang.org/x/crypto/ripemd160"
)
const (
msLatestKey = "s/latest"
msStateKeyFmt = "s/%d" // s/<version>
)
type MultiStore interface {
2017-11-29 07:57:47 -08:00
// Cache wrap MultiStore.
// NOTE: Caller should probably not call .Write() on each, but
// call CacheMultiStore.Write().
CacheMultiStore() CacheMultiStore
2017-10-31 13:45:57 -07:00
// Convenience
GetStore(name string) interface{}
GetKVStore(name string) KVStore
GetIterKVStore(name string) IterKVStore
}
2017-11-29 07:57:47 -08:00
type CacheMultiStore interface {
MultiStore
Write() // Writes operations to underlying KVStore
}
2017-10-31 13:45:57 -07:00
//----------------------------------------
2017-11-29 07:57:47 -08:00
// rootMultiStore is composed of many Committers.
2017-10-31 13:45:57 -07:00
// Implements MultiStore.
2017-11-29 07:57:47 -08:00
type rootMultiStore struct {
2017-10-31 13:45:57 -07:00
db dbm.DB
2017-11-29 07:57:47 -08:00
version int64
2017-10-31 13:45:57 -07:00
storeLoaders map[string]CommitterLoader
substores map[string]Committer
}
2017-11-29 07:57:47 -08:00
func NewMultiStore(db dbm.DB) *rootMultiStore {
return &rootMultiStore{
2017-10-31 13:45:57 -07:00
db: db,
version: 0,
storeLoaders: make(map[string]CommitterLoader),
substores: make(map[string]Committer),
}
}
2017-11-29 07:57:47 -08:00
func (rs *rootMultiStore) SetCommitterLoader(name string, loader CommitterLoader) {
2017-10-31 13:45:57 -07:00
if _, ok := rs.storeLoaders[name]; ok {
2017-11-29 07:57:47 -08:00
panic(fmt.Sprintf("rootMultiStore duplicate substore name " + name))
2017-10-31 13:45:57 -07:00
}
rs.storeLoaders[name] = loader
}
//----------------------------------------
2017-11-29 07:57:47 -08:00
// rootMultiStore state
2017-10-31 13:45:57 -07:00
type msState struct {
Substores []substore
}
2017-11-29 07:57:47 -08:00
func (ms *msState) Sort() {
ms.Substores.Sort()
2017-10-31 13:45:57 -07:00
}
2017-11-29 07:57:47 -08:00
func (ms *msState) Hash() []byte {
m := make(map[string]interface{}, len(ms.Substores))
for _, substore := range ms.Substores {
m[substore.name] = substore.subState
2017-10-31 13:45:57 -07:00
}
return merkle.SimpleHashFromMap(m)
}
//----------------------------------------
// substore state
type substore struct {
name string
2017-11-29 07:57:47 -08:00
subState
2017-10-31 13:45:57 -07:00
}
// This gets serialized by go-wire
2017-11-29 07:57:47 -08:00
type subState struct {
2017-10-31 13:45:57 -07:00
CommitID CommitID
// ... maybe add more state
}
2017-11-29 07:57:47 -08:00
func (ss subState) Hash() []byte {
2017-10-31 13:45:57 -07:00
ssBytes, _ := wire.Marshal(ss) // Does not error
hasher := ripemd160.New()
hasher.Write(ssBytes)
return hasher.Sum(nil)
}
//----------------------------------------
2017-11-29 07:57:47 -08:00
// Call once after all calls to SetCommitterLoader are complete.
func (rs *rootMultiStore) LoadLatestVersion() error {
2017-10-31 13:45:57 -07:00
ver := rs.getLatestVersion()
rs.LoadVersion(ver)
}
2017-11-29 07:57:47 -08:00
func (rs *rootMultiStore) getLatestVersion() int64 {
var latest int64
2017-10-31 13:45:57 -07:00
latestBytes := rs.db.Get(msLatestKey)
if latestBytes == nil {
return 0
}
err := wire.Unmarshal(latestBytes, &latest)
if err != nil {
panic(err)
}
return latest
}
2017-11-29 07:57:47 -08:00
// NOTE: Returns 0 unless LoadVersion() or LoadLatestVersion() is called.
func (rs *rootMultiStore) GetVersion() int64 {
return rs.version
}
func (rs *rootMultiStore) LoadVersion(ver int64) error {
2017-10-31 13:45:57 -07:00
// Special logic for version 0
if ver == 0 {
for name, storeLoader := range rs.storeLoaders {
store, err := storeLoader(CommitID{Version: 0})
if err != nil {
2017-11-29 07:57:47 -08:00
return fmt.Errorf("Failed to load rootMultiStore: %v", err)
2017-10-31 13:45:57 -07:00
}
rs.substores[name] = store
}
return nil
}
// Otherwise, version is 1 or greater
msStateKey := fmt.Sprintf(msStateKeyFmt, ver)
stateBytes := rs.db.Get(msStateKey, ver)
if bz == nil {
2017-11-29 07:57:47 -08:00
return fmt.Errorf("Failed to load rootMultiStore: no data")
2017-10-31 13:45:57 -07:00
}
var state msState
err := wire.Unmarshal(stateBytes, &state)
if err != nil {
2017-11-29 07:57:47 -08:00
return fmt.Errorf("Failed to load rootMultiStore: %v", err)
2017-10-31 13:45:57 -07:00
}
// Load each Substore
2017-11-29 07:57:47 -08:00
var newSubstores = make(map[string]Committer)
2017-10-31 13:45:57 -07:00
for _, store := range state.Substores {
name, commitID := store.Name, store.CommitID
storeLoader := rs.storeLoaders[name]
if storeLoader == nil {
2017-11-29 07:57:47 -08:00
return fmt.Errorf("Failed to loadrootMultiStore: CommitterLoader missing for %v", name)
2017-10-31 13:45:57 -07:00
}
store, err := storeLoader(commitID)
if err != nil {
2017-11-29 07:57:47 -08:00
return fmt.Errorf("Failed to load rootMultiStore: %v", err)
2017-10-31 13:45:57 -07:00
}
2017-11-29 07:57:47 -08:00
newSubstores[name] = store
2017-10-31 13:45:57 -07:00
}
2017-11-29 07:57:47 -08:00
// If any CommitterLoaders were not used, return error.
2017-10-31 13:45:57 -07:00
for name := range rs.storeLoaders {
if _, ok := rs.substores[name]; !ok {
2017-11-29 07:57:47 -08:00
return fmt.Errorf("Unused CommitterLoader: %v", name)
2017-10-31 13:45:57 -07:00
}
}
2017-11-29 07:57:47 -08:00
// Success.
rs.version = ver
rs.substores = newSubstores
2017-10-31 13:45:57 -07:00
return nil
}
// Implements Committer
2017-11-29 07:57:47 -08:00
func (rs *rootMultiStore) Commit() CommitID {
2017-10-31 13:45:57 -07:00
// Needs to be transactional
batch := rs.db.NewBatch()
// Save msState
var state msState
for name, store := range rs.substores {
commitID := store.Commit()
state.Substores = append(state.Substores,
2017-11-29 07:57:47 -08:00
subState{
2017-10-31 13:45:57 -07:00
Name: name,
CommitID: commitID,
},
)
}
state.Sort()
stateBytes, err := wire.Marshal(state)
if err != nil {
panic(err)
}
msStateKey := fmt.Sprintf(msStateKeyFmt, rs.version)
batch.Set(msStateKey, stateBytes)
// Save msLatest
latestBytes, _ := wire.Marshal(rs.version) // Does not error
batch.Set(msLatestKey, latestBytes)
batch.Write()
batch.version += 1
}
2017-11-29 07:57:47 -08:00
// Implements MultiStore
func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore {
2017-10-31 13:45:57 -07:00
return newCacheMultiStore(rs)
}
2017-11-29 07:57:47 -08:00
// Implements MultiStore
func (rs *rootMultiStore) GetCommitter(name string) Committer {
2017-10-31 13:45:57 -07:00
return rs.store[name]
}
2017-11-29 07:57:47 -08:00
// Implements MultiStore
func (rs *rootMultiStore) GetKVStore(name string) KVStore {
2017-10-31 13:45:57 -07:00
return rs.store[name].(KVStore)
}
2017-11-29 07:57:47 -08:00
// Implements MultiStore
func (rs *rootMultiStore) GetIterKVStore(name string) IterKVStore {
2017-10-31 13:45:57 -07:00
return rs.store[name].(IterKVStore)
}
//----------------------------------------
2017-11-29 07:57:47 -08:00
// subStates
2017-10-31 13:45:57 -07:00
2017-11-29 07:57:47 -08:00
type subStates []subState
2017-10-31 13:45:57 -07:00
2017-11-29 07:57:47 -08:00
func (ssz subStates) Len() int { return len(ssz) }
func (ssz subStates) Less(i, j int) bool { return ssz[i].Key < ssz[j].Key }
func (ssz subStates) Swap(i, j int) { ssz[i], ssz[j] = ssz[j], ssz[i] }
func (ssz subStates) Sort() { sort.Sort(ssz) }
2017-10-31 13:45:57 -07:00
2017-11-29 07:57:47 -08:00
func (ssz subStates) Hash() []byte {
2017-10-31 13:45:57 -07:00
hz := make([]merkle.Hashable, len(ssz))
for i, ss := range ssz {
hz[i] = ss
}
return merkle.SimpleHashFromHashables(hz)
}
//----------------------------------------
// cacheMultiStore
type cwWriter interface {
Write()
}
// cacheMultiStore holds many CacheWrap'd stores.
// Implements MultiStore.
type cacheMultiStore struct {
db dbm.DB
2017-11-29 07:57:47 -08:00
version int64
2017-10-31 13:45:57 -07:00
substores map[string]cwWriter
}
2017-11-29 07:57:47 -08:00
func newCacheMultiStore(rs *rootMultiStore) cacheMultiStore {
2017-10-31 13:45:57 -07:00
cms := cacheMultiStore{
db: db.CacheWrap(),
version: rs.version,
substores: make(map[string]cwwWriter), len(rs.substores),
}
for name, substore := range rs.substores {
cms.substores[name] = substore.CacheWrap().(cwWriter)
}
return cms
}
2017-11-29 07:57:47 -08:00
// Implements CacheMultiStore
2017-10-31 13:45:57 -07:00
func (cms cacheMultiStore) Write() {
cms.db.Write()
for substore := range rs.substores {
substore.(cwWriter).Write()
}
}
2017-11-29 07:57:47 -08:00
// Implements CacheMultiStore
func (rs cacheMultiStore) CacheMultiStore() CacheMultiStore {
return newCacheMultiStore(rs)
}
// Implements CacheMultiStore
func (rs cacheMultiStore) GetCommitter(name string) Committer {
return rs.store[name]
}
// Implements CacheMultiStore
func (rs cacheMultiStore) GetKVStore(name string) KVStore {
return rs.store[name].(KVStore)
}
// Implements CacheMultiStore
func (rs cacheMultiStore) GetIterKVStore(name string) IterKVStore {
return rs.store[name].(IterKVStore)
}