From 0cb7ae110058b2a81453018e3e2fcdd11233b1ac Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 4 May 2023 11:10:08 +0200 Subject: [PATCH 1/4] Add timestamp externally in tree --- .../tree/objecttree/objecttreefactory.go | 70 ++----------------- commonspace/space.go | 18 ----- 2 files changed, 5 insertions(+), 83 deletions(-) diff --git a/commonspace/object/tree/objecttree/objecttreefactory.go b/commonspace/object/tree/objecttree/objecttreefactory.go index dfa78f15..dc650bbb 100644 --- a/commonspace/object/tree/objecttree/objecttreefactory.go +++ b/commonspace/object/tree/objecttree/objecttreefactory.go @@ -5,8 +5,6 @@ import ( "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto" "github.com/anytypeio/any-sync/commonspace/object/tree/treestorage" "github.com/anytypeio/any-sync/util/crypto" - "math/rand" - "time" ) type ObjectTreeCreatePayload struct { @@ -15,6 +13,8 @@ type ObjectTreeCreatePayload struct { ChangePayload []byte SpaceId string IsEncrypted bool + Seed []byte + Timestamp int64 } type HistoryTreeParams struct { @@ -85,19 +85,6 @@ func nonVerifiableTreeDeps( } } -func CreateObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) { - bytes := make([]byte, 32) - _, err = rand.Read(bytes) - if err != nil { - return - } - return createObjectTreeRoot(payload, time.Now().Unix(), bytes, aclList) -} - -func DeriveObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) { - return createObjectTreeRoot(payload, 0, nil, aclList) -} - func BuildEmptyDataObjectTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) { rootChange, err := treeStorage.Root() if err != nil { @@ -168,54 +155,7 @@ func BuildHistoryTree(params HistoryTreeParams) (HistoryTree, error) { return buildHistoryTree(deps, params) } -func CreateDerivedObjectTree( - payload ObjectTreeCreatePayload, - aclList list.AclList, - createStorage treestorage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) { - return createObjectTree(payload, 0, nil, aclList, createStorage) -} - -func CreateObjectTree( - payload ObjectTreeCreatePayload, - aclList list.AclList, - createStorage treestorage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) { - bytes := make([]byte, 32) - _, err = rand.Read(bytes) - if err != nil { - return - } - return createObjectTree(payload, time.Now().Unix(), bytes, aclList, createStorage) -} - -func createObjectTree( - payload ObjectTreeCreatePayload, - timestamp int64, - seed []byte, - aclList list.AclList, - createStorage treestorage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) { - raw, err := createObjectTreeRoot(payload, timestamp, seed, aclList) - if err != nil { - return - } - - // create storage - st, err := createStorage(treestorage.TreeStorageCreatePayload{ - RootRawChange: raw, - Changes: []*treechangeproto.RawTreeChangeWithId{raw}, - Heads: []string{raw.Id}, - }) - if err != nil { - return - } - - return BuildObjectTree(st, aclList) -} - -func createObjectTreeRoot( - payload ObjectTreeCreatePayload, - timestamp int64, - seed []byte, - aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) { +func CreateObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) { aclList.RLock() aclHeadId := aclList.Head().Id aclList.RUnlock() @@ -229,8 +169,8 @@ func createObjectTreeRoot( SpaceId: payload.SpaceId, ChangeType: payload.ChangeType, ChangePayload: payload.ChangePayload, - Timestamp: timestamp, - Seed: seed, + Timestamp: payload.Timestamp, + Seed: payload.Seed, } _, root, err = NewChangeBuilder(crypto.NewKeyStorage(), nil).BuildRoot(cnt) diff --git a/commonspace/space.go b/commonspace/space.go index 2bcbb4da..6df03734 100644 --- a/commonspace/space.go +++ b/commonspace/space.go @@ -88,7 +88,6 @@ type Space interface { DebugAllHeads() []headsync.TreeHeads Description() (SpaceDescription, error) - DeriveTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) PutTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload, listener updatelistener.UpdateListener) (t objecttree.ObjectTree, err error) BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t objecttree.ObjectTree, err error) @@ -242,23 +241,6 @@ func (s *space) DebugAllHeads() []headsync.TreeHeads { return s.headSync.DebugAllHeads() } -func (s *space) DeriveTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) { - if s.isClosed.Load() { - err = ErrSpaceClosed - return - } - root, err := objecttree.DeriveObjectTreeRoot(payload, s.aclList) - if err != nil { - return - } - res = treestorage.TreeStorageCreatePayload{ - RootRawChange: root, - Changes: []*treechangeproto.RawTreeChangeWithId{root}, - Heads: []string{root.Id}, - } - return -} - func (s *space) CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) { if s.isClosed.Load() { err = ErrSpaceClosed From d26bce0deade8044a0c82a8de70903b94848613b Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 4 May 2023 14:12:29 +0200 Subject: [PATCH 2/4] Send timestamp to addcontent --- .../object/tree/objecttree/changebuilder.go | 4 +- .../object/tree/objecttree/objecttree.go | 5 ++ .../object/tree/objecttree/objecttree_test.go | 54 +++++++++++++++++-- .../object/tree/objecttree/signablecontent.go | 13 +++-- 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/commonspace/object/tree/objecttree/changebuilder.go b/commonspace/object/tree/objecttree/changebuilder.go index 28b71ec9..31653b34 100644 --- a/commonspace/object/tree/objecttree/changebuilder.go +++ b/commonspace/object/tree/objecttree/changebuilder.go @@ -6,7 +6,6 @@ import ( "github.com/anytypeio/any-sync/util/cidutil" "github.com/anytypeio/any-sync/util/crypto" "github.com/gogo/protobuf/proto" - "time" ) var ErrEmptyChange = errors.New("change payload should not be empty") @@ -20,6 +19,7 @@ type BuilderContent struct { PrivKey crypto.PrivKey ReadKey crypto.SymKey Content []byte + Timestamp int64 } type InitialContent struct { @@ -166,7 +166,7 @@ func (c *changeBuilder) Build(payload BuilderContent) (ch *Change, rawIdChange * AclHeadId: payload.AclHeadId, SnapshotBaseId: payload.SnapshotBaseId, ReadKeyId: payload.ReadKeyId, - Timestamp: time.Now().Unix(), + Timestamp: payload.Timestamp, Identity: identity, IsSnapshot: payload.IsSnapshot, } diff --git a/commonspace/object/tree/objecttree/objecttree.go b/commonspace/object/tree/objecttree/objecttree.go index f81a9ddd..d0fdc1ec 100644 --- a/commonspace/object/tree/objecttree/objecttree.go +++ b/commonspace/object/tree/objecttree/objecttree.go @@ -251,6 +251,10 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt } readKey = ot.currentReadKey } + timestamp := content.Timestamp + if timestamp <= 0 { + timestamp = time.Now().Unix() + } cnt = BuilderContent{ TreeHeadIds: ot.tree.Heads(), AclHeadId: ot.aclList.Head().Id, @@ -260,6 +264,7 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt PrivKey: content.Key, ReadKey: readKey, Content: content.Data, + Timestamp: content.Timestamp, } return } diff --git a/commonspace/object/tree/objecttree/objecttree_test.go b/commonspace/object/tree/objecttree/objecttree_test.go index 4d531d9d..bde899e9 100644 --- a/commonspace/object/tree/objecttree/objecttree_test.go +++ b/commonspace/object/tree/objecttree/objecttree_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "testing" + "time" ) type testTreeContext struct { @@ -19,13 +20,13 @@ type testTreeContext struct { objTree ObjectTree } -func prepareAclList(t *testing.T) list.AclList { +func prepareAclList(t *testing.T) (list.AclList, *accountdata.AccountKeys) { randKeys, err := accountdata.NewRandom() require.NoError(t, err) aclList, err := list.NewTestDerivedAcl("spaceId", randKeys) require.NoError(t, err, "building acl list should be without error") - return aclList + return aclList, randKeys } func prepareHistoryTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTreeDeps) { @@ -88,7 +89,54 @@ func prepareContext( } func TestObjectTree(t *testing.T) { - aclList := prepareAclList(t) + aclList, keys := prepareAclList(t) + ctx := context.Background() + + t.Run("add content", func(t *testing.T) { + root, err := CreateObjectTreeRoot(ObjectTreeCreatePayload{ + PrivKey: keys.SignKey, + ChangeType: "changeType", + ChangePayload: nil, + SpaceId: "spaceId", + IsEncrypted: true, + }, aclList) + require.NoError(t, err) + store, _ := treestorage.NewInMemoryTreeStorage(root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root}) + oTree, err := BuildObjectTree(store, aclList) + require.NoError(t, err) + + t.Run("0 timestamp is changed", func(t *testing.T) { + res, err := oTree.AddContent(ctx, SignableChangeContent{ + Data: []byte("some"), + Key: keys.SignKey, + IsSnapshot: false, + IsEncrypted: true, + Timestamp: 0, + }) + require.NoError(t, err) + require.Len(t, oTree.Heads(), 1) + require.Equal(t, res.Added[0].Id, oTree.Heads()[0]) + ch, err := oTree.(*objectTree).changeBuilder.Unmarshall(res.Added[0], true) + require.NoError(t, err) + require.NotEqual(t, ch.Timestamp, 0) + }) + t.Run("timestamp is set correctly", func(t *testing.T) { + someTs := time.Now().Add(time.Hour).Unix() + res, err := oTree.AddContent(ctx, SignableChangeContent{ + Data: []byte("some"), + Key: keys.SignKey, + IsSnapshot: false, + IsEncrypted: true, + Timestamp: someTs, + }) + require.NoError(t, err) + require.Len(t, oTree.Heads(), 1) + require.Equal(t, res.Added[0].Id, oTree.Heads()[0]) + ch, err := oTree.(*objectTree).changeBuilder.Unmarshall(res.Added[0], true) + require.NoError(t, err) + require.Equal(t, ch.Timestamp, someTs) + }) + }) t.Run("add simple", func(t *testing.T) { ctx := prepareTreeContext(t, aclList) diff --git a/commonspace/object/tree/objecttree/signablecontent.go b/commonspace/object/tree/objecttree/signablecontent.go index e56ac9f1..a48fef5b 100644 --- a/commonspace/object/tree/objecttree/signablecontent.go +++ b/commonspace/object/tree/objecttree/signablecontent.go @@ -4,9 +4,16 @@ import ( "github.com/anytypeio/any-sync/util/crypto" ) +// SignableChangeContent is a payload to be passed when we are creating change type SignableChangeContent struct { - Data []byte - Key crypto.PrivKey - IsSnapshot bool + // Data is a data provided by the client + Data []byte + // Key is the key which will be used to sign the change + Key crypto.PrivKey + // IsSnapshot tells if the change has snapshot of all previous data + IsSnapshot bool + // IsEncrypted tells if we encrypt the data with the relevant symmetric key IsEncrypted bool + // Timestamp is a timestamp of change, if it is <= 0, then we use current timestamp + Timestamp int64 } From ce9fcb5b0a7469b12cd4bc15fa8a734db41845c9 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 4 May 2023 14:58:17 +0200 Subject: [PATCH 3/4] Change timestamp condition --- commonspace/object/tree/objecttree/objecttree.go | 2 +- commonspace/object/tree/objecttree/objecttree_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/commonspace/object/tree/objecttree/objecttree.go b/commonspace/object/tree/objecttree/objecttree.go index d0fdc1ec..d75f76c1 100644 --- a/commonspace/object/tree/objecttree/objecttree.go +++ b/commonspace/object/tree/objecttree/objecttree.go @@ -264,7 +264,7 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt PrivKey: content.Key, ReadKey: readKey, Content: content.Data, - Timestamp: content.Timestamp, + Timestamp: timestamp, } return } diff --git a/commonspace/object/tree/objecttree/objecttree_test.go b/commonspace/object/tree/objecttree/objecttree_test.go index bde899e9..64bd4292 100644 --- a/commonspace/object/tree/objecttree/objecttree_test.go +++ b/commonspace/object/tree/objecttree/objecttree_test.go @@ -118,7 +118,7 @@ func TestObjectTree(t *testing.T) { require.Equal(t, res.Added[0].Id, oTree.Heads()[0]) ch, err := oTree.(*objectTree).changeBuilder.Unmarshall(res.Added[0], true) require.NoError(t, err) - require.NotEqual(t, ch.Timestamp, 0) + require.NotZero(t, ch.Timestamp) }) t.Run("timestamp is set correctly", func(t *testing.T) { someTs := time.Now().Add(time.Hour).Unix() From 7294a74de088cbfdeb5d2e7800ce52495f9c8e3f Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 4 May 2023 16:25:41 +0200 Subject: [PATCH 4/4] Improve timestamp test --- commonspace/object/tree/objecttree/objecttree_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/commonspace/object/tree/objecttree/objecttree_test.go b/commonspace/object/tree/objecttree/objecttree_test.go index 64bd4292..a60cfde6 100644 --- a/commonspace/object/tree/objecttree/objecttree_test.go +++ b/commonspace/object/tree/objecttree/objecttree_test.go @@ -105,7 +105,8 @@ func TestObjectTree(t *testing.T) { oTree, err := BuildObjectTree(store, aclList) require.NoError(t, err) - t.Run("0 timestamp is changed", func(t *testing.T) { + t.Run("0 timestamp is changed to current", func(t *testing.T) { + start := time.Now() res, err := oTree.AddContent(ctx, SignableChangeContent{ Data: []byte("some"), Key: keys.SignKey, @@ -113,12 +114,14 @@ func TestObjectTree(t *testing.T) { IsEncrypted: true, Timestamp: 0, }) + end := time.Now() require.NoError(t, err) require.Len(t, oTree.Heads(), 1) require.Equal(t, res.Added[0].Id, oTree.Heads()[0]) ch, err := oTree.(*objectTree).changeBuilder.Unmarshall(res.Added[0], true) require.NoError(t, err) - require.NotZero(t, ch.Timestamp) + require.GreaterOrEqual(t, start.Unix(), ch.Timestamp) + require.LessOrEqual(t, end.Unix(), ch.Timestamp) }) t.Run("timestamp is set correctly", func(t *testing.T) { someTs := time.Now().Add(time.Hour).Unix()