Skip to content
This repository has been archived by the owner on Jul 15, 2018. It is now read-only.

merkleeyes to iavl #116

Merged
merged 9 commits into from
Oct 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ metalinter: tools
metalinter_test: tools
@gometalinter --install
gometalinter --vendor --deadline=600s --disable-all \
--enable=aligncheck \
--enable=maligned \
--enable=deadcode \
--enable=gas \
--enable=goconst \
--enable=goimports \
--enable=gosimple \
--enable=gotype \
--enable=ineffassign \
--enable=megacheck \
--enable=misspell \
Expand All @@ -71,12 +70,13 @@ metalinter_test: tools
--enable=vetshadow \
./...

#--enable=dupl \
#--enable=errcheck \
#--enable=gocyclo \
#--enable=golint \ <== comments on anything exported
#--enable=interfacer \
#--enable=unparam \
#--enable=vet \
#--enable=dupl \
#--enable=errcheck \
#--enable=gocyclo \
#--enable=golint \ <== comments on anything exported
#--enable=gotype \
#--enable=interfacer \
#--enable=unparam \
#--enable=vet \

.PHONY: all build test fmt get_deps tools
38 changes: 28 additions & 10 deletions example/dummy/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ import (
"strings"

"github.com/tendermint/abci/types"
"github.com/tendermint/merkleeyes/iavl"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/merkle"
dbm "github.com/tendermint/tmlibs/db"
)

type DummyApplication struct {
types.BaseApplication

state merkle.Tree
state *iavl.VersionedTree
}

func NewDummyApplication() *DummyApplication {
state := iavl.NewIAVLTree(0, nil)
state := iavl.NewVersionedTree(0, dbm.NewMemDB())
return &DummyApplication{state: state}
}

Expand All @@ -40,28 +41,45 @@ func (app *DummyApplication) CheckTx(tx []byte) types.Result {
}

func (app *DummyApplication) Commit() types.Result {
hash := app.state.Hash()
// Save a new version
var hash []byte
var err error

if app.state.Size() > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this condition? I mean, by the time we commit the size should always be > 0, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well. We have tests that commit a few empty blocks before adding a TX. They were returning errors here, which broke things. So I added tests. Thus the slack ping to alexis

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. For the persistent dummy I ended up adding the constraint that InitChain must be called, which will make the state non-zero. In general would be nice if we could still get versions of empty trees so we dont have to worry about this edge condition

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use !app.state.IsEmpty() now which is a bit cleaner, but if a tree has no root, what are we saving exactly? We could potentially have it just save a version number but hmmm..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just for useability. The tree is still something the app thinks about and has a root (even if empty). And for recover logic, we like the tree version to equal the height, even if the tree hasn't changed.

// just add one more to height (kind of arbitrarily stupid)
height := app.state.LatestVersion() + 1
hash, err = app.state.SaveVersion(height)
if err != nil {
// if this wasn't a dummy app, we'd do something smarter
panic(err)
}
}

return types.NewResultOK(hash, "")
}

func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
if reqQuery.Prove {
value, proof, exists := app.state.Proof(reqQuery.Data)
value, proof, err := app.state.GetWithProof(reqQuery.Data)
// if this wasn't a dummy app, we'd do something smarter
if err != nil {
panic(err)
}
resQuery.Index = -1 // TODO make Proof return index
resQuery.Key = reqQuery.Data
resQuery.Value = value
resQuery.Proof = proof
if exists {
resQuery.Proof = wire.BinaryBytes(proof)
if value != nil {
resQuery.Log = "exists"
} else {
resQuery.Log = "does not exist"
}
return
} else {
index, value, exists := app.state.Get(reqQuery.Data)
index, value := app.state.Get(reqQuery.Data)
resQuery.Index = int64(index)
resQuery.Value = value
if exists {
if value != nil {
resQuery.Log = "exists"
} else {
resQuery.Log = "does not exist"
Expand Down
21 changes: 9 additions & 12 deletions example/dummy/dummy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import (
abcicli "github.com/tendermint/abci/client"
"github.com/tendermint/abci/server"
"github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
)
Expand Down Expand Up @@ -39,9 +38,10 @@ func testDummy(t *testing.T, app types.Application, tx []byte, key, value string
})
require.Equal(t, types.CodeType_OK, resQuery.Code)
require.Equal(t, value, string(resQuery.Value))
proof, err := iavl.ReadProof(resQuery.Proof)
proof, err := iavl.ReadKeyExistsProof(resQuery.Proof)
require.Nil(t, err)
require.True(t, proof.Verify([]byte(key), resQuery.Value, proof.RootHash)) // NOTE: we have no way to verify the RootHash
err = proof.Verify([]byte(key), resQuery.Value, proof.RootHash)
require.Nil(t, err, "%+v", err) // NOTE: we have no way to verify the RootHash
}

func TestDummyKV(t *testing.T) {
Expand Down Expand Up @@ -78,6 +78,7 @@ func TestPersistentDummyInfo(t *testing.T) {
t.Fatal(err)
}
dummy := NewPersistentDummyApplication(dir)
InitDummy(dummy)
height := uint64(0)

resInfo := dummy.Info(types.RequestInfo{})
Expand Down Expand Up @@ -113,12 +114,7 @@ func TestValSetChanges(t *testing.T) {
// init with some validators
total := 10
nInit := 5
vals := make([]*types.Validator, total)
for i := 0; i < total; i++ {
pubkey := crypto.GenPrivKeyEd25519FromSecret([]byte(cmn.Fmt("test%d", i))).PubKey().Bytes()
power := cmn.RandInt()
vals[i] = &types.Validator{pubkey, uint64(power)}
}
vals := RandVals(total)
// iniitalize with the first nInit
dummy.InitChain(types.RequestInitChain{vals[:nInit]})

Expand Down Expand Up @@ -309,7 +305,8 @@ func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string)
require.Nil(t, err)
require.Equal(t, types.CodeType_OK, resQuery.Code)
require.Equal(t, value, string(resQuery.Value))
proof, err := iavl.ReadProof(resQuery.Proof)
proof, err := iavl.ReadKeyExistsProof(resQuery.Proof)
require.Nil(t, err)
require.True(t, proof.Verify([]byte(key), resQuery.Value, proof.RootHash)) // NOTE: we have no way to verify the RootHash
err = proof.Verify([]byte(key), resQuery.Value, proof.RootHash)
require.Nil(t, err, "%+v", err) // NOTE: we have no way to verify the RootHash
}
34 changes: 34 additions & 0 deletions example/dummy/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dummy

import (
"github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
)

// RandVal creates one random validator, with a key derived
// from the input value
func RandVal(i int) *types.Validator {
pubkey := crypto.GenPrivKeyEd25519FromSecret([]byte(cmn.Fmt("test%d", i))).PubKey().Bytes()
power := cmn.RandUint16() + 1
return &types.Validator{pubkey, uint64(power)}
}

// RandVals returns a list of cnt validators for initializing
// the application. Note that the keys are deterministically
// derived from the index in the array, while the power is
// random (Change this if not desired)
func RandVals(cnt int) []*types.Validator {
res := make([]*types.Validator, cnt)
for i := 0; i < cnt; i++ {
res[i] = RandVal(i)
}
return res
}

// InitDummy initializes the dummy app with some data,
// which allows tests to pass and is fine as long as you
// don't make any tx that modify the validator state
func InitDummy(app *PersistentDummyApplication) {
app.InitChain(types.RequestInitChain{RandVals(1)})
}
86 changes: 21 additions & 65 deletions example/dummy/persistent_dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"strconv"
"strings"

"github.com/pkg/errors"
"github.com/tendermint/abci/types"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/merkleeyes/iavl"
"github.com/tendermint/iavl"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
Expand All @@ -23,11 +21,6 @@ const (

type PersistentDummyApplication struct {
app *DummyApplication
db dbm.DB

// latest received
// TODO: move to merkle tree?
blockHeader *types.Header

// validator set
changes []*types.Validator
Expand All @@ -36,17 +29,17 @@ type PersistentDummyApplication struct {
}

func NewPersistentDummyApplication(dbDir string) *PersistentDummyApplication {
db := dbm.NewDB("dummy", "leveldb", dbDir)
lastBlock := LoadLastBlock(db)

stateTree := iavl.NewIAVLTree(0, db)
stateTree.Load(lastBlock.AppHash)
name := "dummy"
db, err := dbm.NewGoLevelDB(name, dbDir)
if err != nil {
panic(err)
}

// log.Notice("Loaded state", "block", lastBlock.Height, "root", stateTree.Hash())
stateTree := iavl.NewVersionedTree(500, db)
stateTree.Load()

return &PersistentDummyApplication{
app: &DummyApplication{state: stateTree},
db: db,
logger: log.NewNopLogger(),
}
}
Expand All @@ -57,9 +50,8 @@ func (app *PersistentDummyApplication) SetLogger(l log.Logger) {

func (app *PersistentDummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
resInfo = app.app.Info(req)
lastBlock := LoadLastBlock(app.db)
resInfo.LastBlockHeight = lastBlock.Height
resInfo.LastBlockAppHash = lastBlock.AppHash
resInfo.LastBlockHeight = app.app.state.LatestVersion()
resInfo.LastBlockAppHash = app.app.state.Hash()
return resInfo
}

Expand All @@ -85,19 +77,21 @@ func (app *PersistentDummyApplication) CheckTx(tx []byte) types.Result {
return app.app.CheckTx(tx)
}

// Commit will panic if InitChain was not called
func (app *PersistentDummyApplication) Commit() types.Result {
// Save
appHash := app.app.state.Save()
app.logger.Info("Saved state", "root", appHash)

lastBlock := LastBlockInfo{
Height: app.blockHeader.Height,
AppHash: appHash, // this hash will be in the next block header
}
// Save a new version for next height
height := app.app.state.LatestVersion() + 1
var appHash []byte
var err error

app.logger.Info("Saving block", "height", lastBlock.Height, "root", lastBlock.AppHash)
SaveLastBlock(app.db, lastBlock)
appHash, err = app.app.state.SaveVersion(height)
if err != nil {
// if this wasn't a dummy app, we'd do something smarter
panic(err)
}

app.logger.Info("Commit block", "height", height, "root", appHash)
return types.NewResultOK(appHash, "")
}

Expand All @@ -117,9 +111,6 @@ func (app *PersistentDummyApplication) InitChain(params types.RequestInitChain)

// Track the block hash and header information
func (app *PersistentDummyApplication) BeginBlock(params types.RequestBeginBlock) {
// update latest block info
app.blockHeader = params.Header

// reset valset changes
app.changes = make([]*types.Validator, 0)
}
Expand All @@ -129,41 +120,6 @@ func (app *PersistentDummyApplication) EndBlock(height uint64) (resEndBlock type
return types.ResponseEndBlock{Diffs: app.changes}
}

//-----------------------------------------
// persist the last block info

var lastBlockKey = []byte("lastblock")

type LastBlockInfo struct {
Height uint64
AppHash []byte
}

// Get the last block from the db
func LoadLastBlock(db dbm.DB) (lastBlock LastBlockInfo) {
buf := db.Get(lastBlockKey)
if len(buf) != 0 {
r, n, err := bytes.NewReader(buf), new(int), new(error)
wire.ReadBinaryPtr(&lastBlock, r, 0, n, err)
if *err != nil {
cmn.PanicCrisis(errors.Wrap(*err, "cannot load last block (data has been corrupted or its spec has changed)"))
}
// TODO: ensure that buf is completely read.
}

return lastBlock
}

func SaveLastBlock(db dbm.DB, lastBlock LastBlockInfo) {
buf, n, err := new(bytes.Buffer), new(int), new(error)
wire.WriteBinary(lastBlock, buf, n, err)
if *err != nil {
// TODO
cmn.PanicCrisis(errors.Wrap(*err, "cannot save last block"))
}
db.Set(lastBlockKey, buf.Bytes())
}

//---------------------------------------------
// update validators

Expand Down
20 changes: 9 additions & 11 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading