cosmos-sdk/store/rootmultistore.go

306 lines
7.3 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-19 21:23:51 -08:00
nextVersion int64
2017-12-03 23:17:10 -08:00
lastCommitID CommitID
2018-01-12 13:48:54 -08:00
storeLoaders map[SubstoreKey]CommitStoreLoader
substores map[SubstoreKey]CommitStore
2017-10-31 13:45:57 -07:00
}
2017-12-26 17:04:48 -08:00
var _ CommitMultiStore = (*rootMultiStore)(nil)
2018-01-03 17:20:21 -08:00
func NewCommitMultiStore(db dbm.DB) *rootMultiStore {
2017-11-29 07:57:47 -08:00
return &rootMultiStore{
2017-10-31 13:45:57 -07:00
db: db,
2017-12-19 21:23:51 -08:00
nextVersion: 0,
2018-01-12 13:48:54 -08:00
storeLoaders: make(map[SubstoreKey]CommitStoreLoader),
substores: make(map[SubstoreKey]CommitStore),
2017-10-31 13:45:57 -07:00
}
}
2017-12-26 17:04:48 -08:00
// Implements CommitMultiStore.
2018-01-12 13:48:54 -08:00
func (rs *rootMultiStore) SetSubstoreLoader(key SubstoreKey, loader CommitStoreLoader) {
if _, ok := rs.storeLoaders[key]; ok {
panic(fmt.Sprintf("rootMultiStore duplicate substore key", key))
2017-10-31 13:45:57 -07:00
}
2018-01-12 13:48:54 -08:00
rs.storeLoaders[key] = loader
2017-10-31 13:45:57 -07:00
}
2017-12-26 17:04:48 -08:00
// Implements CommitMultiStore.
2018-01-12 13:48:54 -08:00
func (rs *rootMultiStore) GetSubstore(key SubstoreKey) CommitStore {
return rs.substores[key]
2017-12-26 17:04:48 -08:00
}
// Implements CommitMultiStore.
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-12-09 12:35:51 -08:00
return rs.LoadVersion(ver)
2017-10-31 13:45:57 -07:00
}
2017-12-26 17:04:48 -08:00
// Implements CommitMultiStore.
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 {
2018-01-12 13:48:54 -08:00
for key, 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
}
2018-01-12 13:48:54 -08:00
rs.substores[key] = store
2017-10-31 13:45:57 -07:00
}
2017-12-19 21:23:51 -08:00
rs.nextVersion = 1
rs.lastCommitID = CommitID{}
2017-10-31 13:45:57 -07:00
return nil
}
// Otherwise, version is 1 or greater
// Get commitState
state, err := getCommitState(rs.db, ver)
2017-12-09 12:35:51 -08:00
if err != nil {
return err
}
2017-10-31 13:45:57 -07:00
// Load each Substore
2018-01-12 13:48:54 -08:00
var newSubstores = make(map[SubstoreKey]CommitStore)
2017-10-31 13:45:57 -07:00
for _, store := range state.Substores {
2018-01-12 13:48:54 -08:00
key, commitID := rs.nameToKey(store.Name), store.CommitID
storeLoader := rs.storeLoaders[key]
2017-10-31 13:45:57 -07:00
if storeLoader == nil {
2018-01-12 13:48:54 -08:00
return fmt.Errorf("Failed to load rootMultiStore substore %v for commitID %v: %v", key, commitID, err)
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
}
2018-01-12 13:48:54 -08:00
newSubstores[key] = 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.
2018-01-12 13:48:54 -08:00
for key := range rs.storeLoaders {
if _, ok := newSubstores[key]; !ok {
return fmt.Errorf("Unused CommitStoreLoader: %v", key)
2017-10-31 13:45:57 -07:00
}
}
2017-11-29 07:57:47 -08:00
// Success.
2017-12-19 21:23:51 -08:00
rs.nextVersion = 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
}
2018-01-12 13:48:54 -08:00
func (rs *rootMultiStore) nameToKey(name string) SubstoreKey {
for key, _ := range rs.substores {
if key.Name() == name {
return key
}
}
panic("Unknown name " + name)
}
2017-12-26 17:04:48 -08:00
//----------------------------------------
// +CommitStore
// Implements CommitStore.
2017-12-01 08:52:54 -08:00
func (rs *rootMultiStore) Commit() CommitID {
2017-12-26 17:04:48 -08:00
// Commit substores.
2017-12-19 21:23:51 -08:00
version := rs.nextVersion
state := commitSubstores(version, rs.substores)
2017-12-01 08:52:54 -08:00
// Need to update self state atomically.
2017-12-01 08:52:54 -08:00
batch := rs.db.NewBatch()
setCommitState(batch, version, state)
setLatestVersion(batch, version)
2017-10-31 13:45:57 -07:00
batch.Write()
// Prepare for next version.
2017-12-19 21:23:51 -08:00
rs.nextVersion = 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-26 17:04:48 -08:00
// Implements CommitStore.
2017-12-04 00:56:25 -08:00
func (rs *rootMultiStore) CacheWrap() CacheWrap {
return rs.CacheMultiStore().(CacheWrap)
2017-12-01 08:52:54 -08:00
}
2017-12-26 17:04:48 -08:00
//----------------------------------------
// +MultiStore
// Implements MultiStore.
2017-12-01 08:52:54 -08:00
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-12-26 17:04:48 -08:00
// Implements MultiStore.
// NOTE: Returns 0 unless LoadVersion() or LoadLatestVersion() is called.
func (rs *rootMultiStore) NextVersion() int64 {
return rs.nextVersion
}
// Implements MultiStore.
2017-11-29 07:57:47 -08:00
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-12-26 17:04:48 -08:00
// Implements MultiStore.
2018-01-12 13:48:54 -08:00
func (rs *rootMultiStore) GetStore(key SubstoreKey) interface{} {
return rs.substores[key]
2017-10-31 13:45:57 -07:00
}
2017-12-26 17:04:48 -08:00
// Implements MultiStore.
2018-01-12 13:48:54 -08:00
func (rs *rootMultiStore) GetKVStore(key SubstoreKey) KVStore {
return rs.substores[key].(KVStore)
2017-10-31 13:45:57 -07:00
}
//----------------------------------------
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
}
// Hash returns the simple merkle root hash of the substores sorted by name.
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
// substore contains the name and core reference for an underlying store.
// It is the leaf of the rootMultiStores top level simple merkle tree.
2017-12-01 08:52:54 -08:00
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
}
// Hash returns the RIPEMD160 of the wire-encoded substore.
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
//----------------------------------------
2017-12-26 17:04:48 -08:00
// Misc.
2017-12-01 08:52:54 -08:00
func getLatestVersion(db dbm.DB) int64 {
var latest int64
2017-12-09 12:35:51 -08:00
latestBytes := db.Get([]byte(latestVersionKey))
2017-12-01 08:52:54 -08:00
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
}
// Set the latest version.
func setLatestVersion(batch dbm.Batch, version int64) {
latestBytes, _ := wire.Marshal(version) // Does not error
batch.Set([]byte(latestVersionKey), latestBytes)
}
// Commits each substore and returns a new commitState.
2018-01-12 13:48:54 -08:00
func commitSubstores(version int64, substoresMap map[SubstoreKey]CommitStore) commitState {
substores := make([]substore, 0, len(substoresMap))
2018-01-12 13:48:54 -08:00
for key, store := range substoresMap {
// Commit
commitID := store.Commit()
// Record CommitID
substore := substore{}
2018-01-12 13:48:54 -08:00
substore.Name = key.Name()
substore.CommitID = commitID
substores = append(substores, substore)
}
return commitState{
Version: version,
Substores: substores,
}
}
// Gets commitState from disk.
func getCommitState(db dbm.DB, ver int64) (commitState, error) {
// Get from DB.
commitStateKey := fmt.Sprintf(commitStateKeyFmt, ver)
stateBytes := db.Get([]byte(commitStateKey))
if stateBytes == nil {
return commitState{}, fmt.Errorf("Failed to get rootMultiStore: no data")
}
// Parse bytes.
var state commitState
err := wire.Unmarshal(stateBytes, &state)
if err != nil {
return commitState{}, fmt.Errorf("Failed to get rootMultiStore: %v", err)
}
return state, nil
}
// Set a commit state for given version.
func setCommitState(batch dbm.Batch, version int64, state commitState) {
stateBytes, err := wire.Marshal(state)
if err != nil {
panic(err)
}
commitStateKey := fmt.Sprintf(commitStateKeyFmt, version)
batch.Set([]byte(commitStateKey), stateBytes)
}