Merge pull request #85 from anytypeio/GO-1227-add-custom-timestamps
This commit is contained in:
commit
ba5ca3d6be
@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/anytypeio/any-sync/util/cidutil"
|
"github.com/anytypeio/any-sync/util/cidutil"
|
||||||
"github.com/anytypeio/any-sync/util/crypto"
|
"github.com/anytypeio/any-sync/util/crypto"
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrEmptyChange = errors.New("change payload should not be empty")
|
var ErrEmptyChange = errors.New("change payload should not be empty")
|
||||||
@ -20,6 +19,7 @@ type BuilderContent struct {
|
|||||||
PrivKey crypto.PrivKey
|
PrivKey crypto.PrivKey
|
||||||
ReadKey crypto.SymKey
|
ReadKey crypto.SymKey
|
||||||
Content []byte
|
Content []byte
|
||||||
|
Timestamp int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type InitialContent struct {
|
type InitialContent struct {
|
||||||
@ -166,7 +166,7 @@ func (c *changeBuilder) Build(payload BuilderContent) (ch *Change, rawIdChange *
|
|||||||
AclHeadId: payload.AclHeadId,
|
AclHeadId: payload.AclHeadId,
|
||||||
SnapshotBaseId: payload.SnapshotBaseId,
|
SnapshotBaseId: payload.SnapshotBaseId,
|
||||||
ReadKeyId: payload.ReadKeyId,
|
ReadKeyId: payload.ReadKeyId,
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: payload.Timestamp,
|
||||||
Identity: identity,
|
Identity: identity,
|
||||||
IsSnapshot: payload.IsSnapshot,
|
IsSnapshot: payload.IsSnapshot,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -251,6 +251,10 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
|
|||||||
}
|
}
|
||||||
readKey = ot.currentReadKey
|
readKey = ot.currentReadKey
|
||||||
}
|
}
|
||||||
|
timestamp := content.Timestamp
|
||||||
|
if timestamp <= 0 {
|
||||||
|
timestamp = time.Now().Unix()
|
||||||
|
}
|
||||||
cnt = BuilderContent{
|
cnt = BuilderContent{
|
||||||
TreeHeadIds: ot.tree.Heads(),
|
TreeHeadIds: ot.tree.Heads(),
|
||||||
AclHeadId: ot.aclList.Head().Id,
|
AclHeadId: ot.aclList.Head().Id,
|
||||||
@ -260,6 +264,7 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
|
|||||||
PrivKey: content.Key,
|
PrivKey: content.Key,
|
||||||
ReadKey: readKey,
|
ReadKey: readKey,
|
||||||
Content: content.Data,
|
Content: content.Data,
|
||||||
|
Timestamp: timestamp,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testTreeContext struct {
|
type testTreeContext struct {
|
||||||
@ -19,13 +20,13 @@ type testTreeContext struct {
|
|||||||
objTree ObjectTree
|
objTree ObjectTree
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareAclList(t *testing.T) list.AclList {
|
func prepareAclList(t *testing.T) (list.AclList, *accountdata.AccountKeys) {
|
||||||
randKeys, err := accountdata.NewRandom()
|
randKeys, err := accountdata.NewRandom()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
aclList, err := list.NewTestDerivedAcl("spaceId", randKeys)
|
aclList, err := list.NewTestDerivedAcl("spaceId", randKeys)
|
||||||
require.NoError(t, err, "building acl list should be without error")
|
require.NoError(t, err, "building acl list should be without error")
|
||||||
|
|
||||||
return aclList
|
return aclList, randKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareHistoryTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTreeDeps) {
|
func prepareHistoryTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTreeDeps) {
|
||||||
@ -88,7 +89,57 @@ func prepareContext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectTree(t *testing.T) {
|
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 to current", func(t *testing.T) {
|
||||||
|
start := time.Now()
|
||||||
|
res, err := oTree.AddContent(ctx, SignableChangeContent{
|
||||||
|
Data: []byte("some"),
|
||||||
|
Key: keys.SignKey,
|
||||||
|
IsSnapshot: false,
|
||||||
|
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.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()
|
||||||
|
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) {
|
t.Run("add simple", func(t *testing.T) {
|
||||||
ctx := prepareTreeContext(t, aclList)
|
ctx := prepareTreeContext(t, aclList)
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import (
|
|||||||
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
||||||
"github.com/anytypeio/any-sync/util/crypto"
|
"github.com/anytypeio/any-sync/util/crypto"
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ObjectTreeCreatePayload struct {
|
type ObjectTreeCreatePayload struct {
|
||||||
@ -15,6 +13,8 @@ type ObjectTreeCreatePayload struct {
|
|||||||
ChangePayload []byte
|
ChangePayload []byte
|
||||||
SpaceId string
|
SpaceId string
|
||||||
IsEncrypted bool
|
IsEncrypted bool
|
||||||
|
Seed []byte
|
||||||
|
Timestamp int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type HistoryTreeParams struct {
|
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) {
|
func BuildEmptyDataObjectTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
|
||||||
rootChange, err := treeStorage.Root()
|
rootChange, err := treeStorage.Root()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -168,54 +155,7 @@ func BuildHistoryTree(params HistoryTreeParams) (HistoryTree, error) {
|
|||||||
return buildHistoryTree(deps, params)
|
return buildHistoryTree(deps, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDerivedObjectTree(
|
func CreateObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) {
|
||||||
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) {
|
|
||||||
aclList.RLock()
|
aclList.RLock()
|
||||||
aclHeadId := aclList.Head().Id
|
aclHeadId := aclList.Head().Id
|
||||||
aclList.RUnlock()
|
aclList.RUnlock()
|
||||||
@ -229,8 +169,8 @@ func createObjectTreeRoot(
|
|||||||
SpaceId: payload.SpaceId,
|
SpaceId: payload.SpaceId,
|
||||||
ChangeType: payload.ChangeType,
|
ChangeType: payload.ChangeType,
|
||||||
ChangePayload: payload.ChangePayload,
|
ChangePayload: payload.ChangePayload,
|
||||||
Timestamp: timestamp,
|
Timestamp: payload.Timestamp,
|
||||||
Seed: seed,
|
Seed: payload.Seed,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, root, err = NewChangeBuilder(crypto.NewKeyStorage(), nil).BuildRoot(cnt)
|
_, root, err = NewChangeBuilder(crypto.NewKeyStorage(), nil).BuildRoot(cnt)
|
||||||
|
|||||||
@ -4,9 +4,16 @@ import (
|
|||||||
"github.com/anytypeio/any-sync/util/crypto"
|
"github.com/anytypeio/any-sync/util/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SignableChangeContent is a payload to be passed when we are creating change
|
||||||
type SignableChangeContent struct {
|
type SignableChangeContent struct {
|
||||||
Data []byte
|
// Data is a data provided by the client
|
||||||
Key crypto.PrivKey
|
Data []byte
|
||||||
IsSnapshot bool
|
// 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
|
IsEncrypted bool
|
||||||
|
// Timestamp is a timestamp of change, if it is <= 0, then we use current timestamp
|
||||||
|
Timestamp int64
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,6 @@ type Space interface {
|
|||||||
DebugAllHeads() []headsync.TreeHeads
|
DebugAllHeads() []headsync.TreeHeads
|
||||||
Description() (SpaceDescription, error)
|
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)
|
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)
|
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)
|
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()
|
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) {
|
func (s *space) CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) {
|
||||||
if s.isClosed.Load() {
|
if s.isClosed.Load() {
|
||||||
err = ErrSpaceClosed
|
err = ErrSpaceClosed
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user