diff --git a/app/app.go b/app/app.go index 5fcd9a56b..08986c526 100644 --- a/app/app.go +++ b/app/app.go @@ -1,6 +1,7 @@ package app import ( + "bytes" "fmt" "path" @@ -18,6 +19,7 @@ type MerkleEyesApp struct { state State db dbm.DB + hash []byte height uint64 } @@ -87,12 +89,14 @@ func NewMerkleEyesApp(dbName string, cacheSize int) *MerkleEyesApp { fmt.Println("error reading MerkleEyesState") panic(err.Error()) } + tree.Load(eyesState.Hash) return &MerkleEyesApp{ state: NewState(tree, true), db: db, height: eyesState.Height, + hash: eyesState.Hash, } } @@ -103,9 +107,15 @@ func (app *MerkleEyesApp) CloseDB() { } } -// Info implements abci.Application +// Info implements abci.Application. It returns the height, hash and size (in the data). +// The height is the block that holds the transactions, not the apphash itself. func (app *MerkleEyesApp) Info() abci.ResponseInfo { - return abci.ResponseInfo{Data: cmn.Fmt("size:%v", app.state.Committed().Size())} + fmt.Printf("Info height %d hash %x\n", app.height, app.hash) + return abci.ResponseInfo{ + Data: cmn.Fmt("size:%v", app.state.Committed().Size()), + LastBlockHeight: app.height - 1, + LastBlockAppHash: app.hash, + } } // SetOption implements abci.Application @@ -168,20 +178,28 @@ func (app *MerkleEyesApp) doTx(tree merkle.Tree, tx []byte) abci.Result { // Commit implements abci.Application func (app *MerkleEyesApp) Commit() abci.Result { - hash := app.state.Commit() - + app.hash = app.state.Hash() app.height++ + fmt.Printf("height=%d,hash=%x\n", app.height, app.hash) + if app.db != nil { - app.db.Set(eyesStateKey, wire.BinaryBytes(MerkleEyesState{ - Hash: hash, + // This is in the batch with the Save, but not in the tree + tree := app.state.Append().(*iavl.IAVLTree) + tree.BatchSet(eyesStateKey, wire.BinaryBytes(MerkleEyesState{ + Hash: app.hash, Height: app.height, })) } + hash := app.state.Commit() + if !bytes.Equal(hash, app.hash) { + panic("AppHash is incorrect") + } + if app.state.Committed().Size() == 0 { return abci.NewResultOK(nil, "Empty hash for empty tree") } - return abci.NewResultOK(hash, "") + return abci.NewResultOK(app.hash, "") } // Query implements abci.Application @@ -198,7 +216,7 @@ func (app *MerkleEyesApp) Query(reqQuery abci.RequestQuery) (resQuery abci.Respo return } - // set the query response height + // set the query response height to current resQuery.Height = app.height switch reqQuery.Path { diff --git a/app/state.go b/app/state.go index 031271768..47c6d9b36 100644 --- a/app/state.go +++ b/app/state.go @@ -32,9 +32,12 @@ func (s State) Check() merkle.Tree { return s.checkTx } -// Commit stores the current Append() state as committed -// starts new Append/Check state, and -// returns the hash for the commit +// Hash updates the tree +func (s *State) Hash() []byte { + return s.deliverTx.Hash() +} + +// Commit save persistent nodes to the database and re-copies the trees func (s *State) Commit() []byte { var hash []byte if s.persistent { diff --git a/iavl/iavl_tree.go b/iavl/iavl_tree.go index 82aee8600..b644612d0 100644 --- a/iavl/iavl_tree.go +++ b/iavl/iavl_tree.go @@ -103,6 +103,11 @@ func (t *IAVLTree) Set(key []byte, value []byte) (updated bool) { return updated } +// BatchSet adds a Set to the current batch, will get handled atomically +func (t *IAVLTree) BatchSet(key []byte, value []byte) { + t.ndb.batch.Set(key, value) +} + func (t *IAVLTree) Hash() []byte { if t.root == nil { return nil diff --git a/tests/tendermint/cleanup.sh b/tests/tendermint/cleanup.sh new file mode 100755 index 000000000..8d6b683b0 --- /dev/null +++ b/tests/tendermint/cleanup.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +echo "Cleaning up old test results" + +rm -rf orphan-test-db 2>/dev/null +rm merkleeyes.log 2>/dev/null +rm tendermint.log 2>/dev/null +rm query.txt 2>/dev/null diff --git a/tests/tendermint/eightbytekey.go b/tests/tendermint/eightbytekey.go new file mode 100644 index 000000000..00c917fa5 --- /dev/null +++ b/tests/tendermint/eightbytekey.go @@ -0,0 +1,12 @@ +package main + +import ( + "crypto/rand" + "fmt" +) + +func main() { + buf := make([]byte, 8) + rand.Read(buf) + fmt.Printf("%x\n", buf) +} diff --git a/tests/tendermint/gen_merk_trans.sh b/tests/tendermint/gen_merk_trans.sh new file mode 100755 index 000000000..db67d9f43 --- /dev/null +++ b/tests/tendermint/gen_merk_trans.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +if [ "$1" = "" ]; then + key=$(go run eightbytekey.go) +else + key=$1 +fi +key_size=0108 +value=33 +value_size=0101 + +echo -n 0x01${key_size}${key}${value_size}${value} + diff --git a/tests/tendermint/height.sh b/tests/tendermint/height.sh new file mode 100755 index 000000000..c1509183b --- /dev/null +++ b/tests/tendermint/height.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +echo "Sending a transaction" + +key=$(go run eightbytekey.go) +tx=`./gen_merk_trans.sh ${key}` + +echo -n "Hash=" +curl http://localhost:46657/broadcast_tx_commit\?tx\=${tx} 2>/dev/null |\ +jq 'if .result == 0 then + "No Result" +elif .check_tx.log > 2 then + .result | .check_tx.log +else + .result | .hash +end' +echo "" + +echo "Checking its height" + +curl http://localhost:46657/status 2>/dev/null > status.txt +curl http://localhost:46657/abci_query\?data\=0x${key}\&path\=\"/key\"\&prove=true 2>/dev/null > query.txt + +echo -n "status height=" +cat status.txt | jq '.result.latest_block_height' + +echo -n "query height=" +cat query.txt | jq '.result.response.height' + +echo -n "status apphash=" +cat status.txt | jq '.result.latest_app_hash' + +# get the apphash from the proof +echo -n "query proof=" +cat query.txt | jq '.result.response.proof' + +# A second time +sleep 1 +curl http://localhost:46657/status 2>/dev/null > status.txt +curl http://localhost:46657/abci_query\?data\=0x${key}\&path\=\"/key\"\&prove=true 2>/dev/null > query.txt + +echo -n "status height=" +cat status.txt | jq '.result.latest_block_height' + +echo -n "query height=" +cat query.txt | jq '.result.response.height' + +echo -n "status apphash=" +cat status.txt | jq '.result.latest_app_hash' + +# get the apphash from the proof +echo -n "query proof=" +cat query.txt | jq '.result.response.proof' + +echo "" diff --git a/tests/tendermint/height_test.sh b/tests/tendermint/height_test.sh new file mode 100755 index 000000000..f8aacdf98 --- /dev/null +++ b/tests/tendermint/height_test.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +./start-tendermerk.sh +./transaction.sh +./height.sh +./transaction.sh +./height.sh +./transaction.sh +./height.sh +./kill-tendermerk.sh + +./restart-tendermerk.sh +./transaction.sh +./height.sh +./transaction.sh +./height.sh +./kill-tendermerk.sh diff --git a/tests/tendermint/kill-tendermerk.sh b/tests/tendermint/kill-tendermerk.sh new file mode 100755 index 000000000..97a679501 --- /dev/null +++ b/tests/tendermint/kill-tendermerk.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "Stopping Merkleeyes and Tendermint" +killall tendermint +killall merkleeyes diff --git a/tests/tendermint/restart-tendermerk.sh b/tests/tendermint/restart-tendermerk.sh new file mode 100755 index 000000000..f6468e47e --- /dev/null +++ b/tests/tendermint/restart-tendermerk.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +echo "Restarting Merkleeyes and Tendermint" + +merkleeyes start -d orphan-test-db --address=tcp://127.0.0.1:46658 >> merkleeyes.log & + +tendermint node >> tendermint.log & + +sleep 4 + +TPID=`pidof tendermint` +if [ -z "$TPID" ]; then + tail tendermint.log + exit 20 +fi diff --git a/tests/tendermint/start-tendermerk.sh b/tests/tendermint/start-tendermerk.sh new file mode 100755 index 000000000..7a20bd37c --- /dev/null +++ b/tests/tendermint/start-tendermerk.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +./cleanup.sh + +echo "Starting Merkleeyes and Tendermint" + +merkleeyes start -d orphan-test-db --address=tcp://127.0.0.1:46658 >> merkleeyes.log & + +rm -rf ~/.tendermint +tendermint init + +tendermint node >> tendermint.log & + +sleep 4 + +TPID=`pidof tendermint` +if [ -z "$TPID" ]; then + tail tendermint.log + exit 20 +fi diff --git a/tests/tendermint/transaction.sh b/tests/tendermint/transaction.sh new file mode 100755 index 000000000..904d8cc57 --- /dev/null +++ b/tests/tendermint/transaction.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +echo "Sending a transaction" + +echo -n "Hash=" +tx=`./gen_merk_trans.sh` + +curl http://localhost:46657/broadcast_tx_commit\?tx\=${tx} 2>/dev/null |\ +jq 'if .result == 0 then + "No Result" +elif .check_tx.log > 2 then + .result | .check_tx.log +else + .result | .hash +end' + +echo ""