Fix app state to commit properly
This commit is contained in:
parent
6b96fa4554
commit
ea8bdd3f92
|
@ -129,7 +129,6 @@ func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
||||||
res, err := app.handler.DeliverTx(ctx, app.state.Append(), tx)
|
res, err := app.handler.DeliverTx(ctx, app.state.Append(), tx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// discard the cache...
|
|
||||||
return errors.Result(err)
|
return errors.Result(err)
|
||||||
}
|
}
|
||||||
return res.ToABCI()
|
return res.ToABCI()
|
||||||
|
|
|
@ -7,12 +7,28 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
"github.com/tendermint/basecoin/state"
|
"github.com/tendermint/basecoin/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func makeState() state.SimpleDB {
|
||||||
|
// return state.NewMemKVStore()
|
||||||
|
|
||||||
|
return state.NewBonsai(iavl.NewIAVLTree(0, nil))
|
||||||
|
|
||||||
|
// tree with persistence....
|
||||||
|
// tmpDir, err := ioutil.TempDir("", "state-tests")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// db := dbm.NewDB("test-get-dbs", dbm.LevelDBBackendStr, tmpDir)
|
||||||
|
// persist := iavl.NewIAVLTree(500, db)
|
||||||
|
// return state.NewBonsai(persist)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCheckpointer(t *testing.T) {
|
func TestCheckpointer(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
|
@ -69,7 +85,7 @@ func TestCheckpointer(t *testing.T) {
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
ctx := NewContext("foo", 100, log.NewNopLogger())
|
ctx := NewContext("foo", 100, log.NewNopLogger())
|
||||||
|
|
||||||
store := state.NewMemKVStore()
|
store := makeState()
|
||||||
_, err := app.CheckTx(ctx, store, tc.tx)
|
_, err := app.CheckTx(ctx, store, tc.tx)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
@ -81,7 +97,7 @@ func TestCheckpointer(t *testing.T) {
|
||||||
assert.EqualValues(m.Value, val, "%d: %#v", i, m)
|
assert.EqualValues(m.Value, val, "%d: %#v", i, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
store = state.NewMemKVStore()
|
store = makeState()
|
||||||
_, err = app.DeliverTx(ctx, store, tc.tx)
|
_, err = app.DeliverTx(ctx, store, tc.tx)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/merkle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// store nonce as it's own type so no one can even try to fake it
|
||||||
|
type nonce int64
|
||||||
|
|
||||||
|
// Bonsai is a deformed tree forced to fit in a small pot
|
||||||
|
type Bonsai struct {
|
||||||
|
id nonce
|
||||||
|
merkle.Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SimpleDB = &Bonsai{}
|
||||||
|
|
||||||
|
// NewBonsai wraps a merkle tree and tags it to track children
|
||||||
|
func NewBonsai(tree merkle.Tree) *Bonsai {
|
||||||
|
return &Bonsai{
|
||||||
|
id: nonce(rand.Int63()),
|
||||||
|
Tree: tree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get matches the signature of KVStore
|
||||||
|
func (b *Bonsai) Get(key []byte) []byte {
|
||||||
|
_, value, _ := b.Tree.Get(key)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set matches the signature of KVStore
|
||||||
|
func (b *Bonsai) Set(key, value []byte) {
|
||||||
|
b.Tree.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) Remove(key []byte) (value []byte) {
|
||||||
|
value, _ = b.Tree.Remove(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) List(start, end []byte, limit int) []Model {
|
||||||
|
res := []Model{}
|
||||||
|
stopAtCount := func(key []byte, value []byte) (stop bool) {
|
||||||
|
m := Model{key, value}
|
||||||
|
res = append(res, m)
|
||||||
|
// return false
|
||||||
|
return limit > 0 && len(res) >= limit
|
||||||
|
}
|
||||||
|
b.Tree.IterateRange(start, end, true, stopAtCount)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) First(start, end []byte) Model {
|
||||||
|
var m Model
|
||||||
|
stopAtFirst := func(key []byte, value []byte) (stop bool) {
|
||||||
|
m = Model{key, value}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
b.Tree.IterateRange(start, end, true, stopAtFirst)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) Last(start, end []byte) Model {
|
||||||
|
var m Model
|
||||||
|
stopAtFirst := func(key []byte, value []byte) (stop bool) {
|
||||||
|
m = Model{key, value}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
b.Tree.IterateRange(start, end, false, stopAtFirst)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) Checkpoint() SimpleDB {
|
||||||
|
return &Bonsai{
|
||||||
|
id: b.id,
|
||||||
|
Tree: b.Tree.Copy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit will take all changes from the checkpoint and write
|
||||||
|
// them to the parent.
|
||||||
|
// Returns an error if this is not a child of this one
|
||||||
|
func (b *Bonsai) Commit(sub SimpleDB) error {
|
||||||
|
bb, ok := sub.(*Bonsai)
|
||||||
|
if !ok || (b.id != bb.id) {
|
||||||
|
return errors.New("Not a sub-transaction")
|
||||||
|
}
|
||||||
|
b.Tree = bb.Tree
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard will remove reference to this
|
||||||
|
func (b *Bonsai) Discard() {
|
||||||
|
b.id = 0
|
||||||
|
b.Tree = nil
|
||||||
|
}
|
129
state/merkle.go
129
state/merkle.go
|
@ -1,9 +1,6 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/tendermint/merkleeyes/iavl"
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
"github.com/tendermint/tmlibs/merkle"
|
"github.com/tendermint/tmlibs/merkle"
|
||||||
)
|
)
|
||||||
|
@ -11,31 +8,32 @@ import (
|
||||||
// State represents the app states, separating the commited state (for queries)
|
// State represents the app states, separating the commited state (for queries)
|
||||||
// from the working state (for CheckTx and AppendTx)
|
// from the working state (for CheckTx and AppendTx)
|
||||||
type State struct {
|
type State struct {
|
||||||
committed merkle.Tree
|
committed *Bonsai
|
||||||
deliverTx merkle.Tree
|
deliverTx *Bonsai
|
||||||
checkTx merkle.Tree
|
checkTx *Bonsai
|
||||||
persistent bool
|
persistent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewState(tree merkle.Tree, persistent bool) State {
|
func NewState(tree merkle.Tree, persistent bool) State {
|
||||||
|
base := NewBonsai(tree)
|
||||||
return State{
|
return State{
|
||||||
committed: tree,
|
committed: base,
|
||||||
deliverTx: tree.Copy(),
|
deliverTx: base.Checkpoint().(*Bonsai),
|
||||||
checkTx: tree.Copy(),
|
checkTx: base.Checkpoint().(*Bonsai),
|
||||||
persistent: persistent,
|
persistent: persistent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s State) Committed() *Bonsai {
|
func (s State) Committed() *Bonsai {
|
||||||
return NewBonsai(s.committed)
|
return s.committed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s State) Append() *Bonsai {
|
func (s State) Append() *Bonsai {
|
||||||
return NewBonsai(s.deliverTx)
|
return s.deliverTx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s State) Check() *Bonsai {
|
func (s State) Check() *Bonsai {
|
||||||
return NewBonsai(s.checkTx)
|
return s.checkTx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash updates the tree
|
// Hash updates the tree
|
||||||
|
@ -47,7 +45,7 @@ func (s *State) Hash() []byte {
|
||||||
func (s *State) BatchSet(key, value []byte) {
|
func (s *State) BatchSet(key, value []byte) {
|
||||||
if s.persistent {
|
if s.persistent {
|
||||||
// This is in the batch with the Save, but not in the tree
|
// This is in the batch with the Save, but not in the tree
|
||||||
tree, ok := s.deliverTx.(*iavl.IAVLTree)
|
tree, ok := s.deliverTx.Tree.(*iavl.IAVLTree)
|
||||||
if ok {
|
if ok {
|
||||||
tree.BatchSet(key, value)
|
tree.BatchSet(key, value)
|
||||||
}
|
}
|
||||||
|
@ -56,106 +54,19 @@ func (s *State) BatchSet(key, value []byte) {
|
||||||
|
|
||||||
// Commit save persistent nodes to the database and re-copies the trees
|
// Commit save persistent nodes to the database and re-copies the trees
|
||||||
func (s *State) Commit() []byte {
|
func (s *State) Commit() []byte {
|
||||||
|
err := s.committed.Commit(s.deliverTx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // ugh, TODO?
|
||||||
|
}
|
||||||
|
|
||||||
var hash []byte
|
var hash []byte
|
||||||
if s.persistent {
|
if s.persistent {
|
||||||
hash = s.deliverTx.Save()
|
hash = s.committed.Tree.Save()
|
||||||
} else {
|
} else {
|
||||||
hash = s.deliverTx.Hash()
|
hash = s.committed.Tree.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
s.committed = s.deliverTx
|
s.deliverTx = s.committed.Checkpoint().(*Bonsai)
|
||||||
s.deliverTx = s.committed.Copy()
|
s.checkTx = s.committed.Checkpoint().(*Bonsai)
|
||||||
s.checkTx = s.committed.Copy()
|
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// store nonce as it's own type so no one can even try to fake it
|
|
||||||
type nonce int64
|
|
||||||
|
|
||||||
// Bonsai is a deformed tree forced to fit in a small pot
|
|
||||||
type Bonsai struct {
|
|
||||||
id nonce
|
|
||||||
merkle.Tree
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ SimpleDB = &Bonsai{}
|
|
||||||
|
|
||||||
func NewBonsai(tree merkle.Tree) *Bonsai {
|
|
||||||
return &Bonsai{
|
|
||||||
id: nonce(rand.Int63()),
|
|
||||||
Tree: tree,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get matches the signature of KVStore
|
|
||||||
func (b *Bonsai) Get(key []byte) []byte {
|
|
||||||
_, value, _ := b.Tree.Get(key)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set matches the signature of KVStore
|
|
||||||
func (b *Bonsai) Set(key, value []byte) {
|
|
||||||
b.Tree.Set(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bonsai) Remove(key []byte) (value []byte) {
|
|
||||||
value, _ = b.Tree.Remove(key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bonsai) List(start, end []byte, limit int) []Model {
|
|
||||||
res := []Model{}
|
|
||||||
stopAtCount := func(key []byte, value []byte) (stop bool) {
|
|
||||||
m := Model{key, value}
|
|
||||||
res = append(res, m)
|
|
||||||
// return false
|
|
||||||
return limit > 0 && len(res) >= limit
|
|
||||||
}
|
|
||||||
b.Tree.IterateRange(start, end, true, stopAtCount)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bonsai) First(start, end []byte) Model {
|
|
||||||
var m Model
|
|
||||||
stopAtFirst := func(key []byte, value []byte) (stop bool) {
|
|
||||||
m = Model{key, value}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
b.Tree.IterateRange(start, end, true, stopAtFirst)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bonsai) Last(start, end []byte) Model {
|
|
||||||
var m Model
|
|
||||||
stopAtFirst := func(key []byte, value []byte) (stop bool) {
|
|
||||||
m = Model{key, value}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
b.Tree.IterateRange(start, end, false, stopAtFirst)
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bonsai) Checkpoint() SimpleDB {
|
|
||||||
return &Bonsai{
|
|
||||||
id: b.id,
|
|
||||||
Tree: b.Tree.Copy(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit will take all changes from the checkpoint and write
|
|
||||||
// them to the parent.
|
|
||||||
// Returns an error if this is not a child of this one
|
|
||||||
func (b *Bonsai) Commit(sub SimpleDB) error {
|
|
||||||
bb, ok := sub.(*Bonsai)
|
|
||||||
if !ok || (b.id != bb.id) {
|
|
||||||
return errors.New("Not a sub-transaction")
|
|
||||||
}
|
|
||||||
b.Tree = bb.Tree
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard will remove reference to this
|
|
||||||
func (b *Bonsai) Discard() {
|
|
||||||
b.id = 0
|
|
||||||
b.Tree = nil
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue