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)
|
|
|
|
}
|