cosmos-sdk/store/rootmultistore.go

277 lines
6.2 KiB
Go
Raw Normal View History

2017-10-31 13:45:57 -07:00
package store
import (
"fmt"
"github.com/tendermint/go-wire"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/merkle"
"golang.org/x/crypto/ripemd160"
)
const (
2017-12-01 08:52:54 -08:00
latestVersionKey = "s/latest"
commitStateKeyFmt = "s/%d" // s/<version>
2017-10-31 13:45:57 -07:00
)
2017-12-03 22:55:15 -08:00
// rootMultiStore is composed of many CommitStores.
2017-12-01 08:52:54 -08:00
// Name contrasts with cacheMultiStore which is for cache-wrapping
// other MultiStores.
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-12-01 08:52:54 -08:00
curVersion int64
2017-12-03 23:17:10 -08:00
lastCommitID CommitID
2017-12-03 22:55:15 -08:00
storeLoaders map[string]CommitStoreLoader
substores map[string]CommitStore
2017-10-31 13:45:57 -07:00
}
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,
2017-12-01 08:52:54 -08:00
curVersion: 0,
2017-12-03 22:55:15 -08:00
storeLoaders: make(map[string]CommitStoreLoader),
substores: make(map[string]CommitStore),
2017-10-31 13:45:57 -07:00
}
}
2017-12-03 22:55:15 -08:00
func (rs *rootMultiStore) SetCommitStoreLoader(name string, loader CommitStoreLoader) {
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-12-03 22:55:15 -08:00
// Call once after all calls to SetCommitStoreLoader are complete.
2017-11-29 07:57:47 -08:00
func (rs *rootMultiStore) LoadLatestVersion() error {
2017-12-01 08:52:54 -08:00
ver := getLatestVersion(rs.db)
2017-10-31 13:45:57 -07:00
rs.LoadVersion(ver)
}
2017-11-29 07:57:47 -08:00
// NOTE: Returns 0 unless LoadVersion() or LoadLatestVersion() is called.
2017-12-01 08:52:54 -08:00
func (rs *rootMultiStore) GetCurrentVersion() int64 {
return rs.curVersion
2017-11-29 07:57:47 -08:00
}
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 {
2017-12-01 08:52:54 -08:00
store, err := storeLoader(CommitID{})
2017-10-31 13:45:57 -07:00
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-12-01 08:52:54 -08:00
rs.curVersion = 1
2017-12-03 23:17:10 -08:00
rs.lastCommitID = CommitID{}
2017-10-31 13:45:57 -07:00
rs.substores[name] = store
}
return nil
}
// Otherwise, version is 1 or greater
2017-12-01 08:52:54 -08:00
// Load commitState
var state commitState = loadCommitState(rs.db, ver)
2017-10-31 13:45:57 -07:00
// Load each Substore
2017-12-03 22:55:15 -08:00
var newSubstores = make(map[string]CommitStore)
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-12-03 22:55:15 -08:00
return fmt.Errorf("Failed to loadrootMultiStore: CommitStoreLoader 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-12-03 22:55:15 -08:00
// If any CommitStoreLoaders 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-12-03 22:55:15 -08:00
return fmt.Errorf("Unused CommitStoreLoader: %v", name)
2017-10-31 13:45:57 -07:00
}
}
2017-11-29 07:57:47 -08:00
// Success.
2017-12-01 08:52:54 -08:00
rs.curVersion = ver + 1
2017-12-03 23:17:10 -08:00
rs.lastCommitID = state.CommitID()
2017-11-29 07:57:47 -08:00
rs.substores = newSubstores
2017-10-31 13:45:57 -07:00
return nil
}
2017-12-01 08:52:54 -08:00
// Commits each substore and gets commitState.
2017-12-03 15:01:17 -08:00
func (rs *rootMultiStore) doCommit() commitState {
2017-12-01 08:52:54 -08:00
version := rs.curVersion
substores := make([]substore, len(rs.substores))
2017-10-31 13:45:57 -07:00
for name, store := range rs.substores {
2017-12-01 08:52:54 -08:00
// Commit
2017-10-31 13:45:57 -07:00
commitID := store.Commit()
2017-12-01 08:52:54 -08:00
// Record CommitID
substores = append(substores,
substore{
2017-10-31 13:45:57 -07:00
Name: name,
CommitID: commitID,
},
)
}
2017-12-01 08:52:54 -08:00
// Incr curVersion
rs.curVersion += 1
return commitState{
Version: version,
Substores: substores,
}
}
//----------------------------------------
2017-12-03 22:55:15 -08:00
// Implements CommitStore
2017-12-01 08:52:54 -08:00
func (rs *rootMultiStore) Commit() CommitID {
version := rs.version
// Needs to be transactional
batch := rs.db.NewBatch()
// Commit each substore and get commitState
state := rs.doCommit()
2017-10-31 13:45:57 -07:00
stateBytes, err := wire.Marshal(state)
if err != nil {
panic(err)
}
2017-12-01 08:52:54 -08:00
commitStateKey := fmt.Sprintf(commitStateKeyFmt, rs.version)
batch.Set(commitStateKey, stateBytes)
2017-10-31 13:45:57 -07:00
2017-12-01 08:52:54 -08:00
// Save the latest version
2017-10-31 13:45:57 -07:00
latestBytes, _ := wire.Marshal(rs.version) // Does not error
2017-12-01 08:52:54 -08:00
batch.Set(latestVersionKey, latestBytes)
2017-10-31 13:45:57 -07:00
batch.Write()
2017-12-01 08:52:54 -08:00
rs.version += 1
2017-12-03 23:17:10 -08:00
commitID := CommitID{
2017-12-01 08:52:54 -08:00
Version: version,
Hash: state.Hash(),
}
2017-12-03 23:17:10 -08:00
rs.lastCommitID = commitID
return commitID
2017-12-03 22:55:15 -08:00
}
2017-12-01 08:52:54 -08:00
2017-12-03 22:55:15 -08:00
// Implements CommitStore
2017-12-04 00:56:25 -08:00
func (rs *rootMultiStore) CacheWrap() CacheWrap {
2017-12-03 22:55:15 -08:00
return rs.CacheMultiStore()
2017-12-01 08:52:54 -08:00
}
// Get the last committed CommitID
func (rs *rootMultiStore) LastCommitID() CommitID {
2017-12-03 23:17:10 -08:00
return rs.lastCommitID
2017-10-31 13:45:57 -07:00
}
2017-11-29 07:57:47 -08:00
// Implements MultiStore
func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore {
2017-12-04 00:23:10 -08:00
return newCacheMultiStoreFromRMS(rs)
2017-10-31 13:45:57 -07:00
}
2017-11-29 07:57:47 -08:00
// Implements MultiStore
2017-12-03 22:55:15 -08:00
func (rs *rootMultiStore) GetCommitStore(name string) CommitStore {
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-12-01 08:52:54 -08:00
// commitState
2017-10-31 13:45:57 -07:00
2017-12-01 08:52:54 -08:00
// NOTE: Keep commitState a simple immutable struct.
type commitState struct {
2017-10-31 13:45:57 -07:00
2017-12-01 08:52:54 -08:00
// Version
Version int64
2017-10-31 13:45:57 -07:00
2017-12-01 08:52:54 -08:00
// Substore info for
Substores []substore
2017-10-31 13:45:57 -07:00
}
2017-12-01 08:52:54 -08:00
// loads commitState from disk.
func loadCommitState(db dbm.DB, ver int64) (commitState, error) {
2017-10-31 13:45:57 -07:00
2017-12-01 08:52:54 -08:00
// Load from DB.
commitStateKey := fmt.Sprintf(commitStateKeyFmt, ver)
stateBytes := db.Get(commitStateKey, ver)
if bz == nil {
return commitState{}, fmt.Errorf("Failed to load rootMultiStore: no data")
}
2017-10-31 13:45:57 -07:00
2017-12-01 08:52:54 -08:00
// Parse bytes.
var state commitState
err := wire.Unmarshal(stateBytes, &state)
if err != nil {
return commitState{}, fmt.Errorf("Failed to load rootMultiStore: %v", err)
}
return state, nil
2017-10-31 13:45:57 -07:00
}
2017-12-01 08:52:54 -08:00
func (cs commitState) Hash() []byte {
// TODO cache to cs.hash []byte
m := make(map[string]interface{}, len(cs.Substores))
for _, substore := range cs.Substores {
m[substore.Name] = substore
2017-10-31 13:45:57 -07:00
}
2017-12-01 08:52:54 -08:00
return merkle.SimpleHashFromMap(m)
2017-10-31 13:45:57 -07:00
}
2017-12-01 08:52:54 -08:00
func (cs commitState) CommitID() CommitID {
return CommitID{
Version: cs.Version,
Hash: cs.Hash(),
2017-10-31 13:45:57 -07:00
}
}
2017-11-29 07:57:47 -08:00
2017-12-01 08:52:54 -08:00
//----------------------------------------
// substore state
type substore struct {
Name string
substoreCore
2017-11-29 07:57:47 -08:00
}
2017-12-01 08:52:54 -08:00
type substoreCore struct {
CommitID CommitID
// ... maybe add more state
2017-11-29 07:57:47 -08:00
}
2017-12-01 08:52:54 -08:00
func (sc substoreCore) Hash() []byte {
scBytes, _ := wire.Marshal(sc) // Does not error
hasher := ripemd160.New()
hasher.Write(scBytes)
return hasher.Sum(nil)
2017-11-29 07:57:47 -08:00
}
2017-12-01 08:52:54 -08:00
//----------------------------------------
func getLatestVersion(db dbm.DB) int64 {
var latest int64
latestBytes := db.Get(latestVersionKey)
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
}