Skip to content

Commit 9fb76ef

Browse files
authored
Merge pull request #10 from tendermint/develop
Develop
2 parents 549dd01 + a09ac0a commit 9fb76ef

File tree

9 files changed

+414
-241
lines changed

9 files changed

+414
-241
lines changed

app/app.go

Lines changed: 152 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,103 @@ package app
22

33
import (
44
"fmt"
5+
"path"
56

67
abci "github.com/tendermint/abci/types"
7-
. "github.com/tendermint/go-common"
8+
cmn "github.com/tendermint/go-common"
9+
dbm "github.com/tendermint/go-db"
810
"github.com/tendermint/go-merkle"
911
"github.com/tendermint/go-wire"
1012
)
1113

1214
type MerkleEyesApp struct {
13-
state State
15+
abci.BaseApplication
16+
17+
state State
18+
db dbm.DB
19+
height uint64
20+
}
21+
22+
// just make sure we really are an application, if the interface
23+
// ever changes in the future
24+
func (m *MerkleEyesApp) assertApplication() abci.Application {
25+
return m
26+
}
27+
28+
var eyesStateKey = []byte("merkleeyes:state") // Database key for merkle tree save value db values
29+
30+
type MerkleEyesState struct {
31+
Hash []byte
32+
Height uint64
1433
}
1534

16-
func NewMerkleEyesApp() *MerkleEyesApp {
17-
tree := merkle.NewIAVLTree(
18-
0,
19-
nil,
20-
)
21-
return &MerkleEyesApp{state: NewState(tree)}
35+
const (
36+
WriteSet byte = 0x01
37+
WriteRem byte = 0x02
38+
)
39+
40+
func NewMerkleEyesApp(dbName string, cacheSize int) *MerkleEyesApp {
41+
// start at 1 so the height returned by query is for the
42+
// next block, ie. the one that includes the AppHash for our current state
43+
initialHeight := uint64(1)
44+
45+
// Non-persistent case
46+
if dbName == "" {
47+
tree := merkle.NewIAVLTree(
48+
0,
49+
nil,
50+
)
51+
return &MerkleEyesApp{
52+
state: NewState(tree, false),
53+
db: nil,
54+
height: initialHeight,
55+
}
56+
}
57+
58+
// Setup the persistent merkle tree
59+
empty, _ := cmn.IsDirEmpty(path.Join(dbName, dbName+".db"))
60+
61+
// Open the db, if the db doesn't exist it will be created
62+
db := dbm.NewDB(dbName, dbm.LevelDBBackendStr, dbName)
63+
64+
// Load Tree
65+
tree := merkle.NewIAVLTree(cacheSize, db)
66+
67+
if empty {
68+
fmt.Println("no existing db, creating new db")
69+
db.Set(eyesStateKey, wire.BinaryBytes(MerkleEyesState{
70+
Hash: tree.Save(),
71+
Height: initialHeight,
72+
}))
73+
} else {
74+
fmt.Println("loading existing db")
75+
}
76+
77+
// Load merkle state
78+
eyesStateBytes := db.Get(eyesStateKey)
79+
var eyesState MerkleEyesState
80+
err := wire.ReadBinaryBytes(eyesStateBytes, &eyesState)
81+
if err != nil {
82+
fmt.Println("error reading MerkleEyesState")
83+
panic(err.Error())
84+
}
85+
tree.Load(eyesState.Hash)
86+
87+
return &MerkleEyesApp{
88+
state: NewState(tree, true),
89+
db: db,
90+
height: eyesState.Height,
91+
}
92+
}
93+
94+
func (app *MerkleEyesApp) CloseDB() {
95+
if app.db != nil {
96+
app.db.Close()
97+
}
2298
}
2399

24100
func (app *MerkleEyesApp) Info() abci.ResponseInfo {
25-
return abci.ResponseInfo{Data: Fmt("size:%v", app.state.Committed().Size())}
101+
return abci.ResponseInfo{Data: cmn.Fmt("size:%v", app.state.Committed().Size())}
26102
}
27103

28104
func (app *MerkleEyesApp) SetOption(key string, value string) (log string) {
@@ -31,113 +107,121 @@ func (app *MerkleEyesApp) SetOption(key string, value string) (log string) {
31107

32108
func (app *MerkleEyesApp) DeliverTx(tx []byte) abci.Result {
33109
tree := app.state.Append()
34-
return app.DoTx(tree, tx)
110+
return app.doTx(tree, tx)
35111
}
36112

37113
func (app *MerkleEyesApp) CheckTx(tx []byte) abci.Result {
38114
tree := app.state.Check()
39-
return app.DoTx(tree, tx)
115+
return app.doTx(tree, tx)
40116
}
41117

42-
func (app *MerkleEyesApp) DoTx(tree merkle.Tree, tx []byte) abci.Result {
118+
func (app *MerkleEyesApp) doTx(tree merkle.Tree, tx []byte) abci.Result {
43119
if len(tx) == 0 {
44120
return abci.ErrEncodingError.SetLog("Tx length cannot be zero")
45121
}
46122
typeByte := tx[0]
47123
tx = tx[1:]
48124
switch typeByte {
49-
case 0x01: // Set
125+
case WriteSet: // Set
50126
key, n, err := wire.GetByteSlice(tx)
51127
if err != nil {
52-
return abci.ErrEncodingError.SetLog(Fmt("Error getting key: %v", err.Error()))
128+
return abci.ErrEncodingError.SetLog(cmn.Fmt("Error reading key: %v", err.Error()))
53129
}
54130
tx = tx[n:]
55131
value, n, err := wire.GetByteSlice(tx)
56132
if err != nil {
57-
return abci.ErrEncodingError.SetLog(Fmt("Error getting value: %v", err.Error()))
133+
return abci.ErrEncodingError.SetLog(cmn.Fmt("Error reading value: %v", err.Error()))
58134
}
59135
tx = tx[n:]
60136
if len(tx) != 0 {
61-
return abci.ErrEncodingError.SetLog(Fmt("Got bytes left over"))
137+
return abci.ErrEncodingError.SetLog(cmn.Fmt("Got bytes left over"))
62138
}
139+
63140
tree.Set(key, value)
64-
fmt.Println("SET", Fmt("%X", key), Fmt("%X", value))
65-
case 0x02: // Remove
141+
// fmt.Println("SET", cmn.Fmt("%X", key), cmn.Fmt("%X", value))
142+
case WriteRem: // Remove
66143
key, n, err := wire.GetByteSlice(tx)
67144
if err != nil {
68-
return abci.ErrEncodingError.SetLog(Fmt("Error getting key: %v", err.Error()))
145+
return abci.ErrEncodingError.SetLog(cmn.Fmt("Error reading key: %v", err.Error()))
69146
}
70147
tx = tx[n:]
71148
if len(tx) != 0 {
72-
return abci.ErrEncodingError.SetLog(Fmt("Got bytes left over"))
149+
return abci.ErrEncodingError.SetLog(cmn.Fmt("Got bytes left over"))
73150
}
74151
tree.Remove(key)
75152
default:
76-
return abci.ErrUnknownRequest.SetLog(Fmt("Unexpected Tx type byte %X", typeByte))
153+
return abci.ErrUnknownRequest.SetLog(cmn.Fmt("Unexpected Tx type byte %X", typeByte))
77154
}
78155
return abci.OK
79156
}
80157

81158
func (app *MerkleEyesApp) Commit() abci.Result {
159+
82160
hash := app.state.Commit()
161+
162+
app.height++
163+
if app.db != nil {
164+
app.db.Set(eyesStateKey, wire.BinaryBytes(MerkleEyesState{
165+
Hash: hash,
166+
Height: app.height,
167+
}))
168+
}
169+
83170
if app.state.Committed().Size() == 0 {
84171
return abci.NewResultOK(nil, "Empty hash for empty tree")
85172
}
86173
return abci.NewResultOK(hash, "")
87174
}
88175

89-
func (app *MerkleEyesApp) Query(query []byte) abci.Result {
90-
if len(query) == 0 {
91-
return abci.OK
176+
func (app *MerkleEyesApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
177+
if len(reqQuery.Data) == 0 {
178+
return
92179
}
93180
tree := app.state.Committed()
94181

95-
typeByte := query[0]
96-
query = query[1:]
97-
switch typeByte {
98-
case 0x01: // Get by key
99-
key, n, err := wire.GetByteSlice(query)
100-
if err != nil {
101-
return abci.ErrEncodingError.SetLog(Fmt("Error getting key: %v", err.Error()))
102-
}
103-
query = query[n:]
104-
if len(query) != 0 {
105-
return abci.ErrEncodingError.SetLog(Fmt("Got bytes left over"))
106-
}
107-
_, value, _ := tree.Get(key)
108-
return abci.NewResultOK(value, "")
109-
case 0x02: // Get by index
110-
index, n, err := wire.GetVarint(query)
111-
if err != nil {
112-
return abci.ErrEncodingError.SetLog(Fmt("Error getting index: %v", err.Error()))
113-
}
114-
query = query[n:]
115-
if len(query) != 0 {
116-
return abci.ErrEncodingError.SetLog(Fmt("Got bytes left over"))
117-
}
118-
_, value := tree.GetByIndex(index)
119-
return abci.NewResultOK(value, "")
120-
case 0x03: // Get size
121-
size := tree.Size()
122-
res := wire.BinaryBytes(size)
123-
return abci.NewResultOK(res, "")
124-
default:
125-
return abci.ErrUnknownRequest.SetLog(Fmt("Unexpected Query type byte %X", typeByte))
182+
if reqQuery.Height != 0 {
183+
// TODO: support older commits
184+
resQuery.Code = abci.CodeType_InternalError
185+
resQuery.Log = "merkleeyes only supports queries on latest commit"
186+
return
126187
}
127-
}
128188

129-
// Proof fulfills the ABCI app interface. key is the one for which we
130-
// request a proof. blockHeight is the height for which we want the proof.
131-
// If blockHeight is 0, return the last commit.
132-
func (app *MerkleEyesApp) Proof(key []byte, blockHeight uint64) abci.Result {
133-
// TODO: support older commits - right now we don't save the info
134-
if blockHeight != 0 {
135-
return abci.ErrInternalError.SetLog("merkleeyes only supports proofs on latest commit")
136-
}
189+
// set the query response height
190+
resQuery.Height = app.height
137191

138-
proof, exists := app.state.Committed().Proof(key)
139-
if !exists {
140-
return abci.NewResultOK(nil, "Key not found")
192+
switch reqQuery.Path {
193+
case "/store", "/key": // Get by key
194+
key := reqQuery.Data // Data holds the key bytes
195+
resQuery.Key = key
196+
if reqQuery.Prove {
197+
value, proof, exists := tree.Proof(key)
198+
if !exists {
199+
resQuery.Log = "Key not found"
200+
}
201+
resQuery.Value = value
202+
resQuery.Proof = proof
203+
// TODO: return index too?
204+
} else {
205+
index, value, _ := tree.Get(key)
206+
resQuery.Value = value
207+
resQuery.Index = int64(index)
208+
}
209+
210+
case "/index": // Get by Index
211+
index := wire.GetInt64(reqQuery.Data)
212+
key, value := tree.GetByIndex(int(index))
213+
resQuery.Key = key
214+
resQuery.Index = int64(index)
215+
resQuery.Value = value
216+
217+
case "/size": // Get size
218+
size := tree.Size()
219+
sizeBytes := wire.BinaryBytes(size)
220+
resQuery.Value = sizeBytes
221+
222+
default:
223+
resQuery.Code = abci.CodeType_UnknownRequest
224+
resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path)
141225
}
142-
return abci.NewResultOK(proof, "")
226+
return
143227
}

0 commit comments

Comments
 (0)