diff --git a/commonspace/object/tree/objecttree/objecttree.go b/commonspace/object/tree/objecttree/objecttree.go index 4f1ada2f..ba2494b2 100644 --- a/commonspace/object/tree/objecttree/objecttree.go +++ b/commonspace/object/tree/objecttree/objecttree.go @@ -661,7 +661,10 @@ func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate err = list.ErrNoReadKey return } - + if c.Data == nil { + err = fmt.Errorf("no data in change %s", c.Id) + return + } decrypted, err = readKey.Decrypt(c.Data) return } diff --git a/commonspace/object/tree/objecttree/objecttree_test.go b/commonspace/object/tree/objecttree/objecttree_test.go index c8044544..9b768e72 100644 --- a/commonspace/object/tree/objecttree/objecttree_test.go +++ b/commonspace/object/tree/objecttree/objecttree_test.go @@ -135,7 +135,8 @@ func newStore(ctx context.Context, t *testing.T) anystore.DB { } func newNamedStore(ctx context.Context, t *testing.T, name string) anystore.DB { - db, err := anystore.Open(ctx, filepath.Join(t.TempDir(), name), nil) + path := filepath.Join(t.TempDir(), name) + db, err := anystore.Open(ctx, path, nil) require.NoError(t, err) t.Cleanup(func() { err := db.Close() @@ -143,7 +144,7 @@ func newNamedStore(ctx context.Context, t *testing.T, name string) anystore.DB { }) return testStore{ DB: db, - path: filepath.Join(t.TempDir(), name), + path: path, } } @@ -357,7 +358,8 @@ func TestObjectTree(t *testing.T) { IsEncrypted: true, }, aAccount.Acl) require.NoError(t, err) - aStore, _ := createStorage(ctx, root, storeA) + aStore, err := createStorage(ctx, root, storeA) + require.NoError(t, err) aTree, err := BuildKeyFilterableObjectTree(aStore, aAccount.Acl) require.NoError(t, err) _, err = aTree.AddContent(ctx, SignableChangeContent{ @@ -369,7 +371,8 @@ func TestObjectTree(t *testing.T) { }) require.NoError(t, err) storeB := copyStore(ctx, t, storeA.(testStore), "b") - bStore, _ := newStorage(ctx, root.Id, storeB) + bStore, err := newStorage(ctx, root.Id, storeB) + require.NoError(t, err) bTree, err := BuildKeyFilterableObjectTree(bStore, bAccount.Acl) require.NoError(t, err) err = exec.Execute("a.remove::b") @@ -394,14 +397,19 @@ func TestObjectTree(t *testing.T) { require.Equal(t, oldHeads, bTree.Heads()) rawAdded = allChanges(ctx, t, bStore) require.NoError(t, err) + validateStore := newStore(ctx, t) newTree, err := ValidateFilterRawTree(treestorage.TreeStorageCreatePayload{ RootRawChange: root, Changes: rawAdded, Heads: bTree.Heads(), - }, InMemoryStorageCreator{}, bAccount.Acl) + }, &tempTreeStorageCreator{ + store: validateStore, + }, bAccount.Acl) + require.NoError(t, err) + treeCopy, err := BuildObjectTree(newTree.Storage(), bAccount.Acl) require.NoError(t, err) chCount := 0 - err = newTree.IterateRoot(func(change *Change, decrypted []byte) (any, error) { + err = treeCopy.IterateRoot(func(change *Change, decrypted []byte) (any, error) { chCount++ return nil, nil }, func(change *Change) bool { @@ -576,11 +584,12 @@ func TestObjectTree(t *testing.T) { oTree, err := BuildObjectTree(storage, aclList) require.NoError(t, err) emptyDataTreeDeps = nonVerifiableTreeDeps + validateStore := newStore(ctx, t) err = ValidateRawTree(treestorage.TreeStorageCreatePayload{ RootRawChange: oTree.Header(), Heads: []string{root.Id}, Changes: allChanges(ctx, t, oTree.Storage()), - }, aclList) + }, aclList, validateStore) require.NoError(t, err) }) @@ -596,12 +605,12 @@ func TestObjectTree(t *testing.T) { storage, _ := createStorage(ctx, root, store) oTree, err := BuildObjectTree(storage, aclList) require.NoError(t, err) - emptyDataTreeDeps = nonVerifiableTreeDeps + validateStore := newStore(ctx, t) err = ValidateRawTree(treestorage.TreeStorageCreatePayload{ RootRawChange: oTree.Header(), Heads: []string{root.Id}, Changes: allChanges(ctx, t, oTree.Storage()), - }, aclList) + }, aclList, validateStore) require.Equal(t, ErrDerived, err) }) @@ -641,11 +650,12 @@ func TestObjectTree(t *testing.T) { require.NoError(t, err) chs := allChanges(ctx, t, storage) emptyDataTreeDeps = nonVerifiableTreeDeps + validateStore := newStore(ctx, t) err = ValidateRawTree(treestorage.TreeStorageCreatePayload{ RootRawChange: oTree.Header(), Heads: []string{oTree.Heads()[0]}, Changes: chs, - }, aclList) + }, aclList, validateStore) require.NoError(t, err) rawChs := allChanges(ctx, t, oTree.Storage()) require.NoError(t, err) @@ -684,11 +694,12 @@ func TestObjectTree(t *testing.T) { }) require.NoError(t, err) chs := allChanges(ctx, t, storage) + validateStore := newStore(ctx, t) err = ValidateRawTree(treestorage.TreeStorageCreatePayload{ RootRawChange: oTree.Header(), Heads: []string{oTree.Heads()[0]}, Changes: chs, - }, aclList) + }, aclList, validateStore) require.NoError(t, err) rawChs := allChanges(ctx, t, oTree.Storage()) require.NoError(t, err) @@ -705,21 +716,22 @@ func TestObjectTree(t *testing.T) { }) t.Run("validate from start, multiple snapshots, correct", func(t *testing.T) { - ctx := prepareTreeContext(t, aclList) - changeCreator := ctx.changeCreator + treeCtx := prepareTreeContext(t, aclList) + changeCreator := treeCtx.changeCreator rawChanges := []*treechangeproto.RawTreeChangeWithId{ - ctx.objTree.Header(), + treeCtx.objTree.Header(), changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"), } emptyDataTreeDeps = nonVerifiableTreeDeps + validateStore := newStore(ctx, t) err := ValidateRawTree(treestorage.TreeStorageCreatePayload{ - RootRawChange: ctx.objTree.Header(), + RootRawChange: treeCtx.objTree.Header(), Heads: []string{"3"}, Changes: rawChanges, - }, ctx.aclList) + }, treeCtx.aclList, validateStore) require.NoError(t, err) }) }) @@ -1465,38 +1477,40 @@ func TestObjectTree(t *testing.T) { }) t.Run("validate tree", func(t *testing.T) { - ctx := prepareTreeContext(t, aclList) - changeCreator := ctx.changeCreator + treeCtx := prepareTreeContext(t, aclList) + changeCreator := treeCtx.changeCreator rawChanges := []*treechangeproto.RawTreeChangeWithId{ - ctx.objTree.Header(), + treeCtx.objTree.Header(), changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"), } emptyDataTreeDeps = nonVerifiableTreeDeps + validateStore := newStore(ctx, t) err := ValidateRawTree(treestorage.TreeStorageCreatePayload{ - RootRawChange: ctx.objTree.Header(), + RootRawChange: treeCtx.objTree.Header(), Heads: []string{"3"}, Changes: rawChanges, - }, ctx.aclList) + }, treeCtx.aclList, validateStore) require.NoError(t, err) }) t.Run("fail to validate not connected tree", func(t *testing.T) { - ctx := prepareTreeContext(t, aclList) - changeCreator := ctx.changeCreator + treeCtx := prepareTreeContext(t, aclList) + changeCreator := treeCtx.changeCreator rawChanges := []*treechangeproto.RawTreeChangeWithId{ - ctx.objTree.Header(), + treeCtx.objTree.Header(), changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"), } emptyDataTreeDeps = nonVerifiableTreeDeps + validateStore := newStore(ctx, t) err := ValidateRawTree(treestorage.TreeStorageCreatePayload{ - RootRawChange: ctx.objTree.Header(), + RootRawChange: treeCtx.objTree.Header(), Heads: []string{"3"}, Changes: rawChanges, - }, ctx.aclList) + }, treeCtx.aclList, validateStore) require.True(t, errors.Is(err, ErrHasInvalidChanges)) }) diff --git a/commonspace/object/tree/objecttree/objecttreevalidator.go b/commonspace/object/tree/objecttree/objecttreevalidator.go index db723bab..e351609f 100644 --- a/commonspace/object/tree/objecttree/objecttreevalidator.go +++ b/commonspace/object/tree/objecttree/objecttreevalidator.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + anystore "github.com/anyproto/any-store" + "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/util/slice" @@ -13,10 +15,12 @@ type TreeStorageCreator interface { CreateTreeStorage(payload treestorage.TreeStorageCreatePayload) (Storage, error) } -type InMemoryStorageCreator struct{} +type tempTreeStorageCreator struct { + store anystore.DB +} -func (i InMemoryStorageCreator) CreateTreeStorage(payload treestorage.TreeStorageCreatePayload) (Storage, error) { - return nil, nil +func (t *tempTreeStorageCreator) CreateTreeStorage(payload treestorage.TreeStorageCreatePayload) (Storage, error) { + return createStorage(context.Background(), payload.RootRawChange, t.store) } type ValidatorFunc func(payload treestorage.TreeStorageCreatePayload, storageCreator TreeStorageCreator, aclList list.AclList) (ret ObjectTree, err error) @@ -241,7 +245,7 @@ func ValidateFilterRawTree(payload treestorage.TreeStorageCreatePayload, storage return tree, nil } -func ValidateRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList) (err error) { - _, err = ValidateRawTreeDefault(payload, InMemoryStorageCreator{}, aclList) +func ValidateRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList, store anystore.DB) (err error) { + _, err = ValidateRawTreeDefault(payload, &tempTreeStorageCreator{store: store}, aclList) return } diff --git a/commonspace/object/tree/objecttree/storage.go b/commonspace/object/tree/objecttree/storage.go index bc04abb7..393041ac 100644 --- a/commonspace/object/tree/objecttree/storage.go +++ b/commonspace/object/tree/objecttree/storage.go @@ -149,7 +149,7 @@ func newStorage(ctx context.Context, id string, store anystore.DB) (Storage, err Unique: true, } err = st.changesColl.EnsureIndex(ctx, orderIdx) - if err != nil { + if err != nil && !errors.Is(err, anystore.ErrIndexExists) { return nil, err } st.arena = &anyenc.Arena{}