-
Notifications
You must be signed in to change notification settings - Fork 33
[Persistence] Adds atomic Update for TreeStore #861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d8d8ddf
1a5777c
f87518a
671598d
c988cbc
4ae3912
a601002
795ce87
4df81fe
f77f764
674d539
5ee3f42
713017b
7f947f9
d70a067
d107ed0
626ea5d
b3ab6b0
fc04ab0
2af89dd
8ed31bf
09097ef
07b64da
1ecfde6
a687768
800b2cb
0b22519
ba85105
7a7d95c
0f87f0b
cb4497d
6ce32dc
7bad92e
4d218dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| package trees | ||
|
|
||
| import ( | ||
| "encoding/hex" | ||
| "testing" | ||
|
|
||
| "github.com/golang/mock/gomock" | ||
| "github.com/pokt-network/pocket/logger" | ||
| mock_types "github.com/pokt-network/pocket/persistence/types/mocks" | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "github.com/pokt-network/pocket/shared/modules" | ||
| mockModules "github.com/pokt-network/pocket/shared/modules/mocks" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| const ( | ||
| // the root hash of a tree store where each tree is empty but present and initialized | ||
Olshansk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| h0 = "302f2956c084cc3e0e760cf1b8c2da5de79c45fa542f68a660a5fc494b486972" | ||
| // the root hash of a tree store where each tree has has key foo value bar added to it | ||
| h1 = "7d5712ea1507915c40e295845fa58773baa405b24b87e9d99761125d826ff915" | ||
| ) | ||
|
|
||
| func TestTreeStore_AtomicUpdatesWithSuccessfulRollback(t *testing.T) { | ||
| ctrl := gomock.NewController(t) | ||
|
|
||
| mockTxIndexer := mock_types.NewMockTxIndexer(ctrl) | ||
| mockBus := mockModules.NewMockBus(ctrl) | ||
| mockPersistenceMod := mockModules.NewMockPersistenceModule(ctrl) | ||
|
|
||
| mockBus.EXPECT().GetPersistenceModule().AnyTimes().Return(mockPersistenceMod) | ||
| mockPersistenceMod.EXPECT().GetTxIndexer().AnyTimes().Return(mockTxIndexer) | ||
|
|
||
| ts := &treeStore{ | ||
| logger: logger.Global.CreateLoggerForModule(modules.TreeStoreSubmoduleName), | ||
| treeStoreDir: ":memory:", | ||
| } | ||
| require.NoError(t, ts.setupTrees()) | ||
| require.NotEmpty(t, ts.merkleTrees[TransactionsTreeName]) | ||
|
|
||
| hash0 := ts.getStateHash() | ||
| require.NotEmpty(t, hash0) | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require.Equal(t, hash0, h0) | ||
|
|
||
| require.NoError(t, ts.Savepoint()) | ||
|
|
||
| // insert test data into every tree | ||
| for _, treeName := range stateTreeNames { | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| err := ts.merkleTrees[treeName].tree.Update([]byte("foo"), []byte("bar")) | ||
| require.NoError(t, err) | ||
| } | ||
|
|
||
| // commit the above changes | ||
| require.NoError(t, ts.Commit()) | ||
|
|
||
| // assert state hash is changed | ||
| hash1 := ts.getStateHash() | ||
| require.NotEmpty(t, hash1) | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require.NotEqual(t, hash0, hash1) | ||
| require.Equal(t, hash1, h1) | ||
|
|
||
| // set a new savepoint | ||
| require.NoError(t, ts.Savepoint()) | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require.NotEmpty(t, ts.prevState.merkleTrees) | ||
| require.NotEmpty(t, ts.prevState.rootTree) | ||
| // assert that savepoint creation doesn't mutate state hash | ||
| require.Equal(t, hash1, hex.EncodeToString(ts.prevState.rootTree.tree.Root())) | ||
|
|
||
| // verify that creating a savepoint does not change state hash | ||
| hash2 := ts.getStateHash() | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
Olshansk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require.Equal(t, hash2, hash1) | ||
| require.Equal(t, hash2, h1) | ||
|
|
||
| // validate that state tree was updated and a previous savepoint is created | ||
| for _, treeName := range stateTreeNames { | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require.NotEmpty(t, ts.merkleTrees[treeName]) | ||
| require.NotEmpty(t, ts.prevState.merkleTrees[treeName]) | ||
| } | ||
|
|
||
| // insert additional test data into all of the trees | ||
| for _, treeName := range stateTreeNames { | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require.NoError(t, ts.merkleTrees[treeName].tree.Update([]byte("fiz"), []byte("buz"))) | ||
| } | ||
|
|
||
| // rollback the changes made to the trees above BEFORE anything was committed | ||
| err := ts.Rollback() | ||
| require.NoError(t, err) | ||
|
|
||
| // validate that the state hash is unchanged after new data was inserted but rolled back before commitment | ||
| hash3 := ts.getStateHash() | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require.Equal(t, hash3, hash2) | ||
| require.Equal(t, hash3, h1) | ||
| } | ||
Olshansk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| //go:build test | ||
|
|
||
| package trees | ||
|
Comment on lines
+1
to
+3
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Here, we have the Feels inconsistent, but not sure if this is general best practice for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was following the prescription of TestUtils for testing unexported members like previously discussed. |
||
|
|
||
| import ( | ||
| "crypto/sha256" | ||
| "hash" | ||
| ) | ||
|
|
||
| type TreeStore = treeStore | ||
|
|
||
| var SMTTreeHasher hash.Hash = sha256.New() | ||
dylanlott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package trees | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "testing" | ||
|
|
||
| "github.com/pokt-network/pocket/persistence/kvstore" | ||
| "github.com/pokt-network/smt" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestTreeStore_Prove(t *testing.T) { | ||
| nodeStore := kvstore.NewMemKVStore() | ||
| tree := smt.NewSparseMerkleTree(nodeStore, smtTreeHasher) | ||
| testTree := &stateTree{ | ||
| name: "test", | ||
| tree: tree, | ||
| nodeStore: nodeStore, | ||
| } | ||
|
|
||
| require.NoError(t, testTree.tree.Update([]byte("key"), []byte("value"))) | ||
| require.NoError(t, testTree.tree.Commit()) | ||
|
|
||
| treeStore := &treeStore{ | ||
| merkleTrees: make(map[string]*stateTree, 1), | ||
| } | ||
| treeStore.merkleTrees["test"] = testTree | ||
|
|
||
| testCases := []struct { | ||
| name string | ||
| treeName string | ||
| key []byte | ||
| value []byte | ||
| valid bool | ||
| expectedErr error | ||
| }{ | ||
| { | ||
| name: "valid inclusion proof: key and value in tree", | ||
| treeName: "test", | ||
| key: []byte("key"), | ||
| value: []byte("value"), | ||
| valid: true, | ||
| expectedErr: nil, | ||
| }, | ||
| { | ||
| name: "valid exclusion proof: key not in tree", | ||
| treeName: "test", | ||
| key: []byte("key2"), | ||
| value: nil, | ||
| valid: true, | ||
| expectedErr: nil, | ||
| }, | ||
| { | ||
| name: "invalid proof: tree not in store", | ||
| treeName: "unstored tree", | ||
| key: []byte("key"), | ||
| value: []byte("value"), | ||
| valid: false, | ||
| expectedErr: fmt.Errorf("tree not found: %s", "unstored tree"), | ||
| }, | ||
| { | ||
| name: "invalid inclusion proof: key in tree, wrong value", | ||
| treeName: "test", | ||
| key: []byte("key"), | ||
| value: []byte("wrong value"), | ||
| valid: false, | ||
| expectedErr: nil, | ||
| }, | ||
| { | ||
| name: "invalid exclusion proof: key in tree", | ||
| treeName: "test", | ||
| key: []byte("key"), | ||
| value: nil, | ||
| valid: false, | ||
| expectedErr: nil, | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| valid, err := treeStore.Prove(tc.treeName, tc.key, tc.value) | ||
| require.Equal(t, valid, tc.valid) | ||
| if tc.expectedErr == nil { | ||
| require.NoError(t, err) | ||
| return | ||
| } | ||
| require.ErrorAs(t, err, &tc.expectedErr) | ||
| }) | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.