From 4de90bda8755e944f5162dc382cd75f89667bb82 Mon Sep 17 00:00:00 2001 From: shakezula Date: Thu, 25 May 2023 18:01:50 -0600 Subject: [PATCH 01/24] [WIP] pulls stateTrees into its own file --- persistence/context.go | 5 +- persistence/debug.go | 15 +- persistence/module.go | 10 +- persistence/state.go | 382 --------------------- persistence/test/treestore_test.go | 8 + persistence/trees/trees.go | 493 +++++++++++++++++++++++++++ shared/modules/persistence_module.go | 5 + 7 files changed, 518 insertions(+), 400 deletions(-) delete mode 100644 persistence/state.go create mode 100644 persistence/test/treestore_test.go create mode 100644 persistence/trees/trees.go diff --git a/persistence/context.go b/persistence/context.go index f72a41f86..50f591866 100644 --- a/persistence/context.go +++ b/persistence/context.go @@ -10,6 +10,7 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/pokt-network/pocket/persistence/blockstore" "github.com/pokt-network/pocket/persistence/indexer" + "github.com/pokt-network/pocket/persistence/trees" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/modules" ) @@ -31,7 +32,7 @@ type PostgresContext struct { // Need to simply access them via the bus. blockStore blockstore.BlockStore txIndexer indexer.TxIndexer - stateTrees *stateTrees + stateTrees trees.TreeStore networkId string } @@ -50,7 +51,7 @@ func (p *PostgresContext) RollbackToSavePoint(bytes []byte) error { // IMPROVE(#361): Guarantee the integrity of the state // Full details in the thread from the PR review: https://github.com/pokt-network/pocket/pull/285#discussion_r1018471719 func (p *PostgresContext) ComputeStateHash() (string, error) { - stateHash, err := p.updateMerkleTrees() + stateHash, err := p.stateTrees.Update(p.tx, uint64(p.Height)) if err != nil { return "", err } diff --git a/persistence/debug.go b/persistence/debug.go index cd6a70d99..1cf607c65 100644 --- a/persistence/debug.go +++ b/persistence/debug.go @@ -1,12 +1,10 @@ package persistence import ( - "crypto/sha256" "runtime/debug" "github.com/pokt-network/pocket/persistence/types" "github.com/pokt-network/pocket/shared/messaging" - "github.com/pokt-network/smt" ) // A list of functions to clear data from the DB not associated with protocol actors @@ -118,16 +116,5 @@ func (p *PostgresContext) clearAllSQLState() error { } func (p *PostgresContext) clearAllTreeState() error { - for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { - nodeStore := p.stateTrees.nodeStores[treeType] - - if err := nodeStore.ClearAll(); err != nil { - return err - } - - // Needed in order to make sure the root is re-set correctly after clearing - p.stateTrees.merkleTrees[treeType] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) - } - - return nil + return p.stateTrees.ClearAll() } diff --git a/persistence/module.go b/persistence/module.go index 32a6f51a4..a1ff7f3fd 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -9,6 +9,7 @@ import ( "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/persistence/blockstore" "github.com/pokt-network/pocket/persistence/indexer" + "github.com/pokt-network/pocket/persistence/trees" "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/runtime/genesis" "github.com/pokt-network/pocket/shared/modules" @@ -42,7 +43,8 @@ type persistenceModule struct { txIndexer indexer.TxIndexer // A list of all the merkle trees maintained by the persistence module that roll up into the state commitment. - stateTrees *stateTrees + // stateTrees *stateTrees + stateTrees trees.TreeStore // Only one write context is allowed at a time writeContext *PostgresContext @@ -102,7 +104,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio return nil, err } - stateTrees, err := newStateTrees(persistenceCfg.TreesStoreDir) + stateTrees, err := trees.NewtreeStore(persistenceCfg.TreesStoreDir) if err != nil { return nil, err } @@ -234,6 +236,10 @@ func (m *persistenceModule) GetTxIndexer() indexer.TxIndexer { return m.txIndexer } +func (m *persistenceModule) GetTreeStore() trees.TreeStore { + return m.stateTrees +} + func (m *persistenceModule) GetNetworkID() string { return m.networkId } diff --git a/persistence/state.go b/persistence/state.go deleted file mode 100644 index 7fc6d448a..000000000 --- a/persistence/state.go +++ /dev/null @@ -1,382 +0,0 @@ -package persistence - -import ( - "bytes" - "crypto/sha256" - "encoding/hex" - "fmt" - - "github.com/pokt-network/pocket/persistence/kvstore" - "github.com/pokt-network/pocket/persistence/types" - "github.com/pokt-network/pocket/shared/codec" - coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/crypto" - "github.com/pokt-network/smt" -) - -type merkleTree float64 - -type stateTrees struct { - merkleTrees map[merkleTree]*smt.SMT - - // nodeStores are part of the SMT, but references are kept below for convenience - // and debugging purposes - nodeStores map[merkleTree]kvstore.KVStore -} - -// A list of Merkle Trees used to maintain the state hash. -const ( - // 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 - // to generate the state hash. - - // Actor Merkle Trees - appMerkleTree merkleTree = iota - valMerkleTree - fishMerkleTree - servicerMerkleTree - - // Account Merkle Trees - accountMerkleTree - poolMerkleTree - - // Data Merkle Trees - transactionsMerkleTree - paramsMerkleTree - flagsMerkleTree - - // Used for iteration purposes only; see https://stackoverflow.com/a/64178235/768439 as a reference - numMerkleTrees -) - -var merkleTreeToString = map[merkleTree]string{ - appMerkleTree: "app", - valMerkleTree: "val", - fishMerkleTree: "fish", - servicerMerkleTree: "servicer", - - accountMerkleTree: "account", - poolMerkleTree: "pool", - - transactionsMerkleTree: "transactions", - paramsMerkleTree: "params", - flagsMerkleTree: "flags", -} - -var actorTypeToMerkleTreeName = map[coreTypes.ActorType]merkleTree{ - coreTypes.ActorType_ACTOR_TYPE_APP: appMerkleTree, - coreTypes.ActorType_ACTOR_TYPE_VAL: valMerkleTree, - coreTypes.ActorType_ACTOR_TYPE_FISH: fishMerkleTree, - coreTypes.ActorType_ACTOR_TYPE_SERVICER: servicerMerkleTree, -} - -var actorTypeToSchemaName = map[coreTypes.ActorType]types.ProtocolActorSchema{ - coreTypes.ActorType_ACTOR_TYPE_APP: types.ApplicationActor, - coreTypes.ActorType_ACTOR_TYPE_VAL: types.ValidatorActor, - coreTypes.ActorType_ACTOR_TYPE_FISH: types.FishermanActor, - coreTypes.ActorType_ACTOR_TYPE_SERVICER: types.ServicerActor, -} - -var merkleTreeToActorTypeName = map[merkleTree]coreTypes.ActorType{ - appMerkleTree: coreTypes.ActorType_ACTOR_TYPE_APP, - valMerkleTree: coreTypes.ActorType_ACTOR_TYPE_VAL, - fishMerkleTree: coreTypes.ActorType_ACTOR_TYPE_FISH, - servicerMerkleTree: coreTypes.ActorType_ACTOR_TYPE_SERVICER, -} - -func newStateTrees(treesStoreDir string) (*stateTrees, error) { - if treesStoreDir == ":memory:" { - return newMemStateTrees() - } - - stateTrees := &stateTrees{ - merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), - nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), - } - - for tree := merkleTree(0); tree < numMerkleTrees; tree++ { - nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", treesStoreDir, merkleTreeToString[tree])) - if err != nil { - return nil, err - } - stateTrees.nodeStores[tree] = nodeStore - stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) - } - return stateTrees, nil -} - -func newMemStateTrees() (*stateTrees, error) { - stateTrees := &stateTrees{ - merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), - nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), - } - for tree := merkleTree(0); tree < numMerkleTrees; tree++ { - nodeStore := kvstore.NewMemKVStore() // For testing, `smt.NewSimpleMap()` can be used as well - stateTrees.nodeStores[tree] = nodeStore - stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) - } - return stateTrees, nil -} - -func (p *PostgresContext) updateMerkleTrees() (string, error) { - // Update all the merkle trees - for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { - switch treeType { - // Actor Merkle Trees - case appMerkleTree, valMerkleTree, fishMerkleTree, servicerMerkleTree: - actorType, ok := merkleTreeToActorTypeName[treeType] - if !ok { - return "", fmt.Errorf("no actor type found for merkle tree: %v", treeType) - } - if err := p.updateActorsTree(actorType); err != nil { - return "", fmt.Errorf("failed to update actors tree for treeType: %v, actorType: %v - %w", treeType, actorType, err) - } - - // Account Merkle Trees - case accountMerkleTree: - if err := p.updateAccountTrees(); err != nil { - return "", fmt.Errorf("failed to update account trees - %w", err) - } - case poolMerkleTree: - if err := p.updatePoolTrees(); err != nil { - return "", fmt.Errorf("failed to update pool trees - %w", err) - } - - // Data Merkle Trees - case transactionsMerkleTree: - if err := p.updateTransactionsTree(); err != nil { - return "", fmt.Errorf("failed to update transactions tree - %w", err) - } - case paramsMerkleTree: - if err := p.updateParamsTree(); err != nil { - return "", fmt.Errorf("failed to update params tree - %w", err) - } - case flagsMerkleTree: - if err := p.updateFlagsTree(); err != nil { - return "", fmt.Errorf("failed to update flags tree - %w", err) - } - - // Default - default: - p.logger.Fatal().Msgf("Not handled yet in state commitment update. Merkle tree #{%v}", treeType) - } - } - - return p.getStateHash(), nil -} - -func (p *PostgresContext) getStateHash() string { - // Get the root of each Merkle Tree - roots := make([][]byte, 0) - for tree := merkleTree(0); tree < numMerkleTrees; tree++ { - roots = append(roots, p.stateTrees.merkleTrees[tree].Root()) - } - - // Get the state hash - rootsConcat := bytes.Join(roots, []byte{}) - stateHash := sha256.Sum256(rootsConcat) - - // Convert the array to a slice and return it - return hex.EncodeToString(stateHash[:]) -} - -// Actor Tree Helpers - -func (p *PostgresContext) updateActorsTree(actorType coreTypes.ActorType) error { - actors, err := p.getActorsUpdatedAtHeight(actorType, p.Height) - if err != nil { - return err - } - - for _, actor := range actors { - bzAddr, err := hex.DecodeString(actor.GetAddress()) - if err != nil { - return err - } - - actorBz, err := codec.GetCodec().Marshal(actor) - if err != nil { - return err - } - - merkleTreeName, ok := actorTypeToMerkleTreeName[actorType] - if !ok { - return fmt.Errorf("no merkle tree found for actor type: %s", actorType) - } - if err := p.stateTrees.merkleTrees[merkleTreeName].Update(bzAddr, actorBz); err != nil { - return err - } - - // If no errors updating then commit changes to the nodestore - if err := p.stateTrees.merkleTrees[merkleTreeName].Commit(); err != nil { - return err - } - } - - return nil -} - -func (p *PostgresContext) getActorsUpdatedAtHeight(actorType coreTypes.ActorType, height int64) (actors []*coreTypes.Actor, err error) { - actorSchema, ok := actorTypeToSchemaName[actorType] - if !ok { - return nil, fmt.Errorf("no schema found for actor type: %s", actorType) - } - - schemaActors, err := p.GetActorsUpdated(actorSchema, height) - if err != nil { - return nil, err - } - - actors = make([]*coreTypes.Actor, len(schemaActors)) - for i, schemaActor := range schemaActors { - actor := &coreTypes.Actor{ - ActorType: actorType, - Address: schemaActor.Address, - PublicKey: schemaActor.PublicKey, - Chains: schemaActor.Chains, - ServiceUrl: schemaActor.ServiceUrl, - StakedAmount: schemaActor.StakedAmount, - PausedHeight: schemaActor.PausedHeight, - UnstakingHeight: schemaActor.UnstakingHeight, - Output: schemaActor.Output, - } - actors[i] = actor - } - return -} - -// Account Tree Helpers - -func (p *PostgresContext) updateAccountTrees() error { - accounts, err := p.GetAccountsUpdated(p.Height) - if err != nil { - return err - } - - for _, account := range accounts { - bzAddr, err := hex.DecodeString(account.GetAddress()) - if err != nil { - return err - } - - accBz, err := codec.GetCodec().Marshal(account) - if err != nil { - return err - } - - if err := p.stateTrees.merkleTrees[accountMerkleTree].Update(bzAddr, accBz); err != nil { - return err - } - - // If no errors updating then commit changes to the nodestore - if err := p.stateTrees.merkleTrees[accountMerkleTree].Commit(); err != nil { - return err - } - } - - return nil -} - -func (p *PostgresContext) updatePoolTrees() error { - pools, err := p.GetPoolsUpdated(p.Height) - if err != nil { - return err - } - - for _, pool := range pools { - bzAddr, err := hex.DecodeString(pool.GetAddress()) - if err != nil { - return err - } - - accBz, err := codec.GetCodec().Marshal(pool) - if err != nil { - return err - } - - if err := p.stateTrees.merkleTrees[poolMerkleTree].Update(bzAddr, accBz); err != nil { - return err - } - - // If no errors updating then commit changes to the nodestore - if err := p.stateTrees.merkleTrees[poolMerkleTree].Commit(); err != nil { - return err - } - } - - return nil -} - -// Data Tree Helpers - -func (p *PostgresContext) updateTransactionsTree() error { - indexedTxs, err := p.txIndexer.GetByHeight(p.Height, false) - if err != nil { - return err - } - - for _, idxTx := range indexedTxs { - txBz := idxTx.GetTx() - txHash := crypto.SHA3Hash(txBz) - if err := p.stateTrees.merkleTrees[transactionsMerkleTree].Update(txHash, txBz); err != nil { - return err - } - - // If no errors updating then commit changes to the nodestore - if err := p.stateTrees.merkleTrees[transactionsMerkleTree].Commit(); err != nil { - return err - } - } - - return nil -} - -func (p *PostgresContext) updateParamsTree() error { - params, err := p.getParamsUpdated(p.Height) - if err != nil { - return err - } - - for _, param := range params { - paramBz, err := codec.GetCodec().Marshal(param) - paramKey := crypto.SHA3Hash([]byte(param.Name)) - if err != nil { - return err - } - if err := p.stateTrees.merkleTrees[paramsMerkleTree].Update(paramKey, paramBz); err != nil { - return err - } - - // If no errors updating then commit changes to the nodestore - if err := p.stateTrees.merkleTrees[paramsMerkleTree].Commit(); err != nil { - return err - } - } - - return nil -} - -func (p *PostgresContext) updateFlagsTree() error { - flags, err := p.getFlagsUpdated(p.Height) - if err != nil { - return err - } - - for _, flag := range flags { - flagBz, err := codec.GetCodec().Marshal(flag) - flagKey := crypto.SHA3Hash([]byte(flag.Name)) - if err != nil { - return err - } - if err := p.stateTrees.merkleTrees[flagsMerkleTree].Update(flagKey, flagBz); err != nil { - return err - } - - // If no errors updating then commit changes to the nodestore - if err := p.stateTrees.merkleTrees[flagsMerkleTree].Commit(); err != nil { - return err - } - } - - return nil -} diff --git a/persistence/test/treestore_test.go b/persistence/test/treestore_test.go new file mode 100644 index 000000000..90a9eaf48 --- /dev/null +++ b/persistence/test/treestore_test.go @@ -0,0 +1,8 @@ +package test + +import ( + "testing" +) + +func TestTreestoreUpdate(t *testing.T) { +} diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go new file mode 100644 index 000000000..e50f2d7d9 --- /dev/null +++ b/persistence/trees/trees.go @@ -0,0 +1,493 @@ +package trees + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + + "github.com/jackc/pgx/v5" + "github.com/pokt-network/pocket/persistence/kvstore" + "github.com/pokt-network/pocket/persistence/types" + "github.com/pokt-network/pocket/shared/codec" + coreTypes "github.com/pokt-network/pocket/shared/core/types" + "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/smt" +) + +type TreeStore interface { + Update(pgtx pgx.Tx, height uint64) (string, error) + // ClearAll completely clears the state of the trees. + // For debugging purposes only. + ClearAll() error +} + +var merkleTreeToString = map[merkleTree]string{ + appMerkleTree: "app", + valMerkleTree: "val", + fishMerkleTree: "fish", + servicerMerkleTree: "servicer", + + accountMerkleTree: "account", + poolMerkleTree: "pool", + + transactionsMerkleTree: "transactions", + paramsMerkleTree: "params", + flagsMerkleTree: "flags", +} + +var actorTypeToMerkleTreeName = map[coreTypes.ActorType]merkleTree{ + coreTypes.ActorType_ACTOR_TYPE_APP: appMerkleTree, + coreTypes.ActorType_ACTOR_TYPE_VAL: valMerkleTree, + coreTypes.ActorType_ACTOR_TYPE_FISH: fishMerkleTree, + coreTypes.ActorType_ACTOR_TYPE_SERVICER: servicerMerkleTree, +} + +var actorTypeToSchemaName = map[coreTypes.ActorType]types.ProtocolActorSchema{ + coreTypes.ActorType_ACTOR_TYPE_APP: types.ApplicationActor, + coreTypes.ActorType_ACTOR_TYPE_VAL: types.ValidatorActor, + coreTypes.ActorType_ACTOR_TYPE_FISH: types.FishermanActor, + coreTypes.ActorType_ACTOR_TYPE_SERVICER: types.ServicerActor, +} + +var merkleTreeToActorTypeName = map[merkleTree]coreTypes.ActorType{ + appMerkleTree: coreTypes.ActorType_ACTOR_TYPE_APP, + valMerkleTree: coreTypes.ActorType_ACTOR_TYPE_VAL, + fishMerkleTree: coreTypes.ActorType_ACTOR_TYPE_FISH, + servicerMerkleTree: coreTypes.ActorType_ACTOR_TYPE_SERVICER, +} + +type merkleTree float64 + +// A list of Merkle Trees used to maintain the state hash. +const ( + // 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 + // to generate the state hash. + + // Actor Merkle Trees + appMerkleTree merkleTree = iota + valMerkleTree + fishMerkleTree + servicerMerkleTree + + // Account Merkle Trees + accountMerkleTree + poolMerkleTree + + // Data Merkle Trees + transactionsMerkleTree + paramsMerkleTree + flagsMerkleTree + + // Used for iteration purposes only; see https://stackoverflow.com/a/64178235/768439 as a reference + numMerkleTrees +) + +// treeStore stores a set of merkle trees that +// it manages. +// * It is responsible for commit or rollback behavior +// of the underlying trees by utilizing the lazy loading +// functionality provided by the underlying smt library. +type treeStore struct { + merkleTrees map[merkleTree]*smt.SparseMerkleTree + + // nodeStores & valueStore are part of the SMT, but references are kept below for convenience + // and debugging purposes + nodeStores map[merkleTree]kvstore.KVStore + valueStores map[merkleTree]kvstore.KVStore +} + +// Update takes a transaction and a height and updates +// all of the trees in the treeStore for that height. +func (t *treeStore) Update(pgtx pgx.Tx, height uint64) (string, error) { + hash, err := t.updateMerkleTrees(pgtx, height) + return hash, err +} + +func NewtreeStore(treesStoreDir string) (*treeStore, error) { + if treesStoreDir == ":memory:" { + return newMemtreeStore() + } + + treeStore := &treeStore{ + merkleTrees: make(map[merkleTree]*smt.SparseMerkleTree, int(numMerkleTrees)), + nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), + valueStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), + } + + for tree := merkleTree(0); tree < numMerkleTrees; tree++ { + nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", treesStoreDir, merkleTreeToString[tree])) + if err != nil { + return nil, err + } + valueStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_values", treesStoreDir, merkleTreeToString[tree])) + if err != nil { + return nil, err + } + treeStore.nodeStores[tree] = nodeStore + treeStore.valueStores[tree] = valueStore + treeStore.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, valueStore, sha256.New()) + } + return treeStore, nil +} + +func newMemtreeStore() (*treeStore, error) { + treeStore := &treeStore{ + merkleTrees: make(map[merkleTree]*smt.SparseMerkleTree, int(numMerkleTrees)), + nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), + valueStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), + } + for tree := merkleTree(0); tree < numMerkleTrees; tree++ { + nodeStore := kvstore.NewMemKVStore() // For testing, `smt.NewSimpleMap()` can be used as well + valueStore := kvstore.NewMemKVStore() + treeStore.nodeStores[tree] = nodeStore + treeStore.valueStores[tree] = valueStore + treeStore.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, valueStore, sha256.New()) + } + return treeStore, nil +} + +// updateMerkleTrees updates all of the merkle trees that TreeStore manages. +// * it returns an hash of the output or an error. +func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, height uint64) (string, error) { + // TECHDEBT: Interop with the smt Commit functionality for rollbacks. + // Update each of the merkle trees in the list of trees + // TODO: loop through the trees, update each & commit; Trigger rollback if any fail to update. + for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { + switch treeType { + // Actor Merkle Trees + case appMerkleTree, valMerkleTree, fishMerkleTree, servicerMerkleTree: + actorType, ok := merkleTreeToActorTypeName[treeType] + if !ok { + return "", fmt.Errorf("no actor type found for merkle tree: %v", treeType) + } + + actors, err := t.getActorsUpdated(pgtx, actorType, height) + if err != nil { + return "", fmt.Errorf("failed to get actors at height: %w", err) + } + + if err := t.updateActorsTree(actorType, actors); err != nil { + return "", fmt.Errorf("failed to update actors tree for treeType: %v, actorType: %v - %w", treeType, actorType, err) + } + + // Account Merkle Trees + case accountMerkleTree: + accounts, err := t.getAccounts(pgtx) + if err != nil { + return "", fmt.Errorf("failed to get accounts: %w", err) + } + if err := t.updateAccountTrees(accounts); err != nil { + return "", fmt.Errorf("failed to update account trees: %w", err) + } + case poolMerkleTree: + pools, err := t.getPools(pgtx) + if err != nil { + return "", fmt.Errorf("failed to get transactions: %w", err) + } + if err := t.updatePoolTrees(pools); err != nil { + return "", fmt.Errorf("failed to update pool trees - %w", err) + } + + // Data Merkle Trees + case transactionsMerkleTree: + indexedTxs, err := t.getTransactions(pgtx) + if err != nil { + return "", fmt.Errorf("failed to get transactions: %w", err) + } + if err := t.updateTransactionsTree(indexedTxs); err != nil { + return "", fmt.Errorf("failed to update transactions: %w", err) + } + case paramsMerkleTree: + params, err := t.getParams(pgtx) + if err != nil { + return "", fmt.Errorf("failed to get params: %w", err) + } + if err := t.updateParamsTree(params); err != nil { + return "", fmt.Errorf("failed to update params tree: %w", err) + } + case flagsMerkleTree: + flags, err := t.getFlags(pgtx) + if err != nil { + return "", fmt.Errorf("failed to get flags from transaction: %w", err) + } + if err := t.updateFlagsTree(flags); err != nil { + return "", fmt.Errorf("failed to update flags tree - %w", err) + } + // Default + default: + // t.logger.Fatal().Msgf("Not handled yet in state commitment update. Merkle tree #{%v}", treeType) + } + } + + return t.getStateHash(), nil +} + +func (t *treeStore) getStateHash() string { + // Get the root of each Merkle Tree + roots := make([][]byte, 0) + for tree := merkleTree(0); tree < numMerkleTrees; tree++ { + roots = append(roots, t.merkleTrees[tree].Root()) + } + + // Get the state hash + rootsConcat := bytes.Join(roots, []byte{}) + stateHash := sha256.Sum256(rootsConcat) + + // Convert the array to a slice and return it + return hex.EncodeToString(stateHash[:]) +} + +// Actor Tree Helpers + +// NB: I think this needs to be done manually for all 4 types. +func (t *treeStore) updateActorsTree(actorType coreTypes.ActorType, actors []*coreTypes.Actor) error { + for _, actor := range actors { + bzAddr, err := hex.DecodeString(actor.GetAddress()) + if err != nil { + return err + } + + actorBz, err := codec.GetCodec().Marshal(actor) + if err != nil { + return err + } + + merkleTreeName, ok := actorTypeToMerkleTreeName[actorType] + if !ok { + return fmt.Errorf("no merkle tree found for actor type: %s", actorType) + } + if _, err := t.merkleTrees[merkleTreeName].Update(bzAddr, actorBz); err != nil { + return err + } + } + + return nil +} + +// Account Tree Helpers + +func (t *treeStore) updateAccountTrees(accounts []*coreTypes.Account) error { + for _, account := range accounts { + bzAddr, err := hex.DecodeString(account.GetAddress()) + if err != nil { + return err + } + + accBz, err := codec.GetCodec().Marshal(account) + if err != nil { + return err + } + + if _, err := t.merkleTrees[accountMerkleTree].Update(bzAddr, accBz); err != nil { + return err + } + } + + return nil +} + +func (t *treeStore) updatePoolTrees(pools []*coreTypes.Account) error { + for _, pool := range pools { + bzAddr, err := hex.DecodeString(pool.GetAddress()) + if err != nil { + return err + } + + accBz, err := codec.GetCodec().Marshal(pool) + if err != nil { + return err + } + + if _, err := t.merkleTrees[poolMerkleTree].Update(bzAddr, accBz); err != nil { + return err + } + } + + return nil +} + +// Data Tree Helpers + +func (t *treeStore) updateTransactionsTree(indexedTxs []*coreTypes.IndexedTransaction) error { + for _, idxTx := range indexedTxs { + txBz := idxTx.GetTx() + txHash := crypto.SHA3Hash(txBz) + if _, err := t.merkleTrees[transactionsMerkleTree].Update(txHash, txBz); err != nil { + return err + } + } + + return nil +} + +func (t *treeStore) updateParamsTree(params []*coreTypes.Param) error { + for _, param := range params { + paramBz, err := codec.GetCodec().Marshal(param) + paramKey := crypto.SHA3Hash([]byte(param.Name)) + if err != nil { + return err + } + if _, err := t.merkleTrees[paramsMerkleTree].Update(paramKey, paramBz); err != nil { + return err + } + } + + return nil +} + +func (t *treeStore) updateFlagsTree(flags []*coreTypes.Flag) error { + for _, flag := range flags { + flagBz, err := codec.GetCodec().Marshal(flag) + flagKey := crypto.SHA3Hash([]byte(flag.Name)) + if err != nil { + return err + } + if _, err := t.merkleTrees[flagsMerkleTree].Update(flagKey, flagBz); err != nil { + return err + } + } + + return nil +} + +func (t *treeStore) getActorsUpdated(pgtx pgx.Tx, actorType coreTypes.ActorType, height uint64) ([]*coreTypes.Actor, error) { + actorSchema, ok := actorTypeToSchemaName[actorType] + if !ok { + return nil, fmt.Errorf("no schema found for actor type: %s", actorType) + } + + // TODO: this height usage is gross and could cause issues, we should use + // uint64 in the source so that this doesn't have to cast + query := actorSchema.GetUpdatedAtHeightQuery(int64(height)) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, err + } + defer rows.Close() + + addrs := make([][]byte, 0) + for rows.Next() { + var addr string + if err := rows.Scan(&addr); err != nil { + return nil, err + } + addrBz, err := hex.DecodeString(addr) + if err != nil { + return nil, err + } + addrs = append(addrs, addrBz) + } + + actors := make([]*coreTypes.Actor, len(addrs)) + for i, addr := range addrs { + // TODO same goes here for int64 height cast here + actor, err := t.getActor(pgtx, actorSchema, addr, int64(height)) + if err != nil { + return nil, err + } + actors[i] = actor + } + rows.Close() + + return actors, nil +} + +func (t *treeStore) getTransactions(pgtx pgx.Tx) ([]*coreTypes.IndexedTransaction, error) { + return nil, fmt.Errorf("not impl") +} + +func (t *treeStore) getPools(pgtx pgx.Tx) ([]*coreTypes.Account, error) { + return nil, fmt.Errorf("not impl") +} + +func (t *treeStore) getAccounts(pgtx pgx.Tx) ([]*coreTypes.Account, error) { + return nil, fmt.Errorf("not impl") +} + +func (t *treeStore) getFlags(pgtx pgx.Tx) ([]*coreTypes.Flag, error) { + return nil, fmt.Errorf("not impl") +} + +func (t *treeStore) getParams(pgtx pgx.Tx) ([]*coreTypes.Param, error) { + return nil, fmt.Errorf("not impl") +} + +func (t *treeStore) getActor(tx pgx.Tx, actorSchema types.ProtocolActorSchema, address []byte, height int64) (actor *coreTypes.Actor, err error) { + ctx := context.TODO() + actor, height, err = t.getActorFromRow(actorSchema.GetActorType(), tx.QueryRow(ctx, actorSchema.GetQuery(hex.EncodeToString(address), height))) + if err != nil { + return + } + return t.getChainsForActor(ctx, tx, actorSchema, actor, height) +} + +func (t *treeStore) getActorFromRow(actorType coreTypes.ActorType, row pgx.Row) (actor *coreTypes.Actor, height int64, err error) { + actor = &coreTypes.Actor{ + ActorType: actorType, + } + err = row.Scan( + &actor.Address, + &actor.PublicKey, + &actor.StakedAmount, + &actor.ServiceUrl, + &actor.Output, + &actor.PausedHeight, + &actor.UnstakingHeight, + &height) + return +} + +func (t *treeStore) getChainsForActor( + ctx context.Context, + tx pgx.Tx, + actorSchema types.ProtocolActorSchema, + actor *coreTypes.Actor, + height int64, +) (a *coreTypes.Actor, err error) { + if actorSchema.GetChainsTableName() == "" { + return actor, nil + } + rows, err := tx.Query(ctx, actorSchema.GetChainsQuery(actor.Address, height)) + if err != nil { + return actor, err + } + defer rows.Close() + + var chainAddr string + var chainID string + var chainEndHeight int64 // unused + for rows.Next() { + err = rows.Scan(&chainAddr, &chainID, &chainEndHeight) + if err != nil { + return + } + if chainAddr != actor.Address { + return actor, fmt.Errorf("unexpected address %s, expected %s when reading chains", chainAddr, actor.Address) + } + actor.Chains = append(actor.Chains, chainID) + } + return actor, nil +} + +// clearAllTreeState was used by the persistence context for debugging but should +// be handled here in the TreeStore instead of the level above. +func (t *treeStore) ClearAll() error { + // for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { + // valueStore := p.stateTrees.valueStores[treeType] + // nodeStore := p.stateTrees.nodeStores[treeType] + + // if err := valueStore.ClearAll(); err != nil { + // return err + // } + // if err := nodeStore.ClearAll(); err != nil { + // return err + // } + + // // Needed in order to make sure the root is re-set correctly after clearing + // p.stateTrees.merkleTrees[treeType] = smt.NewSparseMerkleTree(valueStore, nodeStore, sha256.New()) + // } + return fmt.Errorf("not impl") +} diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index 9cb0c837c..5fbecc039 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -5,6 +5,7 @@ package modules import ( "github.com/pokt-network/pocket/persistence/blockstore" "github.com/pokt-network/pocket/persistence/indexer" + "github.com/pokt-network/pocket/persistence/trees" "github.com/pokt-network/pocket/runtime/genesis" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/messaging" @@ -23,6 +24,10 @@ type PersistenceModule interface { // BlockStore operations GetBlockStore() blockstore.BlockStore + + // TreeStore operations + GetTreeStore() trees.TreeStore + NewWriteContext() PersistenceRWContext // Indexer operations From 0fa39843ab1086997e31d628720bee32d191c128 Mon Sep 17 00:00:00 2001 From: shakezula Date: Thu, 1 Jun 2023 14:41:09 -0600 Subject: [PATCH 02/24] [WIP] Refactoring to use SMT trees --- persistence/context.go | 3 +- persistence/module.go | 12 ++- persistence/test/account_test.go | 3 +- persistence/test/actor_test.go | 11 +-- persistence/test/application_test.go | 13 +-- persistence/test/fisherman_test.go | 13 +-- persistence/test/generic_test.go | 11 +-- persistence/test/setup_test.go | 13 +-- persistence/test/treestore_test.go | 8 -- persistence/test/validator_test.go | 13 +-- persistence/trees/trees.go | 87 ++++++++----------- runtime/test_artifacts/generator.go | 3 +- .../modules/gomock_reflect_1203808572/prog.go | 66 ++++++++++++++ shared/modules/persistence_module.go | 19 +++- 14 files changed, 170 insertions(+), 105 deletions(-) delete mode 100644 persistence/test/treestore_test.go create mode 100644 shared/modules/gomock_reflect_1203808572/prog.go diff --git a/persistence/context.go b/persistence/context.go index 50f591866..940bd537c 100644 --- a/persistence/context.go +++ b/persistence/context.go @@ -10,7 +10,6 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/pokt-network/pocket/persistence/blockstore" "github.com/pokt-network/pocket/persistence/indexer" - "github.com/pokt-network/pocket/persistence/trees" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/modules" ) @@ -32,7 +31,7 @@ type PostgresContext struct { // Need to simply access them via the bus. blockStore blockstore.BlockStore txIndexer indexer.TxIndexer - stateTrees trees.TreeStore + stateTrees modules.TreeStore networkId string } diff --git a/persistence/module.go b/persistence/module.go index a1ff7f3fd..3ff9920d7 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -42,9 +42,11 @@ type persistenceModule struct { // tx merkle tree. txIndexer indexer.TxIndexer - // A list of all the merkle trees maintained by the persistence module that roll up into the state commitment. + // TODO_IN_THIS_COMMIT updates this comment + // A list of all the merkle trees maintained by the + // persistence module that roll up into the state commitment. // stateTrees *stateTrees - stateTrees trees.TreeStore + stateTrees modules.TreeStore // Only one write context is allowed at a time writeContext *PostgresContext @@ -104,7 +106,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio return nil, err } - stateTrees, err := trees.NewtreeStore(persistenceCfg.TreesStoreDir) + stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir) if err != nil { return nil, err } @@ -228,15 +230,17 @@ func (m *persistenceModule) ReleaseWriteContext() error { return nil } +// TECHDEBT: declare BlockStore interface in shared/modules func (m *persistenceModule) GetBlockStore() blockstore.BlockStore { return m.blockStore } +// TECHDEBT: declare TxIndexer interface in shared/modules func (m *persistenceModule) GetTxIndexer() indexer.TxIndexer { return m.txIndexer } -func (m *persistenceModule) GetTreeStore() trees.TreeStore { +func (m *persistenceModule) GetTreeStore() modules.TreeStore { return m.stateTrees } diff --git a/persistence/test/account_test.go b/persistence/test/account_test.go index b75694cea..ec121efca 100644 --- a/persistence/test/account_test.go +++ b/persistence/test/account_test.go @@ -8,12 +8,13 @@ import ( "math/rand" "testing" + "github.com/stretchr/testify/require" + "github.com/pokt-network/pocket/persistence" "github.com/pokt-network/pocket/runtime/test_artifacts/keygen" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/pocket/shared/utils" - "github.com/stretchr/testify/require" ) func FuzzAccountAmount(f *testing.F) { diff --git a/persistence/test/actor_test.go b/persistence/test/actor_test.go index b4d1d00cc..89ff4afb4 100644 --- a/persistence/test/actor_test.go +++ b/persistence/test/actor_test.go @@ -3,8 +3,9 @@ package test import ( "testing" - "github.com/pokt-network/pocket/shared/core/types" "github.com/stretchr/testify/require" + + coreTypes "github.com/pokt-network/pocket/shared/core/types" ) func TestGetAllStakedActors(t *testing.T) { @@ -21,13 +22,13 @@ func TestGetAllStakedActors(t *testing.T) { actualFishermen := 0 for _, actor := range actors { switch actor.ActorType { - case types.ActorType_ACTOR_TYPE_VAL: + case coreTypes.ActorType_ACTOR_TYPE_VAL: actualValidators++ - case types.ActorType_ACTOR_TYPE_SERVICER: + case coreTypes.ActorType_ACTOR_TYPE_SERVICER: actualServicers++ - case types.ActorType_ACTOR_TYPE_APP: + case coreTypes.ActorType_ACTOR_TYPE_APP: actualApplications++ - case types.ActorType_ACTOR_TYPE_FISH: + case coreTypes.ActorType_ACTOR_TYPE_FISH: actualFishermen++ } } diff --git a/persistence/test/application_test.go b/persistence/test/application_test.go index ebf02a200..7e99920d5 100644 --- a/persistence/test/application_test.go +++ b/persistence/test/application_test.go @@ -5,23 +5,24 @@ import ( "log" "testing" + "github.com/stretchr/testify/require" + "github.com/pokt-network/pocket/persistence" - "github.com/pokt-network/pocket/persistence/types" + ptypes "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" - "github.com/stretchr/testify/require" ) func FuzzApplication(f *testing.F) { fuzzSingleProtocolActor(f, - newTestGenericActor(types.ApplicationActor, newTestApp), - getGenericActor(types.ApplicationActor, getTestApp), - types.ApplicationActor) + newTestGenericActor(ptypes.ApplicationActor, newTestApp), + getGenericActor(ptypes.ApplicationActor, getTestApp), + ptypes.ApplicationActor) } func TestGetApplicationsUpdatedAtHeight(t *testing.T) { getApplicationsUpdatedFunc := func(db *persistence.PostgresContext, height int64) ([]*coreTypes.Actor, error) { - return db.GetActorsUpdated(types.ApplicationActor, height) + return db.GetActorsUpdated(ptypes.ApplicationActor, height) } getAllActorsUpdatedAtHeightTest(t, createAndInsertDefaultTestApp, getApplicationsUpdatedFunc, 1) } diff --git a/persistence/test/fisherman_test.go b/persistence/test/fisherman_test.go index ee3565bf4..a3a245f37 100644 --- a/persistence/test/fisherman_test.go +++ b/persistence/test/fisherman_test.go @@ -5,18 +5,19 @@ import ( "log" "testing" + "github.com/stretchr/testify/require" + "github.com/pokt-network/pocket/persistence" - "github.com/pokt-network/pocket/persistence/types" + ptypes "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" - "github.com/stretchr/testify/require" ) func FuzzFisherman(f *testing.F) { fuzzSingleProtocolActor(f, - newTestGenericActor(types.FishermanActor, newTestFisherman), - getGenericActor(types.FishermanActor, getTestFisherman), - types.FishermanActor) + newTestGenericActor(ptypes.FishermanActor, newTestFisherman), + getGenericActor(ptypes.FishermanActor, getTestFisherman), + ptypes.FishermanActor) } func TestGetSetFishermanStakeAmount(t *testing.T) { @@ -26,7 +27,7 @@ func TestGetSetFishermanStakeAmount(t *testing.T) { func TestGetFishermanUpdatedAtHeight(t *testing.T) { getFishermanUpdatedFunc := func(db *persistence.PostgresContext, height int64) ([]*coreTypes.Actor, error) { - return db.GetActorsUpdated(types.FishermanActor, height) + return db.GetActorsUpdated(ptypes.FishermanActor, height) } getAllActorsUpdatedAtHeightTest(t, createAndInsertDefaultTestFisherman, getFishermanUpdatedFunc, 1) } diff --git a/persistence/test/generic_test.go b/persistence/test/generic_test.go index 59b86bcbf..557ea091e 100644 --- a/persistence/test/generic_test.go +++ b/persistence/test/generic_test.go @@ -5,14 +5,15 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" + "github.com/pokt-network/pocket/persistence" - "github.com/pokt-network/pocket/persistence/types" + ptypes "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/stretchr/testify/require" ) func getGenericActor[T any]( - protocolActorSchema types.ProtocolActorSchema, + protocolActorSchema ptypes.ProtocolActorSchema, getActor func(*persistence.PostgresContext, []byte) (T, error), ) func(*persistence.PostgresContext, string) (*coreTypes.Actor, error) { return func(db *persistence.PostgresContext, address string) (*coreTypes.Actor, error) { @@ -29,7 +30,7 @@ func getGenericActor[T any]( } } -func newTestGenericActor[T any](protocolActorSchema types.ProtocolActorSchema, newActor func() (T, error)) func() (*coreTypes.Actor, error) { +func newTestGenericActor[T any](protocolActorSchema ptypes.ProtocolActorSchema, newActor func() (T, error)) func() (*coreTypes.Actor, error) { return func() (*coreTypes.Actor, error) { actor, err := newActor() if err != nil { @@ -201,7 +202,7 @@ func getAllActorsUpdatedAtHeightTest[T any]( require.Equal(t, 1, len(accs)) } -func getActorValues(_ types.ProtocolActorSchema, actorValue reflect.Value) *coreTypes.Actor { +func getActorValues(_ ptypes.ProtocolActorSchema, actorValue reflect.Value) *coreTypes.Actor { chains := make([]string, 0) if actorValue.FieldByName("Chains").Kind() != 0 { chains = actorValue.FieldByName("Chains").Interface().([]string) diff --git a/persistence/test/setup_test.go b/persistence/test/setup_test.go index f8d549412..70ac0603b 100644 --- a/persistence/test/setup_test.go +++ b/persistence/test/setup_test.go @@ -11,8 +11,11 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" + "github.com/pokt-network/pocket/persistence" - "github.com/pokt-network/pocket/persistence/types" + ptypes "github.com/pokt-network/pocket/persistence/types" "github.com/pokt-network/pocket/runtime" "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/runtime/test_artifacts" @@ -22,8 +25,6 @@ import ( "github.com/pokt-network/pocket/shared/modules" moduleTypes "github.com/pokt-network/pocket/shared/modules/types" "github.com/pokt-network/pocket/shared/utils" - "github.com/stretchr/testify/require" - "golang.org/x/exp/slices" ) var ( @@ -139,7 +140,7 @@ func fuzzSingleProtocolActor( f *testing.F, newTestActor func() (*coreTypes.Actor, error), getTestActor func(db *persistence.PostgresContext, address string) (*coreTypes.Actor, error), - protocolActorSchema types.ProtocolActorSchema, + protocolActorSchema ptypes.ProtocolActorSchema, ) { // Clear the genesis state. clearAllState() @@ -193,9 +194,9 @@ func fuzzSingleProtocolActor( newStakedTokens = getRandomBigIntString() case 1: switch protocolActorSchema.GetActorSpecificColName() { - case types.ServiceURLCol: + case ptypes.ServiceURLCol: serviceUrl = getRandomServiceURL() - case types.UnusedCol: + case ptypes.UnusedCol: serviceUrl = "" default: t.Error("Unexpected actor specific column name") diff --git a/persistence/test/treestore_test.go b/persistence/test/treestore_test.go deleted file mode 100644 index 90a9eaf48..000000000 --- a/persistence/test/treestore_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package test - -import ( - "testing" -) - -func TestTreestoreUpdate(t *testing.T) { -} diff --git a/persistence/test/validator_test.go b/persistence/test/validator_test.go index bc3182d9e..c35b67a07 100644 --- a/persistence/test/validator_test.go +++ b/persistence/test/validator_test.go @@ -5,18 +5,19 @@ import ( "log" "testing" + "github.com/stretchr/testify/require" + "github.com/pokt-network/pocket/persistence" - "github.com/pokt-network/pocket/persistence/types" + ptypes "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" - "github.com/stretchr/testify/require" ) func FuzzValidator(f *testing.F) { fuzzSingleProtocolActor(f, - newTestGenericActor(types.ValidatorActor, newTestValidator), - getGenericActor(types.ValidatorActor, getTestValidator), - types.ValidatorActor) + newTestGenericActor(ptypes.ValidatorActor, newTestValidator), + getGenericActor(ptypes.ValidatorActor, getTestValidator), + ptypes.ValidatorActor) } func TestGetSetValidatorStakeAmount(t *testing.T) { @@ -26,7 +27,7 @@ func TestGetSetValidatorStakeAmount(t *testing.T) { func TestGetValidatorUpdatedAtHeight(t *testing.T) { getValidatorsUpdatedFunc := func(db *persistence.PostgresContext, height int64) ([]*coreTypes.Actor, error) { - return db.GetActorsUpdated(types.ValidatorActor, height) + return db.GetActorsUpdated(ptypes.ValidatorActor, height) } getAllActorsUpdatedAtHeightTest(t, createAndInsertDefaultTestValidator, getValidatorsUpdatedFunc, 5) } diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index e50f2d7d9..f40ba3c01 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -8,21 +8,15 @@ import ( "fmt" "github.com/jackc/pgx/v5" + "github.com/pokt-network/pocket/persistence/kvstore" - "github.com/pokt-network/pocket/persistence/types" + ptypes "github.com/pokt-network/pocket/persistence/types" "github.com/pokt-network/pocket/shared/codec" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" "github.com/pokt-network/smt" ) -type TreeStore interface { - Update(pgtx pgx.Tx, height uint64) (string, error) - // ClearAll completely clears the state of the trees. - // For debugging purposes only. - ClearAll() error -} - var merkleTreeToString = map[merkleTree]string{ appMerkleTree: "app", valMerkleTree: "val", @@ -44,11 +38,11 @@ var actorTypeToMerkleTreeName = map[coreTypes.ActorType]merkleTree{ coreTypes.ActorType_ACTOR_TYPE_SERVICER: servicerMerkleTree, } -var actorTypeToSchemaName = map[coreTypes.ActorType]types.ProtocolActorSchema{ - coreTypes.ActorType_ACTOR_TYPE_APP: types.ApplicationActor, - coreTypes.ActorType_ACTOR_TYPE_VAL: types.ValidatorActor, - coreTypes.ActorType_ACTOR_TYPE_FISH: types.FishermanActor, - coreTypes.ActorType_ACTOR_TYPE_SERVICER: types.ServicerActor, +var actorTypeToSchemaName = map[coreTypes.ActorType]ptypes.ProtocolActorSchema{ + coreTypes.ActorType_ACTOR_TYPE_APP: ptypes.ApplicationActor, + coreTypes.ActorType_ACTOR_TYPE_VAL: ptypes.ValidatorActor, + coreTypes.ActorType_ACTOR_TYPE_FISH: ptypes.FishermanActor, + coreTypes.ActorType_ACTOR_TYPE_SERVICER: ptypes.ServicerActor, } var merkleTreeToActorTypeName = map[merkleTree]coreTypes.ActorType{ @@ -86,35 +80,33 @@ const ( ) // treeStore stores a set of merkle trees that -// it manages. +// it manages. It fulfills the modules.TreeStore interface. // * It is responsible for commit or rollback behavior // of the underlying trees by utilizing the lazy loading // functionality provided by the underlying smt library. type treeStore struct { - merkleTrees map[merkleTree]*smt.SparseMerkleTree + merkleTrees map[merkleTree]*smt.SMT - // nodeStores & valueStore are part of the SMT, but references are kept below for convenience + // nodeStores are part of the SMT, but references are kept below for convenience // and debugging purposes - nodeStores map[merkleTree]kvstore.KVStore - valueStores map[merkleTree]kvstore.KVStore + nodeStores map[merkleTree]kvstore.KVStore } // Update takes a transaction and a height and updates // all of the trees in the treeStore for that height. func (t *treeStore) Update(pgtx pgx.Tx, height uint64) (string, error) { - hash, err := t.updateMerkleTrees(pgtx, height) - return hash, err + // NB: Consider not wrapping this + return t.updateMerkleTrees(pgtx, height) } -func NewtreeStore(treesStoreDir string) (*treeStore, error) { +func NewStateTrees(treesStoreDir string) (*treeStore, error) { if treesStoreDir == ":memory:" { - return newMemtreeStore() + return newMemStateTrees() } - treeStore := &treeStore{ - merkleTrees: make(map[merkleTree]*smt.SparseMerkleTree, int(numMerkleTrees)), + stateTrees := &treeStore{ + merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), - valueStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), } for tree := merkleTree(0); tree < numMerkleTrees; tree++ { @@ -122,38 +114,28 @@ func NewtreeStore(treesStoreDir string) (*treeStore, error) { if err != nil { return nil, err } - valueStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_values", treesStoreDir, merkleTreeToString[tree])) - if err != nil { - return nil, err - } - treeStore.nodeStores[tree] = nodeStore - treeStore.valueStores[tree] = valueStore - treeStore.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, valueStore, sha256.New()) + stateTrees.nodeStores[tree] = nodeStore + stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) } - return treeStore, nil + return stateTrees, nil } -func newMemtreeStore() (*treeStore, error) { - treeStore := &treeStore{ - merkleTrees: make(map[merkleTree]*smt.SparseMerkleTree, int(numMerkleTrees)), +func newMemStateTrees() (*treeStore, error) { + stateTrees := &treeStore{ + merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), - valueStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), } for tree := merkleTree(0); tree < numMerkleTrees; tree++ { nodeStore := kvstore.NewMemKVStore() // For testing, `smt.NewSimpleMap()` can be used as well - valueStore := kvstore.NewMemKVStore() - treeStore.nodeStores[tree] = nodeStore - treeStore.valueStores[tree] = valueStore - treeStore.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, valueStore, sha256.New()) + stateTrees.nodeStores[tree] = nodeStore + stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) } - return treeStore, nil + return stateTrees, nil } // updateMerkleTrees updates all of the merkle trees that TreeStore manages. // * it returns an hash of the output or an error. func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, height uint64) (string, error) { - // TECHDEBT: Interop with the smt Commit functionality for rollbacks. - // Update each of the merkle trees in the list of trees // TODO: loop through the trees, update each & commit; Trigger rollback if any fail to update. for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { switch treeType { @@ -259,7 +241,7 @@ func (t *treeStore) updateActorsTree(actorType coreTypes.ActorType, actors []*co if !ok { return fmt.Errorf("no merkle tree found for actor type: %s", actorType) } - if _, err := t.merkleTrees[merkleTreeName].Update(bzAddr, actorBz); err != nil { + if err := t.merkleTrees[merkleTreeName].Update(bzAddr, actorBz); err != nil { return err } } @@ -281,7 +263,7 @@ func (t *treeStore) updateAccountTrees(accounts []*coreTypes.Account) error { return err } - if _, err := t.merkleTrees[accountMerkleTree].Update(bzAddr, accBz); err != nil { + if err := t.merkleTrees[accountMerkleTree].Update(bzAddr, accBz); err != nil { return err } } @@ -301,7 +283,7 @@ func (t *treeStore) updatePoolTrees(pools []*coreTypes.Account) error { return err } - if _, err := t.merkleTrees[poolMerkleTree].Update(bzAddr, accBz); err != nil { + if err := t.merkleTrees[poolMerkleTree].Update(bzAddr, accBz); err != nil { return err } } @@ -315,7 +297,7 @@ func (t *treeStore) updateTransactionsTree(indexedTxs []*coreTypes.IndexedTransa for _, idxTx := range indexedTxs { txBz := idxTx.GetTx() txHash := crypto.SHA3Hash(txBz) - if _, err := t.merkleTrees[transactionsMerkleTree].Update(txHash, txBz); err != nil { + if err := t.merkleTrees[transactionsMerkleTree].Update(txHash, txBz); err != nil { return err } } @@ -330,7 +312,7 @@ func (t *treeStore) updateParamsTree(params []*coreTypes.Param) error { if err != nil { return err } - if _, err := t.merkleTrees[paramsMerkleTree].Update(paramKey, paramBz); err != nil { + if err := t.merkleTrees[paramsMerkleTree].Update(paramKey, paramBz); err != nil { return err } } @@ -345,7 +327,7 @@ func (t *treeStore) updateFlagsTree(flags []*coreTypes.Flag) error { if err != nil { return err } - if _, err := t.merkleTrees[flagsMerkleTree].Update(flagKey, flagBz); err != nil { + if err := t.merkleTrees[flagsMerkleTree].Update(flagKey, flagBz); err != nil { return err } } @@ -396,6 +378,7 @@ func (t *treeStore) getActorsUpdated(pgtx pgx.Tx, actorType coreTypes.ActorType, } func (t *treeStore) getTransactions(pgtx pgx.Tx) ([]*coreTypes.IndexedTransaction, error) { + // pgtx.Query() return nil, fmt.Errorf("not impl") } @@ -415,7 +398,7 @@ func (t *treeStore) getParams(pgtx pgx.Tx) ([]*coreTypes.Param, error) { return nil, fmt.Errorf("not impl") } -func (t *treeStore) getActor(tx pgx.Tx, actorSchema types.ProtocolActorSchema, address []byte, height int64) (actor *coreTypes.Actor, err error) { +func (t *treeStore) getActor(tx pgx.Tx, actorSchema ptypes.ProtocolActorSchema, address []byte, height int64) (actor *coreTypes.Actor, err error) { ctx := context.TODO() actor, height, err = t.getActorFromRow(actorSchema.GetActorType(), tx.QueryRow(ctx, actorSchema.GetQuery(hex.EncodeToString(address), height))) if err != nil { @@ -443,7 +426,7 @@ func (t *treeStore) getActorFromRow(actorType coreTypes.ActorType, row pgx.Row) func (t *treeStore) getChainsForActor( ctx context.Context, tx pgx.Tx, - actorSchema types.ProtocolActorSchema, + actorSchema ptypes.ProtocolActorSchema, actor *coreTypes.Actor, height int64, ) (a *coreTypes.Actor, err error) { diff --git a/runtime/test_artifacts/generator.go b/runtime/test_artifacts/generator.go index 24b80a2df..02241381e 100644 --- a/runtime/test_artifacts/generator.go +++ b/runtime/test_artifacts/generator.go @@ -10,7 +10,6 @@ import ( "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/runtime/genesis" "github.com/pokt-network/pocket/runtime/test_artifacts/keygen" - "github.com/pokt-network/pocket/shared/core/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" "google.golang.org/protobuf/types/known/timestamppb" @@ -64,7 +63,7 @@ func WithActors(actors []*coreTypes.Actor, actorKeys []string) func(*genesis.Gen genesis.Accounts = append(genesis.Accounts, newActorAccounts...) for _, actor := range actors { switch actor.ActorType { - case types.ActorType_ACTOR_TYPE_APP: + case coreTypes.ActorType_ACTOR_TYPE_APP: genesis.Applications = append(genesis.Applications, actor) case coreTypes.ActorType_ACTOR_TYPE_VAL: genesis.Validators = append(genesis.Validators, actor) diff --git a/shared/modules/gomock_reflect_1203808572/prog.go b/shared/modules/gomock_reflect_1203808572/prog.go new file mode 100644 index 000000000..cd7c65353 --- /dev/null +++ b/shared/modules/gomock_reflect_1203808572/prog.go @@ -0,0 +1,66 @@ + +package main + +import ( + "encoding/gob" + "flag" + "fmt" + "os" + "path" + "reflect" + + "github.com/golang/mock/mockgen/model" + + pkg_ "github.com/pokt-network/pocket/shared/modules" +) + +var output = flag.String("output", "", "The output file name, or empty to use stdout.") + +func main() { + flag.Parse() + + its := []struct{ + sym string + typ reflect.Type + }{ + + { "LoggerModule", reflect.TypeOf((*pkg_.LoggerModule)(nil)).Elem()}, + + } + pkg := &model.Package{ + // NOTE: This behaves contrary to documented behaviour if the + // package name is not the final component of the import path. + // The reflect package doesn't expose the package name, though. + Name: path.Base("github.com/pokt-network/pocket/shared/modules"), + } + + for _, it := range its { + intf, err := model.InterfaceFromInterfaceType(it.typ) + if err != nil { + fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) + os.Exit(1) + } + intf.Name = it.sym + pkg.Interfaces = append(pkg.Interfaces, intf) + } + + outfile := os.Stdout + if len(*output) != 0 { + var err error + outfile, err = os.Create(*output) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) + } + defer func() { + if err := outfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) + os.Exit(1) + } + }() + } + + if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { + fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) + os.Exit(1) + } +} diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index 5fbecc039..745c70b9f 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -3,9 +3,9 @@ package modules //go:generate mockgen -destination=./mocks/persistence_module_mock.go github.com/pokt-network/pocket/shared/modules PersistenceModule,PersistenceRWContext,PersistenceReadContext,PersistenceWriteContext import ( + "github.com/jackc/pgx/v5" "github.com/pokt-network/pocket/persistence/blockstore" "github.com/pokt-network/pocket/persistence/indexer" - "github.com/pokt-network/pocket/persistence/trees" "github.com/pokt-network/pocket/runtime/genesis" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/messaging" @@ -26,7 +26,7 @@ type PersistenceModule interface { GetBlockStore() blockstore.BlockStore // TreeStore operations - GetTreeStore() trees.TreeStore + GetTreeStore() TreeStore NewWriteContext() PersistenceRWContext @@ -38,6 +38,21 @@ type PersistenceModule interface { HandleDebugMessage(*messaging.DebugMessage) error } +type TreeStore interface { + // Update returns the new state hash for a given height. + // + // DOCUMENTATION: This needs to describe how height is passed + // through to the Update function and used by the queries. + // It updates to the future but not to the past. + // * So passing a higher height will cause a change + // but repeatedly calling the same or a lower height will + // not incur a change. + Update(pgtx pgx.Tx, height uint64) (string, error) + // ClearAll completely clears the state of the trees. + // For debugging purposes only. + ClearAll() error +} + // Interface defining the context within which the node can operate with the persistence layer. // Operations in the context of a PersistenceContext are isolated from other operations and // other persistence contexts until committed, enabling parallelizability along other operations. From 377ea5c7141866835a2fc0612893268081d56f6f Mon Sep 17 00:00:00 2001 From: shakezula Date: Mon, 5 Jun 2023 16:05:37 -0600 Subject: [PATCH 03/24] [WIP] adds pool support --- persistence/context.go | 2 +- persistence/module.go | 2 +- persistence/trees/trees.go | 100 +++++++++++++++++++-------- shared/modules/persistence_module.go | 11 +-- 4 files changed, 78 insertions(+), 37 deletions(-) diff --git a/persistence/context.go b/persistence/context.go index 940bd537c..0af52de76 100644 --- a/persistence/context.go +++ b/persistence/context.go @@ -50,7 +50,7 @@ func (p *PostgresContext) RollbackToSavePoint(bytes []byte) error { // IMPROVE(#361): Guarantee the integrity of the state // Full details in the thread from the PR review: https://github.com/pokt-network/pocket/pull/285#discussion_r1018471719 func (p *PostgresContext) ComputeStateHash() (string, error) { - stateHash, err := p.stateTrees.Update(p.tx, uint64(p.Height)) + stateHash, err := p.stateTrees.Update(p.tx, p.txIndexer, uint64(p.Height)) if err != nil { return "", err } diff --git a/persistence/module.go b/persistence/module.go index 3ff9920d7..492876f7b 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -106,7 +106,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio return nil, err } - stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir) + stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir, txIndexer) if err != nil { return nil, err } diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index f40ba3c01..c3303a65f 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -9,6 +9,7 @@ import ( "github.com/jackc/pgx/v5" + "github.com/pokt-network/pocket/persistence/indexer" "github.com/pokt-network/pocket/persistence/kvstore" ptypes "github.com/pokt-network/pocket/persistence/types" "github.com/pokt-network/pocket/shared/codec" @@ -86,17 +87,14 @@ const ( // functionality provided by the underlying smt library. type treeStore struct { merkleTrees map[merkleTree]*smt.SMT - - // nodeStores are part of the SMT, but references are kept below for convenience - // and debugging purposes - nodeStores map[merkleTree]kvstore.KVStore + nodeStores map[merkleTree]kvstore.KVStore } // Update takes a transaction and a height and updates // all of the trees in the treeStore for that height. -func (t *treeStore) Update(pgtx pgx.Tx, height uint64) (string, error) { +func (t *treeStore) Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { // NB: Consider not wrapping this - return t.updateMerkleTrees(pgtx, height) + return t.updateMerkleTrees(pgtx, txi, height) } func NewStateTrees(treesStoreDir string) (*treeStore, error) { @@ -135,7 +133,7 @@ func newMemStateTrees() (*treeStore, error) { // updateMerkleTrees updates all of the merkle trees that TreeStore manages. // * it returns an hash of the output or an error. -func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, height uint64) (string, error) { +func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { // TODO: loop through the trees, update each & commit; Trigger rollback if any fail to update. for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { switch treeType { @@ -165,7 +163,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, height uint64) (string, error return "", fmt.Errorf("failed to update account trees: %w", err) } case poolMerkleTree: - pools, err := t.getPools(pgtx) + pools, err := t.getPools(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get transactions: %w", err) } @@ -175,7 +173,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, height uint64) (string, error // Data Merkle Trees case transactionsMerkleTree: - indexedTxs, err := t.getTransactions(pgtx) + indexedTxs, err := t.getTransactions(pgtx, txi, height) if err != nil { return "", fmt.Errorf("failed to get transactions: %w", err) } @@ -301,7 +299,6 @@ func (t *treeStore) updateTransactionsTree(indexedTxs []*coreTypes.IndexedTransa return err } } - return nil } @@ -335,7 +332,12 @@ func (t *treeStore) updateFlagsTree(flags []*coreTypes.Flag) error { return nil } -func (t *treeStore) getActorsUpdated(pgtx pgx.Tx, actorType coreTypes.ActorType, height uint64) ([]*coreTypes.Actor, error) { +// getActorsUpdated is responsible for fetching the actors that have been updated at a given height. +func (t *treeStore) getActorsUpdated( + pgtx pgx.Tx, + actorType coreTypes.ActorType, + height uint64, +) ([]*coreTypes.Actor, error) { actorSchema, ok := actorTypeToSchemaName[actorType] if !ok { return nil, fmt.Errorf("no schema found for actor type: %s", actorType) @@ -377,13 +379,48 @@ func (t *treeStore) getActorsUpdated(pgtx pgx.Tx, actorType coreTypes.ActorType, return actors, nil } -func (t *treeStore) getTransactions(pgtx pgx.Tx) ([]*coreTypes.IndexedTransaction, error) { - // pgtx.Query() - return nil, fmt.Errorf("not impl") +func (t *treeStore) getAccountsUpdated( + pgtx pgx.Tx, + acctType ptypes.ProtocolAccountSchema, + height uint64, +) ([]*coreTypes.Account, error) { + accounts := []*coreTypes.Account{} + + // TECHDEBT #XXX: Avoid this cast to int64 + query := acctType.GetAccountsUpdatedAtHeightQuery(int64(height)) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + acc := new(coreTypes.Account) + if err := rows.Scan(&acc.Address, &acc.Amount); err != nil { + return nil, err + } + accounts = append(accounts, acc) + } + + return accounts, nil } -func (t *treeStore) getPools(pgtx pgx.Tx) ([]*coreTypes.Account, error) { - return nil, fmt.Errorf("not impl") +func (t *treeStore) getTransactions(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) ([]*coreTypes.IndexedTransaction, error) { + // TECHDEBT(#XXX): TxIndexer should use uint64s so we avoid this cast. + indexedTxs, err := txi.GetByHeight(int64(height), false) + if err != nil { + return nil, fmt.Errorf("failed to get transactions by height: %w", err) + } + return indexedTxs, nil +} + +// getPools returns the pools updated at the given height +func (t *treeStore) getPools(pgtx pgx.Tx, height uint64) ([]*coreTypes.Account, error) { + pools, err := t.getAccountsUpdated(pgtx, ptypes.Pool, height) + if err != nil { + return nil, fmt.Errorf("failed to get pools: %w", err) + } + return pools, nil } func (t *treeStore) getAccounts(pgtx pgx.Tx) ([]*coreTypes.Account, error) { @@ -458,19 +495,22 @@ func (t *treeStore) getChainsForActor( // clearAllTreeState was used by the persistence context for debugging but should // be handled here in the TreeStore instead of the level above. func (t *treeStore) ClearAll() error { - // for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { - // valueStore := p.stateTrees.valueStores[treeType] - // nodeStore := p.stateTrees.nodeStores[treeType] - - // if err := valueStore.ClearAll(); err != nil { - // return err - // } - // if err := nodeStore.ClearAll(); err != nil { - // return err - // } - - // // Needed in order to make sure the root is re-set correctly after clearing - // p.stateTrees.merkleTrees[treeType] = smt.NewSparseMerkleTree(valueStore, nodeStore, sha256.New()) - // } + for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { + // nodeStore := t.nodeStores[treeType] + // tree := t.merkleTrees[treeType] + + // TODO: implement the below + // if err := valueStore.ClearAll(); err != nil { + // return err + // } + // if err := tree.ClearAll(); err != nil { + // return err + // } + + // p.stateTrees.merkleTrees[treeType] = smt.NewSparseMerkleTree(valueStore, nodeStore, sha256.New()) + // TODO: like the line above, create a new set of state tree and assign it to the merkleTrees property + // // This is Needed in order to make sure the root is re-set correctly after clearing + // t.merkleTrees[treeType] = smt.NewSparseMerkleTree() + } return fmt.Errorf("not impl") } diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index 745c70b9f..0e7acb226 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -38,16 +38,17 @@ type PersistenceModule interface { HandleDebugMessage(*messaging.DebugMessage) error } +// TreeStore is fulfilled by the treeStore to create an +// atomic tree component for use by the peristence context. type TreeStore interface { // Update returns the new state hash for a given height. - // - // DOCUMENTATION: This needs to describe how height is passed - // through to the Update function and used by the queries. + // * Height is passed through to the Update function + // and used by the queries against the TxIndexer and the // It updates to the future but not to the past. - // * So passing a higher height will cause a change + // * Passing a higher height will cause a change // but repeatedly calling the same or a lower height will // not incur a change. - Update(pgtx pgx.Tx, height uint64) (string, error) + Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) // ClearAll completely clears the state of the trees. // For debugging purposes only. ClearAll() error From afbbcfde2f5af29bdbe0517d8ccd7589b1401ea3 Mon Sep 17 00:00:00 2001 From: shakezula Date: Mon, 5 Jun 2023 17:09:19 -0600 Subject: [PATCH 04/24] [WIP] add accounts and params support --- persistence/trees/trees.go | 60 +++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index c3303a65f..21431f2d7 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -155,7 +155,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height // Account Merkle Trees case accountMerkleTree: - accounts, err := t.getAccounts(pgtx) + accounts, err := t.getAccounts(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get accounts: %w", err) } @@ -181,7 +181,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height return "", fmt.Errorf("failed to update transactions: %w", err) } case paramsMerkleTree: - params, err := t.getParams(pgtx) + params, err := t.getParams(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get params: %w", err) } @@ -189,7 +189,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height return "", fmt.Errorf("failed to update params tree: %w", err) } case flagsMerkleTree: - flags, err := t.getFlags(pgtx) + flags, err := t.getFlags(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get flags from transaction: %w", err) } @@ -423,16 +423,57 @@ func (t *treeStore) getPools(pgtx pgx.Tx, height uint64) ([]*coreTypes.Account, return pools, nil } -func (t *treeStore) getAccounts(pgtx pgx.Tx) ([]*coreTypes.Account, error) { - return nil, fmt.Errorf("not impl") +// getAccounts returns the list of accounts updated at the provided height +func (t *treeStore) getAccounts(pgtx pgx.Tx, height uint64) ([]*coreTypes.Account, error) { + accounts, err := t.getAccountsUpdated(pgtx, ptypes.Account, height) + if err != nil { + return nil, fmt.Errorf("failed to get accounts: %w", err) + } + return accounts, nil } -func (t *treeStore) getFlags(pgtx pgx.Tx) ([]*coreTypes.Flag, error) { - return nil, fmt.Errorf("not impl") +func (t *treeStore) getFlags(pgtx pgx.Tx, height uint64) ([]*coreTypes.Flag, error) { + fields := "name,value,enabled" + query := fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, ptypes.FlagsTableName, height) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, fmt.Errorf("failed to get flags: %w", err) + } + defer rows.Close() + + flagSlice := []*coreTypes.Flag{} + for rows.Next() { + flag := new(coreTypes.Flag) + if err := rows.Scan(&flag.Name, &flag.Value, &flag.Enabled); err != nil { + return nil, err + } + flag.Height = int64(height) + flagSlice = append(flagSlice, flag) + } + + return flagSlice, nil } -func (t *treeStore) getParams(pgtx pgx.Tx) ([]*coreTypes.Param, error) { - return nil, fmt.Errorf("not impl") +func (t *treeStore) getParams(pgtx pgx.Tx, height uint64) ([]*coreTypes.Param, error) { + fields := "name,value" + // Return a query to select all params or queries but only the most recent update for each + query := fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, ptypes.ParamsTableName, height) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, err + } + defer rows.Close() + + var paramSlice []*coreTypes.Param // Store returned rows + for rows.Next() { + param := new(coreTypes.Param) + if err := rows.Scan(¶m.Name, ¶m.Value); err != nil { + return nil, err + } + param.Height = int64(height) + paramSlice = append(paramSlice, param) + } + return paramSlice, nil } func (t *treeStore) getActor(tx pgx.Tx, actorSchema ptypes.ProtocolActorSchema, address []byte, height int64) (actor *coreTypes.Actor, err error) { @@ -498,7 +539,6 @@ func (t *treeStore) ClearAll() error { for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { // nodeStore := t.nodeStores[treeType] // tree := t.merkleTrees[treeType] - // TODO: implement the below // if err := valueStore.ClearAll(); err != nil { // return err From 25af7d437e6fcb09f3a8b6550f79d33801db8010 Mon Sep 17 00:00:00 2001 From: shakezula Date: Mon, 5 Jun 2023 17:58:58 -0600 Subject: [PATCH 05/24] [WIP] implement ClearAll for treeStore --- persistence/trees/trees.go | 77 +++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index 21431f2d7..74f16f2ca 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -86,14 +86,14 @@ const ( // of the underlying trees by utilizing the lazy loading // functionality provided by the underlying smt library. type treeStore struct { - merkleTrees map[merkleTree]*smt.SMT - nodeStores map[merkleTree]kvstore.KVStore + treeStoreDir string + merkleTrees map[merkleTree]*smt.SMT + nodeStores map[merkleTree]kvstore.KVStore } // Update takes a transaction and a height and updates // all of the trees in the treeStore for that height. func (t *treeStore) Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { - // NB: Consider not wrapping this return t.updateMerkleTrees(pgtx, txi, height) } @@ -103,12 +103,13 @@ func NewStateTrees(treesStoreDir string) (*treeStore, error) { } stateTrees := &treeStore{ - merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), - nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), + treeStoreDir: dir, + merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), + nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), } for tree := merkleTree(0); tree < numMerkleTrees; tree++ { - nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", treesStoreDir, merkleTreeToString[tree])) + nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", dir, merkleTreeToString[tree])) if err != nil { return nil, err } @@ -134,7 +135,6 @@ func newMemStateTrees() (*treeStore, error) { // updateMerkleTrees updates all of the merkle trees that TreeStore manages. // * it returns an hash of the output or an error. func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { - // TODO: loop through the trees, update each & commit; Trigger rollback if any fail to update. for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { switch treeType { // Actor Merkle Trees @@ -202,25 +202,40 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height } } + if err := t.commit(); err != nil { + return "", fmt.Errorf("failed to commit: %w", err) + } return t.getStateHash(), nil } +func (t *treeStore) commit() error { + for tree := merkleTree(0); tree < numMerkleTrees; tree++ { + if err := t.merkleTrees[tree].Commit(); err != nil { + return fmt.Errorf("failed to commit %s: %w", merkleTreeToString[tree]) + } + } + return nil +} + func (t *treeStore) getStateHash() string { - // Get the root of each Merkle Tree + // create an order-matters list of roots roots := make([][]byte, 0) for tree := merkleTree(0); tree < numMerkleTrees; tree++ { roots = append(roots, t.merkleTrees[tree].Root()) } - // Get the state hash + // combine them and hash the result rootsConcat := bytes.Join(roots, []byte{}) stateHash := sha256.Sum256(rootsConcat) // Convert the array to a slice and return it + // REF: https://stackoverflow.com/questions/28886616/convert-array-to-slice-in-go return hex.EncodeToString(stateHash[:]) } -// Actor Tree Helpers +//////////////////////// +// Actor Tree Helpers // +//////////////////////// // NB: I think this needs to be done manually for all 4 types. func (t *treeStore) updateActorsTree(actorType coreTypes.ActorType, actors []*coreTypes.Actor) error { @@ -247,7 +262,9 @@ func (t *treeStore) updateActorsTree(actorType coreTypes.ActorType, actors []*co return nil } -// Account Tree Helpers +////////////////////////// +// Account Tree Helpers // +////////////////////////// func (t *treeStore) updateAccountTrees(accounts []*coreTypes.Account) error { for _, account := range accounts { @@ -289,7 +306,9 @@ func (t *treeStore) updatePoolTrees(pools []*coreTypes.Account) error { return nil } -// Data Tree Helpers +/////////////////////// +// Data Tree Helpers // +/////////////////////// func (t *treeStore) updateTransactionsTree(indexedTxs []*coreTypes.IndexedTransaction) error { for _, idxTx := range indexedTxs { @@ -367,7 +386,7 @@ func (t *treeStore) getActorsUpdated( actors := make([]*coreTypes.Actor, len(addrs)) for i, addr := range addrs { - // TODO same goes here for int64 height cast here + // TECHDEBT #XXX: Avoid this cast to int64 actor, err := t.getActor(pgtx, actorSchema, addr, int64(height)) if err != nil { return nil, err @@ -456,7 +475,6 @@ func (t *treeStore) getFlags(pgtx pgx.Tx, height uint64) ([]*coreTypes.Flag, err func (t *treeStore) getParams(pgtx pgx.Tx, height uint64) ([]*coreTypes.Param, error) { fields := "name,value" - // Return a query to select all params or queries but only the most recent update for each query := fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, ptypes.ParamsTableName, height) rows, err := pgtx.Query(context.TODO(), query) if err != nil { @@ -464,7 +482,7 @@ func (t *treeStore) getParams(pgtx pgx.Tx, height uint64) ([]*coreTypes.Param, e } defer rows.Close() - var paramSlice []*coreTypes.Param // Store returned rows + var paramSlice []*coreTypes.Param for rows.Next() { param := new(coreTypes.Param) if err := rows.Scan(¶m.Name, ¶m.Value); err != nil { @@ -473,6 +491,7 @@ func (t *treeStore) getParams(pgtx pgx.Tx, height uint64) ([]*coreTypes.Param, e param.Height = int64(height) paramSlice = append(paramSlice, param) } + return paramSlice, nil } @@ -519,7 +538,7 @@ func (t *treeStore) getChainsForActor( var chainAddr string var chainID string - var chainEndHeight int64 // unused + var chainEndHeight int64 // DISCUSS: why is this commented as "unused"? for rows.Next() { err = rows.Scan(&chainAddr, &chainID, &chainEndHeight) if err != nil { @@ -533,24 +552,14 @@ func (t *treeStore) getChainsForActor( return actor, nil } -// clearAllTreeState was used by the persistence context for debugging but should -// be handled here in the TreeStore instead of the level above. func (t *treeStore) ClearAll() error { for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { - // nodeStore := t.nodeStores[treeType] - // tree := t.merkleTrees[treeType] - // TODO: implement the below - // if err := valueStore.ClearAll(); err != nil { - // return err - // } - // if err := tree.ClearAll(); err != nil { - // return err - // } - - // p.stateTrees.merkleTrees[treeType] = smt.NewSparseMerkleTree(valueStore, nodeStore, sha256.New()) - // TODO: like the line above, create a new set of state tree and assign it to the merkleTrees property - // // This is Needed in order to make sure the root is re-set correctly after clearing - // t.merkleTrees[treeType] = smt.NewSparseMerkleTree() - } - return fmt.Errorf("not impl") + nodeStore := t.nodeStores[treeType] + if err := nodeStore.ClearAll(); err != nil { + return fmt.Errorf("failed to clear %s node store: %w", merkleTreeToString[treeType], err) + } + t.merkleTrees[treeType] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) + } + + return nil } From 68bc5f0c99b0cbe433abc10160523b1fe6ecc8b6 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 6 Jun 2023 13:41:06 -0600 Subject: [PATCH 06/24] [WIP] test_persistence is passing locally --- persistence/docs/CHANGELOG.md | 5 ++ persistence/module.go | 12 ++-- persistence/trees/trees.go | 48 +++++++++----- runtime/docs/CHANGELOG.md | 4 ++ shared/modules/doc/CHANGELOG.md | 4 ++ .../modules/gomock_reflect_1203808572/prog.go | 66 ------------------- shared/modules/persistence_module.go | 22 +++---- 7 files changed, 58 insertions(+), 103 deletions(-) delete mode 100644 shared/modules/gomock_reflect_1203808572/prog.go diff --git a/persistence/docs/CHANGELOG.md b/persistence/docs/CHANGELOG.md index 0948b2792..acf5c65a7 100644 --- a/persistence/docs/CHANGELOG.md +++ b/persistence/docs/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.58] - 2023-06-08 + +- Refactors the stateTrees implementation off of the PersistenceContext and into its in module +- Implements the new Update and Commit pattern with the SMT trees in the trees module + ## [0.0.0.57] - 2023-06-06 - Uses ":memory:" to signify when connecting to an in-memory database diff --git a/persistence/module.go b/persistence/module.go index 492876f7b..40f662e06 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -37,15 +37,13 @@ type persistenceModule struct { // A key-value store mapping heights to blocks. Needed for block synchronization. blockStore blockstore.BlockStore - // A tx indexer (i.e. key-value store) mapping transaction hashes to transactions. Needed for - // avoiding tx replays attacks, and is also used as the backing database for the transaction - // tx merkle tree. + // txIndexer is a key-value store mapping transaction hashes to transactions. + // It is needed for avoiding tx replays attacks, and is also used as the backing + // data store for tree store. txIndexer indexer.TxIndexer - // TODO_IN_THIS_COMMIT updates this comment - // A list of all the merkle trees maintained by the + // stateTrees manages all of the merkle trees maintained by the // persistence module that roll up into the state commitment. - // stateTrees *stateTrees stateTrees modules.TreeStore // Only one write context is allowed at a time @@ -230,12 +228,10 @@ func (m *persistenceModule) ReleaseWriteContext() error { return nil } -// TECHDEBT: declare BlockStore interface in shared/modules func (m *persistenceModule) GetBlockStore() blockstore.BlockStore { return m.blockStore } -// TECHDEBT: declare TxIndexer interface in shared/modules func (m *persistenceModule) GetTxIndexer() indexer.TxIndexer { return m.txIndexer } diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index 74f16f2ca..0862d96ee 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -1,3 +1,7 @@ +// package trees maintains a set of sparse merkle trees +// each backed by the KVStore interface. It offers an atomic +// commit and rollback mechanism for interacting with +// that core resource map of merkle trees. package trees import ( @@ -119,6 +123,27 @@ func NewStateTrees(treesStoreDir string) (*treeStore, error) { return stateTrees, nil } +// Update takes a transaction and a height and updates +// all of the trees in the treeStore for that height. +func (t *treeStore) Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { + return t.updateMerkleTrees(pgtx, txi, height) +} + +// ClearAll is used by debug cli to completely reset the tree. +// This should only be called by the debug CLI. +func (t *treeStore) ClearAll() error { + for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { + nodeStore := t.nodeStores[treeType] + if err := nodeStore.ClearAll(); err != nil { + return fmt.Errorf("failed to clear %s node store: %w", merkleTreeToString[treeType], err) + } + t.merkleTrees[treeType] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) + } + + return nil +} + +// newMemStateTrees creates a new in-memory state tree func newMemStateTrees() (*treeStore, error) { stateTrees := &treeStore{ merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), @@ -173,7 +198,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height // Data Merkle Trees case transactionsMerkleTree: - indexedTxs, err := t.getTransactions(pgtx, txi, height) + indexedTxs, err := t.getTransactions(txi, height) if err != nil { return "", fmt.Errorf("failed to get transactions: %w", err) } @@ -198,7 +223,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height } // Default default: - // t.logger.Fatal().Msgf("Not handled yet in state commitment update. Merkle tree #{%v}", treeType) + panic(fmt.Sprintf("not handled in state commitment update. Merkle tree #{%v}", treeType)) } } @@ -211,7 +236,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height func (t *treeStore) commit() error { for tree := merkleTree(0); tree < numMerkleTrees; tree++ { if err := t.merkleTrees[tree].Commit(); err != nil { - return fmt.Errorf("failed to commit %s: %w", merkleTreeToString[tree]) + return fmt.Errorf("failed to commit %s: %w", merkleTreeToString[tree], err) } } return nil @@ -362,8 +387,7 @@ func (t *treeStore) getActorsUpdated( return nil, fmt.Errorf("no schema found for actor type: %s", actorType) } - // TODO: this height usage is gross and could cause issues, we should use - // uint64 in the source so that this doesn't have to cast + // TECHDEBT #XXX: Avoid this cast to int64 query := actorSchema.GetUpdatedAtHeightQuery(int64(height)) rows, err := pgtx.Query(context.TODO(), query) if err != nil { @@ -424,7 +448,7 @@ func (t *treeStore) getAccountsUpdated( return accounts, nil } -func (t *treeStore) getTransactions(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) ([]*coreTypes.IndexedTransaction, error) { +func (t *treeStore) getTransactions(txi indexer.TxIndexer, height uint64) ([]*coreTypes.IndexedTransaction, error) { // TECHDEBT(#XXX): TxIndexer should use uint64s so we avoid this cast. indexedTxs, err := txi.GetByHeight(int64(height), false) if err != nil { @@ -551,15 +575,3 @@ func (t *treeStore) getChainsForActor( } return actor, nil } - -func (t *treeStore) ClearAll() error { - for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { - nodeStore := t.nodeStores[treeType] - if err := nodeStore.ClearAll(); err != nil { - return fmt.Errorf("failed to clear %s node store: %w", merkleTreeToString[treeType], err) - } - t.merkleTrees[treeType] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) - } - - return nil -} diff --git a/runtime/docs/CHANGELOG.md b/runtime/docs/CHANGELOG.md index 98206c2d4..5263781a4 100644 --- a/runtime/docs/CHANGELOG.md +++ b/runtime/docs/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.43] - 2023-06-13 + +- Rename package import types to coreTypes for consitency and clarity + ## [0.0.0.42] - 2023-06-13 - Append "Hostname" to validator endpoint hostname constants diff --git a/shared/modules/doc/CHANGELOG.md b/shared/modules/doc/CHANGELOG.md index 25c9b9f89..91ea9a097 100644 --- a/shared/modules/doc/CHANGELOG.md +++ b/shared/modules/doc/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.14] - 2023-06-08 + +- Defines the TreeStore interface + ## [0.0.0.14] - 2023-06-06 - Adds fisherman, servicer, and validator modules to utility interface. diff --git a/shared/modules/gomock_reflect_1203808572/prog.go b/shared/modules/gomock_reflect_1203808572/prog.go deleted file mode 100644 index cd7c65353..000000000 --- a/shared/modules/gomock_reflect_1203808572/prog.go +++ /dev/null @@ -1,66 +0,0 @@ - -package main - -import ( - "encoding/gob" - "flag" - "fmt" - "os" - "path" - "reflect" - - "github.com/golang/mock/mockgen/model" - - pkg_ "github.com/pokt-network/pocket/shared/modules" -) - -var output = flag.String("output", "", "The output file name, or empty to use stdout.") - -func main() { - flag.Parse() - - its := []struct{ - sym string - typ reflect.Type - }{ - - { "LoggerModule", reflect.TypeOf((*pkg_.LoggerModule)(nil)).Elem()}, - - } - pkg := &model.Package{ - // NOTE: This behaves contrary to documented behaviour if the - // package name is not the final component of the import path. - // The reflect package doesn't expose the package name, though. - Name: path.Base("github.com/pokt-network/pocket/shared/modules"), - } - - for _, it := range its { - intf, err := model.InterfaceFromInterfaceType(it.typ) - if err != nil { - fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) - os.Exit(1) - } - intf.Name = it.sym - pkg.Interfaces = append(pkg.Interfaces, intf) - } - - outfile := os.Stdout - if len(*output) != 0 { - var err error - outfile, err = os.Create(*output) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) - } - defer func() { - if err := outfile.Close(); err != nil { - fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) - os.Exit(1) - } - }() - } - - if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { - fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) - os.Exit(1) - } -} diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index 0e7acb226..44e814400 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -22,10 +22,11 @@ type PersistenceModule interface { NewReadContext(height int64) (PersistenceReadContext, error) ReleaseWriteContext() error // The module can maintain many read contexts, but only one write context can exist at a time - // BlockStore operations + // BlockStore maps a block height to an *coreTypes.IndexedTransaction GetBlockStore() blockstore.BlockStore - // TreeStore operations + // TreeStore manages atomic access to a set of merkle trees + // that compose the state hash. GetTreeStore() TreeStore NewWriteContext() PersistenceRWContext @@ -38,19 +39,18 @@ type PersistenceModule interface { HandleDebugMessage(*messaging.DebugMessage) error } -// TreeStore is fulfilled by the treeStore to create an -// atomic tree component for use by the peristence context. +// TreeStore defines the interface for atomic updates and rollbacks to the internal +// merkle trees that compose the state hash of pocket. type TreeStore interface { // Update returns the new state hash for a given height. - // * Height is passed through to the Update function - // and used by the queries against the TxIndexer and the - // It updates to the future but not to the past. - // * Passing a higher height will cause a change - // but repeatedly calling the same or a lower height will + // * Height is passed through to the Update function and is used to query the TxIndexer for transactions + // to update into the merkle tree set + // * Passing a higher height will cause a change but repeatedly calling the same or a lower height will // not incur a change. + // * By nature of it taking a pgx transaction at runtime, Update inherits the pgx transaction's read view of the + // database. Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) - // ClearAll completely clears the state of the trees. - // For debugging purposes only. + // ClearAll completely clears the state of the trees. For debugging purposes only. ClearAll() error } From 0878e893fcdb0cc14fb241487375218b9d90bc07 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 6 Jun 2023 16:51:07 -0600 Subject: [PATCH 07/24] [WIP] updates comments and simplifies NewTreeStore function signature --- persistence/debug.go | 2 +- persistence/module.go | 8 ++++---- persistence/trees/trees.go | 14 ++++---------- runtime/docs/CHANGELOG.md | 2 +- shared/modules/persistence_module.go | 4 ++-- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/persistence/debug.go b/persistence/debug.go index 1cf607c65..81b075365 100644 --- a/persistence/debug.go +++ b/persistence/debug.go @@ -116,5 +116,5 @@ func (p *PostgresContext) clearAllSQLState() error { } func (p *PostgresContext) clearAllTreeState() error { - return p.stateTrees.ClearAll() + return p.stateTrees.DebugClearAll() } diff --git a/persistence/module.go b/persistence/module.go index 40f662e06..4f9389580 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -37,9 +37,9 @@ type persistenceModule struct { // A key-value store mapping heights to blocks. Needed for block synchronization. blockStore blockstore.BlockStore - // txIndexer is a key-value store mapping transaction hashes to transactions. - // It is needed for avoiding tx replays attacks, and is also used as the backing - // data store for tree store. + // txIndexer is a key-value store mapping transaction hashes to `IndexedTransaction` protos. + // It is needed to avoid tx replay attacks and for business logic around transaction validation. + // IMPORTANT: It doubles as the data store for the transaction tree in state tree set. txIndexer indexer.TxIndexer // stateTrees manages all of the merkle trees maintained by the @@ -104,7 +104,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio return nil, err } - stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir, txIndexer) + stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir) if err != nil { return nil, err } diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index 0862d96ee..81018131c 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -107,13 +107,13 @@ func NewStateTrees(treesStoreDir string) (*treeStore, error) { } stateTrees := &treeStore{ - treeStoreDir: dir, + treeStoreDir: treesStoreDir, merkleTrees: make(map[merkleTree]*smt.SMT, int(numMerkleTrees)), nodeStores: make(map[merkleTree]kvstore.KVStore, int(numMerkleTrees)), } for tree := merkleTree(0); tree < numMerkleTrees; tree++ { - nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", dir, merkleTreeToString[tree])) + nodeStore, err := kvstore.NewKVStore(fmt.Sprintf("%s/%s_nodes", treesStoreDir, merkleTreeToString[tree])) if err != nil { return nil, err } @@ -123,15 +123,9 @@ func NewStateTrees(treesStoreDir string) (*treeStore, error) { return stateTrees, nil } -// Update takes a transaction and a height and updates -// all of the trees in the treeStore for that height. -func (t *treeStore) Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { - return t.updateMerkleTrees(pgtx, txi, height) -} - -// ClearAll is used by debug cli to completely reset the tree. +// DebugClearAll is used by the debug cli to completely reset all merkle trees. // This should only be called by the debug CLI. -func (t *treeStore) ClearAll() error { +func (t *treeStore) DebugClearAll() error { for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { nodeStore := t.nodeStores[treeType] if err := nodeStore.ClearAll(); err != nil { diff --git a/runtime/docs/CHANGELOG.md b/runtime/docs/CHANGELOG.md index 5263781a4..e85db89f1 100644 --- a/runtime/docs/CHANGELOG.md +++ b/runtime/docs/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Append "Hostname" to validator endpoint hostname constants - Promoted string literal to `RandomValidatorEndpointK8SHostname` constant -## [0.0.0.41] - 2023-06-06 +## [0.0.0.41] - 2023-06-07 - Adds fisherman and servicer proto configurations. - Renames actor hostnames diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index 44e814400..85cc2e6e9 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -50,8 +50,8 @@ type TreeStore interface { // * By nature of it taking a pgx transaction at runtime, Update inherits the pgx transaction's read view of the // database. Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) - // ClearAll completely clears the state of the trees. For debugging purposes only. - ClearAll() error + // DebugClearAll completely clears the state of the trees. For debugging purposes only. + DebugClearAll() error } // Interface defining the context within which the node can operate with the persistence layer. From 428a71a4cb70770badade9ad2e8d5fee7f8e6eea Mon Sep 17 00:00:00 2001 From: shakezula Date: Thu, 8 Jun 2023 14:10:44 -0600 Subject: [PATCH 08/24] adds comments --- persistence/module.go | 1 + persistence/trees/trees.go | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/persistence/module.go b/persistence/module.go index 4f9389580..f374555d2 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -104,6 +104,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio return nil, err } + // TECHDEBT (PR #808): Make TreeStore into a full Module stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir) if err != nil { return nil, err diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index 81018131c..cd0d23029 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -381,7 +381,8 @@ func (t *treeStore) getActorsUpdated( return nil, fmt.Errorf("no schema found for actor type: %s", actorType) } - // TECHDEBT #XXX: Avoid this cast to int64 + // TECHDEBT (ISSUE #813): Avoid this cast to int64 + // https://github.com/pokt-network/pocket/issues/813 query := actorSchema.GetUpdatedAtHeightQuery(int64(height)) rows, err := pgtx.Query(context.TODO(), query) if err != nil { @@ -423,7 +424,8 @@ func (t *treeStore) getAccountsUpdated( ) ([]*coreTypes.Account, error) { accounts := []*coreTypes.Account{} - // TECHDEBT #XXX: Avoid this cast to int64 + // TECHDEBT (ISSUE #813): Avoid this cast to int64 + // https://github.com/pokt-network/pocket/issues/813 query := acctType.GetAccountsUpdatedAtHeightQuery(int64(height)) rows, err := pgtx.Query(context.TODO(), query) if err != nil { @@ -443,7 +445,8 @@ func (t *treeStore) getAccountsUpdated( } func (t *treeStore) getTransactions(txi indexer.TxIndexer, height uint64) ([]*coreTypes.IndexedTransaction, error) { - // TECHDEBT(#XXX): TxIndexer should use uint64s so we avoid this cast. + // TECHDEBT (ISSUE #813): Avoid this cast to int64 + // https://github.com/pokt-network/pocket/issues/813 indexedTxs, err := txi.GetByHeight(int64(height), false) if err != nil { return nil, fmt.Errorf("failed to get transactions by height: %w", err) From 35632f13c5721155432fd374dcc4351dbd74b383 Mon Sep 17 00:00:00 2001 From: shakezula Date: Thu, 8 Jun 2023 14:31:14 -0600 Subject: [PATCH 09/24] changelogs --- shared/modules/doc/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/modules/doc/CHANGELOG.md b/shared/modules/doc/CHANGELOG.md index 91ea9a097..dd39b6925 100644 --- a/shared/modules/doc/CHANGELOG.md +++ b/shared/modules/doc/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.14] - 2023-06-08 +## [0.0.0.15] - 2023-06-08 - Defines the TreeStore interface From 141ab330d36f7afbb621d12e3ec774fdf429adc1 Mon Sep 17 00:00:00 2001 From: shakezula Date: Thu, 8 Jun 2023 18:03:37 -0600 Subject: [PATCH 10/24] add stubbed out test case --- persistence/trees/trees_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 persistence/trees/trees_test.go diff --git a/persistence/trees/trees_test.go b/persistence/trees/trees_test.go new file mode 100644 index 000000000..9d442799e --- /dev/null +++ b/persistence/trees/trees_test.go @@ -0,0 +1,18 @@ +package trees + +import "testing" + +func TestTreeStore_Update(t *testing.T) { + // TODO: Write test case for the Update method + t.Skip("TODO: Write test case for Update method") +} + +func TestNewStateTrees(t *testing.T) { + // TODO: Write test case for the NewStateTrees function + t.Skip("TODO: Write test case for NewStateTrees function") +} + +func TestTreeStore_DebugClearAll(t *testing.T) { + // TODO: Write test case for the DebugClearAll method + t.Skip("TODO: Write test case for DebugClearAll method") +} From d0511c5705d89ccfdf8f3f275ca775930d007bd4 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 13 Jun 2023 12:33:06 -0600 Subject: [PATCH 11/24] changelogs --- persistence/docs/CHANGELOG.md | 2 +- shared/modules/doc/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/persistence/docs/CHANGELOG.md b/persistence/docs/CHANGELOG.md index acf5c65a7..f73c63e4b 100644 --- a/persistence/docs/CHANGELOG.md +++ b/persistence/docs/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.58] - 2023-06-08 +## [0.0.0.58] - 2023-06-13 - Refactors the stateTrees implementation off of the PersistenceContext and into its in module - Implements the new Update and Commit pattern with the SMT trees in the trees module diff --git a/shared/modules/doc/CHANGELOG.md b/shared/modules/doc/CHANGELOG.md index dd39b6925..a59273714 100644 --- a/shared/modules/doc/CHANGELOG.md +++ b/shared/modules/doc/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.15] - 2023-06-08 +## [0.0.0.15] - 2023-06-13 - Defines the TreeStore interface From d74032d1ad79f236e476b4ee5e0d38b6e3a4c9b2 Mon Sep 17 00:00:00 2001 From: shakezula Date: Wed, 14 Jun 2023 13:57:28 -0600 Subject: [PATCH 12/24] changelogs --- persistence/docs/CHANGELOG.md | 2 +- runtime/docs/CHANGELOG.md | 2 +- shared/modules/doc/CHANGELOG.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/persistence/docs/CHANGELOG.md b/persistence/docs/CHANGELOG.md index f73c63e4b..38c4080aa 100644 --- a/persistence/docs/CHANGELOG.md +++ b/persistence/docs/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.58] - 2023-06-13 +## [0.0.0.58] - 2023-06-14 - Refactors the stateTrees implementation off of the PersistenceContext and into its in module - Implements the new Update and Commit pattern with the SMT trees in the trees module diff --git a/runtime/docs/CHANGELOG.md b/runtime/docs/CHANGELOG.md index e85db89f1..b36289c67 100644 --- a/runtime/docs/CHANGELOG.md +++ b/runtime/docs/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.43] - 2023-06-13 +## [0.0.0.43] - 2023-06-14 - Rename package import types to coreTypes for consitency and clarity diff --git a/shared/modules/doc/CHANGELOG.md b/shared/modules/doc/CHANGELOG.md index a59273714..d6d965cce 100644 --- a/shared/modules/doc/CHANGELOG.md +++ b/shared/modules/doc/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.0.15] - 2023-06-13 +## [0.0.0.15] - 2023-06-14 - Defines the TreeStore interface From 1b277ce682ec8175462059f08c5a3f20b2f8a85e Mon Sep 17 00:00:00 2001 From: shakezula Date: Wed, 14 Jun 2023 15:01:06 -0600 Subject: [PATCH 13/24] updates --- persistence/module.go | 2 +- persistence/trees/trees.go | 9 +++++---- shared/modules/persistence_module.go | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/persistence/module.go b/persistence/module.go index f374555d2..887a85f06 100644 --- a/persistence/module.go +++ b/persistence/module.go @@ -104,7 +104,7 @@ func (*persistenceModule) Create(bus modules.Bus, options ...modules.ModuleOptio return nil, err } - // TECHDEBT (PR #808): Make TreeStore into a full Module + // TECHDEBT (#808): Make TreeStore into a full Module stateTrees, err := trees.NewStateTrees(persistenceCfg.TreesStoreDir) if err != nil { return nil, err diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index cd0d23029..413d37d70 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -65,6 +65,8 @@ const ( // defines the index of the root hash each independent as they are concatenated together // to generate the state hash. + // TECHDEBT: Enforce this ordering in protobufs using enums + // Actor Merkle Trees appMerkleTree merkleTree = iota valMerkleTree @@ -86,7 +88,7 @@ const ( // treeStore stores a set of merkle trees that // it manages. It fulfills the modules.TreeStore interface. -// * It is responsible for commit or rollback behavior +// * It is responsible for atomic commit or rollback behavior // of the underlying trees by utilizing the lazy loading // functionality provided by the underlying smt library. type treeStore struct { @@ -405,14 +407,13 @@ func (t *treeStore) getActorsUpdated( actors := make([]*coreTypes.Actor, len(addrs)) for i, addr := range addrs { - // TECHDEBT #XXX: Avoid this cast to int64 + // TECHDEBT #813: Avoid this cast to int64 actor, err := t.getActor(pgtx, actorSchema, addr, int64(height)) if err != nil { return nil, err } actors[i] = actor } - rows.Close() return actors, nil } @@ -559,7 +560,7 @@ func (t *treeStore) getChainsForActor( var chainAddr string var chainID string - var chainEndHeight int64 // DISCUSS: why is this commented as "unused"? + var chainEndHeight int64 for rows.Next() { err = rows.Scan(&chainAddr, &chainID, &chainEndHeight) if err != nil { diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index 85cc2e6e9..ff8bbe217 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -49,6 +49,7 @@ type TreeStore interface { // not incur a change. // * By nature of it taking a pgx transaction at runtime, Update inherits the pgx transaction's read view of the // database. + // TODO(#808): Change interface to `Update(pgtx pgx.Tx, height uint64) (string, error)` Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) // DebugClearAll completely clears the state of the trees. For debugging purposes only. DebugClearAll() error From 3d5701d45ace45fb4771ba3b8b4628208a92e625 Mon Sep 17 00:00:00 2001 From: shakezula Date: Thu, 15 Jun 2023 16:36:53 -0600 Subject: [PATCH 14/24] refactors the sql handling into a separate package --- persistence/sql/README.md | 35 ++++++ persistence/sql/sql.go | 226 +++++++++++++++++++++++++++++++++++++ persistence/trees/trees.go | 224 ++---------------------------------- 3 files changed, 268 insertions(+), 217 deletions(-) create mode 100644 persistence/sql/README.md create mode 100644 persistence/sql/sql.go diff --git a/persistence/sql/README.md b/persistence/sql/README.md new file mode 100644 index 000000000..b851ef559 --- /dev/null +++ b/persistence/sql/README.md @@ -0,0 +1,35 @@ +# SQL Package + +The SQL package is a library of functions that all take a `pgx.Tx` as an argument and execute SQL queries on it. + +- GetAccountsUpdated +- GetActors +- GetTransactions +- GetPools +- GetAccounts +- GetFlags +- GetParams + +These functions are meant to abstract away the SQL row and error handling for components of the persistence package. + +GetActors is used to get each of the actor types in the system: Applications, Validators, Watchers, and Servicers. + +## Why a whole package? + +If the SQL handling lives in the persistence package, the package submodules will run into import cycle errors when attempting to use the SQL handlers. Putting the SQL helpers into a package inside persistence avoids the import cycle. + +```mermaid +flowchart TD + subgraph pers[persistence] + subgraph p[ without sql package - import cycle error ] + persistenceContext1[PersistenceContext] + treeStore1[TreeStore] + treeStore1 -- Tx --> persistenceContext1 + persistenceContext1 -- Tx --> treeStore1 + end + subgraph p2[ with sql package - refactored ] + persistenceContext -- Tx --> sql + treeStore2 -- Tx --> sql + end + end +``` diff --git a/persistence/sql/sql.go b/persistence/sql/sql.go new file mode 100644 index 000000000..cd8c74ed2 --- /dev/null +++ b/persistence/sql/sql.go @@ -0,0 +1,226 @@ +package sql + +import ( + "context" + "encoding/hex" + "fmt" + + "github.com/jackc/pgx/v5" + "github.com/pokt-network/pocket/persistence/indexer" + ptypes "github.com/pokt-network/pocket/persistence/types" + coreTypes "github.com/pokt-network/pocket/shared/core/types" +) + +// actorTypeToSchmeaName maps an ActorType to a PostgreSQL schema name +var actorTypeToSchemaName = map[coreTypes.ActorType]ptypes.ProtocolActorSchema{ + coreTypes.ActorType_ACTOR_TYPE_APP: ptypes.ApplicationActor, + coreTypes.ActorType_ACTOR_TYPE_VAL: ptypes.ValidatorActor, + coreTypes.ActorType_ACTOR_TYPE_FISH: ptypes.FishermanActor, + coreTypes.ActorType_ACTOR_TYPE_SERVICER: ptypes.ServicerActor, +} + +// GetActors is responsible for fetching the actors that have been updated at a given height. +func GetActors( + pgtx pgx.Tx, + actorType coreTypes.ActorType, + height uint64, +) ([]*coreTypes.Actor, error) { + actorSchema, ok := actorTypeToSchemaName[actorType] + if !ok { + return nil, fmt.Errorf("no schema found for actor type: %s", actorType) + } + + // TECHDEBT (ISSUE #813): Avoid this cast to int64 + // https://github.com/pokt-network/pocket/issues/813 + query := actorSchema.GetUpdatedAtHeightQuery(int64(height)) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, err + } + defer rows.Close() + + addrs := make([][]byte, 0) + for rows.Next() { + var addr string + if err := rows.Scan(&addr); err != nil { + return nil, err + } + addrBz, err := hex.DecodeString(addr) + if err != nil { + return nil, err + } + addrs = append(addrs, addrBz) + } + + actors := make([]*coreTypes.Actor, len(addrs)) + for i, addr := range addrs { + // TECHDEBT #813: Avoid this cast to int64 + actor, err := getActor(pgtx, actorSchema, addr, int64(height)) + if err != nil { + return nil, err + } + actors[i] = actor + } + + return actors, nil +} + +// GetAccountsUpdated gets the AccountSchema accounts that have been updated at height +func GetAccountsUpdated( + pgtx pgx.Tx, + acctType ptypes.ProtocolAccountSchema, + height uint64, +) ([]*coreTypes.Account, error) { + accounts := []*coreTypes.Account{} + + // TECHDEBT (ISSUE #813): Avoid this cast to int64 + // https://github.com/pokt-network/pocket/issues/813 + query := acctType.GetAccountsUpdatedAtHeightQuery(int64(height)) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + acc := new(coreTypes.Account) + if err := rows.Scan(&acc.Address, &acc.Amount); err != nil { + return nil, err + } + accounts = append(accounts, acc) + } + + return accounts, nil +} + +// GetTransactions takes a transaction indexer and returns the transactions for the current height +func GetTransactions(txi indexer.TxIndexer, height uint64) ([]*coreTypes.IndexedTransaction, error) { + // TECHDEBT (ISSUE #813): Avoid this cast to int64 + // https://github.com/pokt-network/pocket/issues/813 + indexedTxs, err := txi.GetByHeight(int64(height), false) + if err != nil { + return nil, fmt.Errorf("failed to get transactions by height: %w", err) + } + return indexedTxs, nil +} + +// GetPools returns the pools updated at the given height +func GetPools(pgtx pgx.Tx, height uint64) ([]*coreTypes.Account, error) { + pools, err := GetAccountsUpdated(pgtx, ptypes.Pool, height) + if err != nil { + return nil, fmt.Errorf("failed to get pools: %w", err) + } + return pools, nil +} + +// GetAccounts returns the list of accounts updated at the provided height +func GetAccounts(pgtx pgx.Tx, height uint64) ([]*coreTypes.Account, error) { + accounts, err := GetAccountsUpdated(pgtx, ptypes.Account, height) + if err != nil { + return nil, fmt.Errorf("failed to get accounts: %w", err) + } + return accounts, nil +} + +// GetFlags returns the set of flags updated at the given height +func GetFlags(pgtx pgx.Tx, height uint64) ([]*coreTypes.Flag, error) { + fields := "name,value,enabled" + query := fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, ptypes.FlagsTableName, height) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, fmt.Errorf("failed to get flags: %w", err) + } + defer rows.Close() + + flagSlice := []*coreTypes.Flag{} + for rows.Next() { + flag := new(coreTypes.Flag) + if err := rows.Scan(&flag.Name, &flag.Value, &flag.Enabled); err != nil { + return nil, err + } + flag.Height = int64(height) + flagSlice = append(flagSlice, flag) + } + + return flagSlice, nil +} + +// GetParams returns the set of params updated at the currented height +func GetParams(pgtx pgx.Tx, height uint64) ([]*coreTypes.Param, error) { + fields := "name,value" + query := fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, ptypes.ParamsTableName, height) + rows, err := pgtx.Query(context.TODO(), query) + if err != nil { + return nil, err + } + defer rows.Close() + + var paramSlice []*coreTypes.Param + for rows.Next() { + param := new(coreTypes.Param) + if err := rows.Scan(¶m.Name, ¶m.Value); err != nil { + return nil, err + } + param.Height = int64(height) + paramSlice = append(paramSlice, param) + } + + return paramSlice, nil +} + +func getActor(tx pgx.Tx, actorSchema ptypes.ProtocolActorSchema, address []byte, height int64) (actor *coreTypes.Actor, err error) { + ctx := context.TODO() + actor, height, err = getActorFromRow(actorSchema.GetActorType(), tx.QueryRow(ctx, actorSchema.GetQuery(hex.EncodeToString(address), height))) + if err != nil { + return + } + return getChainsForActor(ctx, tx, actorSchema, actor, height) +} + +func getActorFromRow(actorType coreTypes.ActorType, row pgx.Row) (actor *coreTypes.Actor, height int64, err error) { + actor = &coreTypes.Actor{ + ActorType: actorType, + } + err = row.Scan( + &actor.Address, + &actor.PublicKey, + &actor.StakedAmount, + &actor.ServiceUrl, + &actor.Output, + &actor.PausedHeight, + &actor.UnstakingHeight, + &height) + return +} + +func getChainsForActor( + ctx context.Context, + tx pgx.Tx, + actorSchema ptypes.ProtocolActorSchema, + actor *coreTypes.Actor, + height int64, +) (a *coreTypes.Actor, err error) { + if actorSchema.GetChainsTableName() == "" { + return actor, nil + } + rows, err := tx.Query(ctx, actorSchema.GetChainsQuery(actor.Address, height)) + if err != nil { + return actor, err + } + defer rows.Close() + + var chainAddr string + var chainID string + var chainEndHeight int64 + for rows.Next() { + err = rows.Scan(&chainAddr, &chainID, &chainEndHeight) + if err != nil { + return + } + if chainAddr != actor.Address { + return actor, fmt.Errorf("unexpected address %s, expected %s when reading chains", chainAddr, actor.Address) + } + actor.Chains = append(actor.Chains, chainID) + } + return actor, nil +} diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index 413d37d70..a4f30342d 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -6,7 +6,6 @@ package trees import ( "bytes" - "context" "crypto/sha256" "encoding/hex" "fmt" @@ -15,7 +14,7 @@ import ( "github.com/pokt-network/pocket/persistence/indexer" "github.com/pokt-network/pocket/persistence/kvstore" - ptypes "github.com/pokt-network/pocket/persistence/types" + "github.com/pokt-network/pocket/persistence/sql" "github.com/pokt-network/pocket/shared/codec" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" @@ -43,13 +42,6 @@ var actorTypeToMerkleTreeName = map[coreTypes.ActorType]merkleTree{ coreTypes.ActorType_ACTOR_TYPE_SERVICER: servicerMerkleTree, } -var actorTypeToSchemaName = map[coreTypes.ActorType]ptypes.ProtocolActorSchema{ - coreTypes.ActorType_ACTOR_TYPE_APP: ptypes.ApplicationActor, - coreTypes.ActorType_ACTOR_TYPE_VAL: ptypes.ValidatorActor, - coreTypes.ActorType_ACTOR_TYPE_FISH: ptypes.FishermanActor, - coreTypes.ActorType_ACTOR_TYPE_SERVICER: ptypes.ServicerActor, -} - var merkleTreeToActorTypeName = map[merkleTree]coreTypes.ActorType{ appMerkleTree: coreTypes.ActorType_ACTOR_TYPE_APP, valMerkleTree: coreTypes.ActorType_ACTOR_TYPE_VAL, @@ -165,7 +157,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height return "", fmt.Errorf("no actor type found for merkle tree: %v", treeType) } - actors, err := t.getActorsUpdated(pgtx, actorType, height) + actors, err := sql.GetActors(pgtx, actorType, height) if err != nil { return "", fmt.Errorf("failed to get actors at height: %w", err) } @@ -176,7 +168,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height // Account Merkle Trees case accountMerkleTree: - accounts, err := t.getAccounts(pgtx, height) + accounts, err := sql.GetAccounts(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get accounts: %w", err) } @@ -184,7 +176,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height return "", fmt.Errorf("failed to update account trees: %w", err) } case poolMerkleTree: - pools, err := t.getPools(pgtx, height) + pools, err := sql.GetPools(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get transactions: %w", err) } @@ -194,7 +186,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height // Data Merkle Trees case transactionsMerkleTree: - indexedTxs, err := t.getTransactions(txi, height) + indexedTxs, err := sql.GetTransactions(txi, height) if err != nil { return "", fmt.Errorf("failed to get transactions: %w", err) } @@ -202,7 +194,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height return "", fmt.Errorf("failed to update transactions: %w", err) } case paramsMerkleTree: - params, err := t.getParams(pgtx, height) + params, err := sql.GetParams(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get params: %w", err) } @@ -210,7 +202,7 @@ func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height return "", fmt.Errorf("failed to update params tree: %w", err) } case flagsMerkleTree: - flags, err := t.getFlags(pgtx, height) + flags, err := sql.GetFlags(pgtx, height) if err != nil { return "", fmt.Errorf("failed to get flags from transaction: %w", err) } @@ -371,205 +363,3 @@ func (t *treeStore) updateFlagsTree(flags []*coreTypes.Flag) error { return nil } - -// getActorsUpdated is responsible for fetching the actors that have been updated at a given height. -func (t *treeStore) getActorsUpdated( - pgtx pgx.Tx, - actorType coreTypes.ActorType, - height uint64, -) ([]*coreTypes.Actor, error) { - actorSchema, ok := actorTypeToSchemaName[actorType] - if !ok { - return nil, fmt.Errorf("no schema found for actor type: %s", actorType) - } - - // TECHDEBT (ISSUE #813): Avoid this cast to int64 - // https://github.com/pokt-network/pocket/issues/813 - query := actorSchema.GetUpdatedAtHeightQuery(int64(height)) - rows, err := pgtx.Query(context.TODO(), query) - if err != nil { - return nil, err - } - defer rows.Close() - - addrs := make([][]byte, 0) - for rows.Next() { - var addr string - if err := rows.Scan(&addr); err != nil { - return nil, err - } - addrBz, err := hex.DecodeString(addr) - if err != nil { - return nil, err - } - addrs = append(addrs, addrBz) - } - - actors := make([]*coreTypes.Actor, len(addrs)) - for i, addr := range addrs { - // TECHDEBT #813: Avoid this cast to int64 - actor, err := t.getActor(pgtx, actorSchema, addr, int64(height)) - if err != nil { - return nil, err - } - actors[i] = actor - } - - return actors, nil -} - -func (t *treeStore) getAccountsUpdated( - pgtx pgx.Tx, - acctType ptypes.ProtocolAccountSchema, - height uint64, -) ([]*coreTypes.Account, error) { - accounts := []*coreTypes.Account{} - - // TECHDEBT (ISSUE #813): Avoid this cast to int64 - // https://github.com/pokt-network/pocket/issues/813 - query := acctType.GetAccountsUpdatedAtHeightQuery(int64(height)) - rows, err := pgtx.Query(context.TODO(), query) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - acc := new(coreTypes.Account) - if err := rows.Scan(&acc.Address, &acc.Amount); err != nil { - return nil, err - } - accounts = append(accounts, acc) - } - - return accounts, nil -} - -func (t *treeStore) getTransactions(txi indexer.TxIndexer, height uint64) ([]*coreTypes.IndexedTransaction, error) { - // TECHDEBT (ISSUE #813): Avoid this cast to int64 - // https://github.com/pokt-network/pocket/issues/813 - indexedTxs, err := txi.GetByHeight(int64(height), false) - if err != nil { - return nil, fmt.Errorf("failed to get transactions by height: %w", err) - } - return indexedTxs, nil -} - -// getPools returns the pools updated at the given height -func (t *treeStore) getPools(pgtx pgx.Tx, height uint64) ([]*coreTypes.Account, error) { - pools, err := t.getAccountsUpdated(pgtx, ptypes.Pool, height) - if err != nil { - return nil, fmt.Errorf("failed to get pools: %w", err) - } - return pools, nil -} - -// getAccounts returns the list of accounts updated at the provided height -func (t *treeStore) getAccounts(pgtx pgx.Tx, height uint64) ([]*coreTypes.Account, error) { - accounts, err := t.getAccountsUpdated(pgtx, ptypes.Account, height) - if err != nil { - return nil, fmt.Errorf("failed to get accounts: %w", err) - } - return accounts, nil -} - -func (t *treeStore) getFlags(pgtx pgx.Tx, height uint64) ([]*coreTypes.Flag, error) { - fields := "name,value,enabled" - query := fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, ptypes.FlagsTableName, height) - rows, err := pgtx.Query(context.TODO(), query) - if err != nil { - return nil, fmt.Errorf("failed to get flags: %w", err) - } - defer rows.Close() - - flagSlice := []*coreTypes.Flag{} - for rows.Next() { - flag := new(coreTypes.Flag) - if err := rows.Scan(&flag.Name, &flag.Value, &flag.Enabled); err != nil { - return nil, err - } - flag.Height = int64(height) - flagSlice = append(flagSlice, flag) - } - - return flagSlice, nil -} - -func (t *treeStore) getParams(pgtx pgx.Tx, height uint64) ([]*coreTypes.Param, error) { - fields := "name,value" - query := fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, ptypes.ParamsTableName, height) - rows, err := pgtx.Query(context.TODO(), query) - if err != nil { - return nil, err - } - defer rows.Close() - - var paramSlice []*coreTypes.Param - for rows.Next() { - param := new(coreTypes.Param) - if err := rows.Scan(¶m.Name, ¶m.Value); err != nil { - return nil, err - } - param.Height = int64(height) - paramSlice = append(paramSlice, param) - } - - return paramSlice, nil -} - -func (t *treeStore) getActor(tx pgx.Tx, actorSchema ptypes.ProtocolActorSchema, address []byte, height int64) (actor *coreTypes.Actor, err error) { - ctx := context.TODO() - actor, height, err = t.getActorFromRow(actorSchema.GetActorType(), tx.QueryRow(ctx, actorSchema.GetQuery(hex.EncodeToString(address), height))) - if err != nil { - return - } - return t.getChainsForActor(ctx, tx, actorSchema, actor, height) -} - -func (t *treeStore) getActorFromRow(actorType coreTypes.ActorType, row pgx.Row) (actor *coreTypes.Actor, height int64, err error) { - actor = &coreTypes.Actor{ - ActorType: actorType, - } - err = row.Scan( - &actor.Address, - &actor.PublicKey, - &actor.StakedAmount, - &actor.ServiceUrl, - &actor.Output, - &actor.PausedHeight, - &actor.UnstakingHeight, - &height) - return -} - -func (t *treeStore) getChainsForActor( - ctx context.Context, - tx pgx.Tx, - actorSchema ptypes.ProtocolActorSchema, - actor *coreTypes.Actor, - height int64, -) (a *coreTypes.Actor, err error) { - if actorSchema.GetChainsTableName() == "" { - return actor, nil - } - rows, err := tx.Query(ctx, actorSchema.GetChainsQuery(actor.Address, height)) - if err != nil { - return actor, err - } - defer rows.Close() - - var chainAddr string - var chainID string - var chainEndHeight int64 - for rows.Next() { - err = rows.Scan(&chainAddr, &chainID, &chainEndHeight) - if err != nil { - return - } - if chainAddr != actor.Address { - return actor, fmt.Errorf("unexpected address %s, expected %s when reading chains", chainAddr, actor.Address) - } - actor.Chains = append(actor.Chains, chainID) - } - return actor, nil -} From 5a2679728e64407e58763286a7d210acbfb57793 Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Fri, 16 Jun 2023 16:03:00 -0700 Subject: [PATCH 15/24] nits to comments in sql.go --- persistence/sql/sql.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/persistence/sql/sql.go b/persistence/sql/sql.go index cd8c74ed2..48bbec2d3 100644 --- a/persistence/sql/sql.go +++ b/persistence/sql/sql.go @@ -30,8 +30,7 @@ func GetActors( return nil, fmt.Errorf("no schema found for actor type: %s", actorType) } - // TECHDEBT (ISSUE #813): Avoid this cast to int64 - // https://github.com/pokt-network/pocket/issues/813 + // TECHDEBT(#813): Avoid this cast to int64 query := actorSchema.GetUpdatedAtHeightQuery(int64(height)) rows, err := pgtx.Query(context.TODO(), query) if err != nil { @@ -54,7 +53,7 @@ func GetActors( actors := make([]*coreTypes.Actor, len(addrs)) for i, addr := range addrs { - // TECHDEBT #813: Avoid this cast to int64 + // TECHDEBT(#813): Avoid this cast to int64 actor, err := getActor(pgtx, actorSchema, addr, int64(height)) if err != nil { return nil, err @@ -73,8 +72,7 @@ func GetAccountsUpdated( ) ([]*coreTypes.Account, error) { accounts := []*coreTypes.Account{} - // TECHDEBT (ISSUE #813): Avoid this cast to int64 - // https://github.com/pokt-network/pocket/issues/813 + // TECHDEBT(#813): Avoid this cast to int64 query := acctType.GetAccountsUpdatedAtHeightQuery(int64(height)) rows, err := pgtx.Query(context.TODO(), query) if err != nil { @@ -95,8 +93,7 @@ func GetAccountsUpdated( // GetTransactions takes a transaction indexer and returns the transactions for the current height func GetTransactions(txi indexer.TxIndexer, height uint64) ([]*coreTypes.IndexedTransaction, error) { - // TECHDEBT (ISSUE #813): Avoid this cast to int64 - // https://github.com/pokt-network/pocket/issues/813 + // TECHDEBT(#813): Avoid this cast to int64 indexedTxs, err := txi.GetByHeight(int64(height), false) if err != nil { return nil, fmt.Errorf("failed to get transactions by height: %w", err) From 61936e9739d891305537acc3a1624f554fce45d9 Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Fri, 16 Jun 2023 16:04:31 -0700 Subject: [PATCH 16/24] Updated comment on tree ordering techdebt --- persistence/trees/trees.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index a4f30342d..a3bd42a14 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -57,7 +57,7 @@ const ( // defines the index of the root hash each independent as they are concatenated together // to generate the state hash. - // TECHDEBT: Enforce this ordering in protobufs using enums + // TECHDEBT(#834): Remove the need for enforced ordering // Actor Merkle Trees appMerkleTree merkleTree = iota From 905bcc6ee69821268cb4f6ac2ebbad251ee494e4 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 20 Jun 2023 15:40:43 -0600 Subject: [PATCH 17/24] remove dead code --- persistence/gov.go | 52 ---------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/persistence/gov.go b/persistence/gov.go index e0bbcdb38..5ddec2884 100644 --- a/persistence/gov.go +++ b/persistence/gov.go @@ -8,7 +8,6 @@ import ( "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/persistence/types" "github.com/pokt-network/pocket/runtime/genesis" - coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/modules" ) @@ -156,48 +155,6 @@ func getParamOrFlag[T int | string | []byte](p *PostgresContext, tableName, para return } -func (p *PostgresContext) getParamsUpdated(height int64) ([]*coreTypes.Param, error) { - ctx, tx := p.getCtxAndTx() - // Get all parameters / flags at current height - rows, err := tx.Query(ctx, p.getParamsOrFlagsUpdateAtHeightQuery(types.ParamsTableName, height)) - if err != nil { - return nil, err - } - defer rows.Close() - var paramSlice []*coreTypes.Param // Store returned rows - // Loop over all rows returned and load them into the ParamOrFlag struct array - for rows.Next() { - param := new(coreTypes.Param) - if err := rows.Scan(¶m.Name, ¶m.Value); err != nil { - return nil, err - } - param.Height = height - paramSlice = append(paramSlice, param) - } - return paramSlice, nil -} - -func (p *PostgresContext) getFlagsUpdated(height int64) ([]*coreTypes.Flag, error) { - ctx, tx := p.getCtxAndTx() - // Get all parameters / flags at current height - rows, err := tx.Query(ctx, p.getParamsOrFlagsUpdateAtHeightQuery(types.FlagsTableName, height)) - if err != nil { - return nil, err - } - defer rows.Close() - var flagSlice []*coreTypes.Flag // Store returned rows - // Loop over all rows returned and load them into the ParamOrFlag struct array - for rows.Next() { - flag := new(coreTypes.Flag) - if err := rows.Scan(&flag.Name, &flag.Value, &flag.Enabled); err != nil { - return nil, err - } - flag.Height = height - flagSlice = append(flagSlice, flag) - } - return flagSlice, nil -} - // GetAllParams returns a map of the current latest updated values for all parameters // and their values in the form map[parameterName] = parameterValue func (p *PostgresContext) GetAllParams() ([][]string, error) { @@ -219,15 +176,6 @@ func (p *PostgresContext) GetAllParams() ([][]string, error) { return paramSlice, nil } -func (p *PostgresContext) getParamsOrFlagsUpdateAtHeightQuery(tableName string, height int64) string { - fields := "name,value" - if tableName == types.FlagsTableName { - fields += ",enabled" - } - // Build correct query to get all Params/Flags at certain height ordered by their name values - return fmt.Sprintf("SELECT %s FROM %s WHERE height=%d ORDER BY name ASC", fields, tableName, height) -} - func (p *PostgresContext) getLatestParamsOrFlagsQuery(tableName string) string { fields := "name,value" if tableName == types.FlagsTableName { From 9403106cca44254bce102105a838c59c8a86de80 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 20 Jun 2023 15:41:00 -0600 Subject: [PATCH 18/24] update comments --- shared/modules/persistence_module.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index ff8bbe217..65681a5ac 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -43,12 +43,7 @@ type PersistenceModule interface { // merkle trees that compose the state hash of pocket. type TreeStore interface { // Update returns the new state hash for a given height. - // * Height is passed through to the Update function and is used to query the TxIndexer for transactions - // to update into the merkle tree set - // * Passing a higher height will cause a change but repeatedly calling the same or a lower height will - // not incur a change. - // * By nature of it taking a pgx transaction at runtime, Update inherits the pgx transaction's read view of the - // database. + // * Update inherits the pgx transaction's read view of the database and builds the trees according to that view. // TODO(#808): Change interface to `Update(pgtx pgx.Tx, height uint64) (string, error)` Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) // DebugClearAll completely clears the state of the trees. For debugging purposes only. From a34ff05f61cbd64f452bd9b1869d78d089d23615 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 20 Jun 2023 17:31:33 -0600 Subject: [PATCH 19/24] updates --- persistence/trees/trees.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index a3bd42a14..b963c9ad9 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -9,6 +9,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "hash" "github.com/jackc/pgx/v5" @@ -21,6 +22,8 @@ import ( "github.com/pokt-network/smt" ) +var smtTreeHasher hash.Hash = sha256.New() + var merkleTreeToString = map[merkleTree]string{ appMerkleTree: "app", valMerkleTree: "val", @@ -89,12 +92,12 @@ type treeStore struct { nodeStores map[merkleTree]kvstore.KVStore } -// Update takes a transaction and a height and updates -// all of the trees in the treeStore for that height. func (t *treeStore) Update(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { return t.updateMerkleTrees(pgtx, txi, height) } +// NewStateTrees is the constructor object for a treeStore and initializes and configures a new +// tree for the appropriate type of store, i.e. in-memory vs file system storage. func NewStateTrees(treesStoreDir string) (*treeStore, error) { if treesStoreDir == ":memory:" { return newMemStateTrees() @@ -112,22 +115,22 @@ func NewStateTrees(treesStoreDir string) (*treeStore, error) { return nil, err } stateTrees.nodeStores[tree] = nodeStore - stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) + stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, smtTreeHasher) } return stateTrees, nil } // DebugClearAll is used by the debug cli to completely reset all merkle trees. // This should only be called by the debug CLI. +// TECHDEBT: Move this into a separate file with a debug build flag to avoid accidental usage in prod func (t *treeStore) DebugClearAll() error { for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { nodeStore := t.nodeStores[treeType] if err := nodeStore.ClearAll(); err != nil { return fmt.Errorf("failed to clear %s node store: %w", merkleTreeToString[treeType], err) } - t.merkleTrees[treeType] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) + t.merkleTrees[treeType] = smt.NewSparseMerkleTree(nodeStore, smtTreeHasher) } - return nil } @@ -140,7 +143,7 @@ func newMemStateTrees() (*treeStore, error) { for tree := merkleTree(0); tree < numMerkleTrees; tree++ { nodeStore := kvstore.NewMemKVStore() // For testing, `smt.NewSimpleMap()` can be used as well stateTrees.nodeStores[tree] = nodeStore - stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, sha256.New()) + stateTrees.merkleTrees[tree] = smt.NewSparseMerkleTree(nodeStore, smtTreeHasher) } return stateTrees, nil } From 52172a0fbdd90a3ffa5f46ad0378092256ce5cb6 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 20 Jun 2023 17:32:28 -0600 Subject: [PATCH 20/24] updates --- persistence/trees/trees_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/trees/trees_test.go b/persistence/trees/trees_test.go index 9d442799e..4bd1dd640 100644 --- a/persistence/trees/trees_test.go +++ b/persistence/trees/trees_test.go @@ -7,7 +7,7 @@ func TestTreeStore_Update(t *testing.T) { t.Skip("TODO: Write test case for Update method") } -func TestNewStateTrees(t *testing.T) { +func TestTreeStore_New(t *testing.T) { // TODO: Write test case for the NewStateTrees function t.Skip("TODO: Write test case for NewStateTrees function") } From e3a3081345a1b6308b3d37523b4b031a926b835c Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 20 Jun 2023 17:33:50 -0600 Subject: [PATCH 21/24] updates --- persistence/trees/trees.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index b963c9ad9..2f86f4055 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -148,8 +148,8 @@ func newMemStateTrees() (*treeStore, error) { return stateTrees, nil } -// updateMerkleTrees updates all of the merkle trees that TreeStore manages. -// * it returns an hash of the output or an error. +// updateMerkleTrees updates all of the merkle trees in order defined by `numMerkleTrees` +// * it returns the new state hash capturing the state of all the trees or an error if one occured func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { switch treeType { From b3371d275528ae1cb34304f89f4d8249e752fcba Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 20 Jun 2023 17:57:08 -0600 Subject: [PATCH 22/24] adds techdebt comment --- persistence/trees/trees_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/persistence/trees/trees_test.go b/persistence/trees/trees_test.go index 4bd1dd640..f1e3b27a2 100644 --- a/persistence/trees/trees_test.go +++ b/persistence/trees/trees_test.go @@ -2,6 +2,7 @@ package trees import "testing" +// TECHDEBT #836: Tests added in https://github.com/pokt-network/pocket/pull/836 func TestTreeStore_Update(t *testing.T) { // TODO: Write test case for the Update method t.Skip("TODO: Write test case for Update method") From 70537cacb898c08d8a3225b3b5d5e124aca26bb9 Mon Sep 17 00:00:00 2001 From: shakezula Date: Tue, 20 Jun 2023 20:13:58 -0600 Subject: [PATCH 23/24] spelling error for linter --- persistence/trees/trees.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/trees/trees.go b/persistence/trees/trees.go index 2f86f4055..1463b901c 100644 --- a/persistence/trees/trees.go +++ b/persistence/trees/trees.go @@ -149,7 +149,7 @@ func newMemStateTrees() (*treeStore, error) { } // updateMerkleTrees updates all of the merkle trees in order defined by `numMerkleTrees` -// * it returns the new state hash capturing the state of all the trees or an error if one occured +// * it returns the new state hash capturing the state of all the trees or an error if one occurred func (t *treeStore) updateMerkleTrees(pgtx pgx.Tx, txi indexer.TxIndexer, height uint64) (string, error) { for treeType := merkleTree(0); treeType < numMerkleTrees; treeType++ { switch treeType { From b83e7add3d4570727d6a171996fe14d5701bf22f Mon Sep 17 00:00:00 2001 From: d7t Date: Wed, 21 Jun 2023 14:57:44 -0600 Subject: [PATCH 24/24] Update persistence/trees/trees_test.go Co-authored-by: Daniel Olshansky --- persistence/trees/trees_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/trees/trees_test.go b/persistence/trees/trees_test.go index f1e3b27a2..17c502d1a 100644 --- a/persistence/trees/trees_test.go +++ b/persistence/trees/trees_test.go @@ -2,7 +2,7 @@ package trees import "testing" -// TECHDEBT #836: Tests added in https://github.com/pokt-network/pocket/pull/836 +// TECHDEBT(#836): Tests added in https://github.com/pokt-network/pocket/pull/836 func TestTreeStore_Update(t *testing.T) { // TODO: Write test case for the Update method t.Skip("TODO: Write test case for Update method")