cosmos-sdk/store/v2/smt/store.go

144 lines
3.4 KiB
Go

package smt
import (
"crypto/sha256"
"errors"
dbm "github.com/cosmos/cosmos-sdk/db"
"github.com/cosmos/cosmos-sdk/db/prefix"
"github.com/cosmos/cosmos-sdk/store/types"
ics23 "github.com/confio/ics23/go"
"github.com/lazyledger/smt"
tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto"
)
var (
_ types.BasicKVStore = (*Store)(nil)
_ smt.MapStore = (dbMapStore{})
)
var (
nodesPrefix = []byte{0}
valuesPrefix = []byte{1}
preimagesPrefix = []byte{2}
errKeyEmpty = errors.New("key is empty or nil")
errValueNil = errors.New("value is nil")
)
// Store Implements types.KVStore and CommitKVStore.
type Store struct {
tree *smt.SparseMerkleTree
values dbm.DBReadWriter
// Map hashed keys back to preimage
preimages dbm.DBReadWriter
}
// An smt.MapStore that wraps Get to raise smt.InvalidKeyError;
// smt.SparseMerkleTree expects this error to be returned when a key is not found
type dbMapStore struct{ dbm.DBReadWriter }
func NewStore(db dbm.DBReadWriter) *Store {
nodes := prefix.NewPrefixReadWriter(db, nodesPrefix)
values := prefix.NewPrefixReadWriter(db, valuesPrefix)
preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix)
return &Store{
tree: smt.NewSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New()),
values: values,
preimages: preimages,
}
}
func LoadStore(db dbm.DBReadWriter, root []byte) *Store {
nodes := prefix.NewPrefixReadWriter(db, nodesPrefix)
values := prefix.NewPrefixReadWriter(db, valuesPrefix)
preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix)
return &Store{
tree: smt.ImportSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New(), root),
values: values,
preimages: preimages,
}
}
func (s *Store) GetProof(key []byte) (*tmcrypto.ProofOps, error) {
if len(key) == 0 {
return nil, errKeyEmpty
}
proof, err := s.tree.Prove(key)
if err != nil {
return nil, err
}
op := NewProofOp(s.tree.Root(), key, SHA256, proof)
return &tmcrypto.ProofOps{Ops: []tmcrypto.ProofOp{op.ProofOp()}}, nil
}
func (s *Store) GetProofICS23(key []byte) (*ics23.CommitmentProof, error) {
return createIcs23Proof(s, key)
}
func (s *Store) Root() []byte { return s.tree.Root() }
// BasicKVStore interface below:
// Get returns nil iff key doesn't exist. Panics on nil or empty key.
func (s *Store) Get(key []byte) []byte {
if len(key) == 0 {
panic(errKeyEmpty)
}
val, err := s.tree.Get(key)
if err != nil {
panic(err)
}
return val
}
// Has checks if a key exists. Panics on nil or empty key.
func (s *Store) Has(key []byte) bool {
if len(key) == 0 {
panic(errKeyEmpty)
}
has, err := s.tree.Has(key)
if err != nil {
panic(err)
}
return has
}
// Set sets the key. Panics on nil key or value.
func (s *Store) Set(key []byte, value []byte) {
if len(key) == 0 {
panic(errKeyEmpty)
}
if value == nil {
panic(errValueNil)
}
_, err := s.tree.Update(key, value)
if err != nil {
panic(err)
}
path := sha256.Sum256(key)
s.preimages.Set(path[:], key)
}
// Delete deletes the key. Panics on nil key.
func (s *Store) Delete(key []byte) {
if len(key) == 0 {
panic(errKeyEmpty)
}
_, _ = s.tree.Delete(key)
path := sha256.Sum256(key)
s.preimages.Delete(path[:])
}
func (ms dbMapStore) Get(key []byte) ([]byte, error) {
val, err := ms.DBReadWriter.Get(key)
if err != nil {
return nil, err
}
if val == nil {
return nil, &smt.InvalidKeyError{key}
}
return val, nil
}