Skip to content

Commit 5b2f60b

Browse files
committed
Adds TreeStore module
1 parent 70a1a0e commit 5b2f60b

7 files changed

Lines changed: 127 additions & 69 deletions

File tree

persistence/context.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type PostgresContext struct {
3131
// Need to simply access them via the bus.
3232
blockStore blockstore.BlockStore
3333
txIndexer indexer.TxIndexer
34-
stateTrees modules.TreeStore
34+
stateTrees modules.TreeStoreModule
3535

3636
networkId string
3737
}
@@ -50,7 +50,7 @@ func (p *PostgresContext) RollbackToSavePoint(bytes []byte) error {
5050
// IMPROVE(#361): Guarantee the integrity of the state
5151
// Full details in the thread from the PR review: https://github.com/pokt-network/pocket/pull/285#discussion_r1018471719
5252
func (p *PostgresContext) ComputeStateHash() (string, error) {
53-
stateHash, err := p.stateTrees.Update(p.tx, p.txIndexer, uint64(p.Height))
53+
stateHash, err := p.stateTrees.Update(p.tx, uint64(p.Height))
5454
if err != nil {
5555
return "", err
5656
}

persistence/docs/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
## [0.0.0.58] - 2023-06-14
10+
## [0.0.0.59] - 2023-06-14
11+
12+
- Refactors the persistence treeStore to be an IntegratableModule
13+
14+
## [0.0.0.58] - 2023-06-13
1115

1216
- Refactors the stateTrees implementation off of the PersistenceContext and into its in module
1317
- Implements the new Update and Commit pattern with the SMT trees in the trees module

persistence/module.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818

1919
var (
2020
_ modules.PersistenceModule = &persistenceModule{}
21-
_ modules.PersistenceModule = &persistenceModule{}
2221

2322
_ modules.PersistenceRWContext = &PostgresContext{}
2423
)
@@ -44,7 +43,7 @@ type persistenceModule struct {
4443

4544
// stateTrees manages all of the merkle trees maintained by the
4645
// persistence module that roll up into the state commitment.
47-
stateTrees modules.TreeStore
46+
stateTrees modules.TreeStoreModule
4847

4948
// Only one write context is allowed at a time
5049
writeContext *PostgresContext
@@ -104,8 +103,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio
104103
return nil, err
105104
}
106105

107-
// TECHDEBT (#808): Make TreeStore into a full Module
108-
stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir)
106+
treeModule, err := trees.Create(bus, trees.WithTreeStoreDirectory(persistenceCfg.TreesStoreDir))
109107
if err != nil {
110108
return nil, err
111109
}
@@ -116,7 +114,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio
116114

117115
m.blockStore = blockStore
118116
m.txIndexer = txIndexer
119-
m.stateTrees = stateTrees
117+
m.stateTrees = treeModule
120118

121119
// TECHDEBT: reconsider if this is the best place to call `populateGenesisState`. Note that
122120
// this forces the genesis state to be reloaded on every node startup until state
@@ -237,10 +235,6 @@ func (m *persistenceModule) GetTxIndexer() indexer.TxIndexer {
237235
return m.txIndexer
238236
}
239237

240-
func (m *persistenceModule) GetTreeStore() modules.TreeStore {
241-
return m.stateTrees
242-
}
243-
244238
func (m *persistenceModule) GetNetworkID() string {
245239
return m.networkId
246240
}

persistence/trees/module.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package trees
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/pokt-network/pocket/persistence/kvstore"
7+
"github.com/pokt-network/pocket/shared/modules"
8+
"github.com/pokt-network/smt"
9+
)
10+
11+
func (*treeStore) Create(bus modules.Bus, options ...modules.TreeStoreOption) (modules.TreeStoreModule, error) {
12+
m := &treeStore{}
13+
14+
for _, option := range options {
15+
option(m)
16+
}
17+
18+
m.SetBus(bus)
19+
20+
if err := m.setupTrees(); err != nil {
21+
return nil, err
22+
}
23+
24+
return m, nil
25+
}
26+
27+
func Create(bus modules.Bus, options ...modules.TreeStoreOption) (modules.TreeStoreModule, error) {
28+
return new(treeStore).Create(bus, options...)
29+
}
30+
31+
// WithTreeStoreDirectory assigns the path where the tree store
32+
// saves its data.
33+
func WithTreeStoreDirectory(path string) modules.TreeStoreOption {
34+
return func(m modules.TreeStoreModule) {
35+
mod, ok := m.(*treeStore)
36+
if ok {
37+
mod.treeStoreDir = path
38+
}
39+
}
40+
}
41+
42+
func (t *treeStore) setupTrees() error {
43+
if t.treeStoreDir == ":memory:" {
44+
return t.setupInMemory()
45+
}
46+
47+
t.merkleTrees = make(map[merkleTree]*smt.SMT, int(numMerkleTrees))
48+
t.nodeStores = make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees))
49+
50+
for tree := merkleTree(0); tree < numMerkleTrees; tree++ {
51+
nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", t.treeStoreDir, merkleTreeToString[tree]))
52+
if err != nil {
53+
return err
54+
}
55+
t.nodeStores[tree] = nodeStore
56+
t.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, smtTreeHasher)
57+
}
58+
59+
return nil
60+
}
61+
62+
func (t *treeStore) setupInMemory() error {
63+
t.merkleTrees = make(map[merkleTree]*smt.SMT, int(numMerkleTrees))
64+
t.nodeStores = make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees))
65+
66+
for tree := merkleTree(0); tree < numMerkleTrees; tree++ {
67+
nodeStore := kvstore.NewMemKVStore() // For testing, `smt.NewSimpleMap()` can be used as well
68+
t.nodeStores[tree] = nodeStore
69+
t.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, smtTreeHasher)
70+
}
71+
72+
return nil
73+
}

persistence/trees/trees.go

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ import (
1919
"github.com/pokt-network/pocket/shared/codec"
2020
coreTypes "github.com/pokt-network/pocket/shared/core/types"
2121
"github.com/pokt-network/pocket/shared/crypto"
22+
"github.com/pokt-network/pocket/shared/modules"
23+
"github.com/pokt-network/pocket/shared/modules/base_modules"
2224
"github.com/pokt-network/smt"
2325
)
2426

27+
// smtTreeHasher sets the hasher used by the tree SMT trees
28+
// as a package level variable for visibility and internal use.
2529
var smtTreeHasher hash.Hash = sha256.New()
2630

2731
var merkleTreeToString = map[merkleTree]string{
@@ -56,8 +60,7 @@ type merkleTree float64
5660

5761
// A list of Merkle Trees used to maintain the state hash.
5862
const (
59-
// IMPORTANT: The order in which these trees are defined is important and strict. It implicitly
60-
// defines the index of the root hash each independent as they are concatenated together
63+
// IMPORTANT: The order in which these trees are defined is important and strict. It implicitly // defines the index of the root hash each independent as they are concatenated together
6164
// to generate the state hash.
6265

6366
// TECHDEBT(#834): Remove the need for enforced ordering
@@ -81,45 +84,29 @@ const (
8184
numMerkleTrees
8285
)
8386

87+
// Ensure treeStore implements TreeStore
88+
var _ modules.TreeStoreModule = &treeStore{}
89+
8490
// treeStore stores a set of merkle trees that
8591
// it manages. It fulfills the modules.TreeStore interface.
8692
// * It is responsible for atomic commit or rollback behavior
8793
// of the underlying trees by utilizing the lazy loading
8894
// functionality provided by the underlying smt library.
8995
type treeStore struct {
96+
base_modules.IntegratableModule
97+
9098
treeStoreDir string
9199
merkleTrees map[merkleTree]*smt.SMT
92100
nodeStores map[merkleTree]kvstore.KVStore
93101
}
94102

95-
func (t *treeStore) Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) {
103+
// Update takes a transaction and a height and updates
104+
// all of the trees in the treeStore for that height.
105+
func (t *treeStore) Update(pgtx pgx.Tx, height uint64) (string, error) {
106+
txi := t.GetBus().GetPersistenceModule().GetTxIndexer()
96107
return t.updateMerkleTrees(pgtx, txi, height)
97108
}
98109

99-
// NewStateTrees is the constructor object for a treeStore and initializes and configures a new
100-
// tree for the appropriate type of store, i.e. in-memory vs file system storage.
101-
func NewStateTrees(treesStoreDir string) (*treeStore, error) {
102-
if treesStoreDir == ":memory:" {
103-
return newMemStateTrees()
104-
}
105-
106-
stateTrees := &treeStore{
107-
treeStoreDir: treesStoreDir,
108-
merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)),
109-
nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)),
110-
}
111-
112-
for tree := merkleTree(0); tree < numMerkleTrees; tree++ {
113-
nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", treesStoreDir, merkleTreeToString[tree]))
114-
if err != nil {
115-
return nil, err
116-
}
117-
stateTrees.nodeStores[tree] = nodeStore
118-
stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, smtTreeHasher)
119-
}
120-
return stateTrees, nil
121-
}
122-
123110
// DebugClearAll is used by the debug cli to completely reset all merkle trees.
124111
// This should only be called by the debug CLI.
125112
// TECHDEBT: Move this into a separate file with a debug build flag to avoid accidental usage in prod
@@ -134,20 +121,6 @@ func (t *treeStore) DebugClearAll() error {
134121
return nil
135122
}
136123

137-
// newMemStateTrees creates a new in-memory state tree
138-
func newMemStateTrees() (*treeStore, error) {
139-
stateTrees := &treeStore{
140-
merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)),
141-
nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)),
142-
}
143-
for tree := merkleTree(0); tree < numMerkleTrees; tree++ {
144-
nodeStore := kvstore.NewMemKVStore() // For testing, `smt.NewSimpleMap()` can be used as well
145-
stateTrees.nodeStores[tree] = nodeStore
146-
stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, smtTreeHasher)
147-
}
148-
return stateTrees, nil
149-
}
150-
151124
// updateMerkleTrees updates all of the merkle trees in order defined by `numMerkleTrees`
152125
// * it returns the new state hash capturing the state of all the trees or an error if one occurred
153126
func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) {

shared/modules/persistence_module.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package modules
33
//go:generate mockgen -destination=./mocks/persistence_module_mock.go github.com/pokt-network/pocket/shared/modules PersistenceModule,PersistenceRWContext,PersistenceReadContext,PersistenceWriteContext
44

55
import (
6-
"github.com/jackc/pgx/v5"
76
"github.com/pokt-network/pocket/persistence/blockstore"
87
"github.com/pokt-network/pocket/persistence/indexer"
98
"github.com/pokt-network/pocket/runtime/genesis"
@@ -25,10 +24,6 @@ type PersistenceModule interface {
2524
// BlockStore maps a block height to an *coreTypes.IndexedTransaction
2625
GetBlockStore() blockstore.BlockStore
2726

28-
// TreeStore manages atomic access to a set of merkle trees
29-
// that compose the state hash.
30-
GetTreeStore() TreeStore
31-
3227
NewWriteContext() PersistenceRWContext
3328

3429
// Indexer operations
@@ -39,17 +34,6 @@ type PersistenceModule interface {
3934
HandleDebugMessage(*messaging.DebugMessage) error
4035
}
4136

42-
// TreeStore defines the interface for atomic updates and rollbacks to the internal
43-
// merkle trees that compose the state hash of pocket.
44-
type TreeStore interface {
45-
// Update returns the new state hash for a given height.
46-
// * Update inherits the pgx transaction's read view of the database and builds the trees according to that view.
47-
// TODO(#808): Change interface to `Update(pgtx pgx.Tx, height uint64) (string, error)`
48-
Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error)
49-
// DebugClearAll completely clears the state of the trees. For debugging purposes only.
50-
DebugClearAll() error
51-
}
52-
5337
// Interface defining the context within which the node can operate with the persistence layer.
5438
// Operations in the context of a PersistenceContext are isolated from other operations and
5539
// other persistence contexts until committed, enabling parallelizability along other operations.

shared/modules/treestore_module.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package modules
2+
3+
import (
4+
"github.com/jackc/pgx/v5"
5+
)
6+
7+
const (
8+
TreeStoreModuleName = "tree_store"
9+
)
10+
11+
type TreeStoreOption func(TreeStoreModule)
12+
13+
type TreeStoreFactory = FactoryWithOptions[TreeStoreModule, TreeStoreOption]
14+
15+
// TreeStoreModules defines the interface for atomic updates and rollbacks to the internal
16+
// merkle trees that compose the state hash of pocket.
17+
type TreeStoreModule interface {
18+
IntegratableModule
19+
20+
// Update returns the new state hash for a given height.
21+
// * Height is passed through to the Update function and is used to query the TxIndexer for transactions
22+
// to update into the merkle tree set
23+
// * Passing a higher height will cause a change but repeatedly calling the same or a lower height will
24+
// not incur a change.
25+
// * By nature of it taking a pgx transaction at runtime, Update inherits the pgx transaction's read view of the
26+
// database.
27+
Update(pgtx pgx.Tx, height uint64) (string, error)
28+
// DebugClearAll completely clears the state of the trees. For debugging purposes only.
29+
DebugClearAll() error
30+
}

0 commit comments

Comments
 (0)