mirror of
https://github.com/anyproto/any-sync.git
synced 2025-06-08 05:57:03 +09:00
314 lines
9.4 KiB
Go
314 lines
9.4 KiB
Go
package objecttree
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
|
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
|
|
"github.com/anyproto/any-sync/util/crypto"
|
|
)
|
|
|
|
type ObjectTreeCreatePayload struct {
|
|
PrivKey crypto.PrivKey
|
|
ChangeType string
|
|
ChangePayload []byte
|
|
SpaceId string
|
|
IsEncrypted bool
|
|
Seed []byte
|
|
Timestamp int64
|
|
}
|
|
|
|
type ObjectTreeDerivePayload struct {
|
|
ChangeType string
|
|
ChangePayload []byte
|
|
SpaceId string
|
|
IsEncrypted bool
|
|
}
|
|
|
|
type HistoryTreeParams struct {
|
|
Storage Storage
|
|
AclList list.AclList
|
|
Heads []string
|
|
IncludeBeforeId bool
|
|
}
|
|
|
|
type objectTreeDeps struct {
|
|
changeBuilder ChangeBuilder
|
|
treeBuilder *treeBuilder
|
|
storage Storage
|
|
validator ObjectTreeValidator
|
|
aclList list.AclList
|
|
flusher Flusher
|
|
}
|
|
|
|
type BuildObjectTreeFunc = func(storage Storage, aclList list.AclList) (ObjectTree, error)
|
|
|
|
var defaultObjectTreeDeps = verifiableTreeDeps
|
|
|
|
func verifiableTreeDeps(
|
|
rootChange *treechangeproto.RawTreeChangeWithId,
|
|
storage Storage,
|
|
aclList list.AclList) objectTreeDeps {
|
|
changeBuilder := NewChangeBuilder(crypto.NewKeyStorage(), rootChange)
|
|
treeBuilder := newTreeBuilder(storage, changeBuilder)
|
|
return objectTreeDeps{
|
|
changeBuilder: changeBuilder,
|
|
treeBuilder: treeBuilder,
|
|
storage: storage,
|
|
validator: newTreeValidator(false, false),
|
|
aclList: aclList,
|
|
flusher: &defaultFlusher{},
|
|
}
|
|
}
|
|
|
|
var emptyDataTreeDeps = verifiableEmptyDataTreeDeps
|
|
|
|
func verifiableEmptyDataTreeDeps(
|
|
rootChange *treechangeproto.RawTreeChangeWithId,
|
|
storage Storage,
|
|
aclList list.AclList) objectTreeDeps {
|
|
changeBuilder := NewEmptyDataChangeBuilder(crypto.NewKeyStorage(), rootChange)
|
|
treeBuilder := newTreeBuilder(storage, changeBuilder)
|
|
return objectTreeDeps{
|
|
changeBuilder: changeBuilder,
|
|
treeBuilder: treeBuilder,
|
|
storage: storage,
|
|
validator: newTreeValidator(false, false),
|
|
aclList: aclList,
|
|
flusher: &defaultFlusher{},
|
|
}
|
|
}
|
|
|
|
func nonVerifiableEmptyDataTreeDeps(
|
|
rootChange *treechangeproto.RawTreeChangeWithId,
|
|
storage Storage,
|
|
aclList list.AclList) objectTreeDeps {
|
|
changeBuilder := &nonVerifiableChangeBuilder{NewEmptyDataChangeBuilder(crypto.NewKeyStorage(), rootChange)}
|
|
treeBuilder := newTreeBuilder(storage, changeBuilder)
|
|
return objectTreeDeps{
|
|
changeBuilder: changeBuilder,
|
|
treeBuilder: treeBuilder,
|
|
storage: storage,
|
|
validator: newTreeValidator(false, false),
|
|
aclList: aclList,
|
|
flusher: &defaultFlusher{},
|
|
}
|
|
}
|
|
|
|
func nonVerifiableTreeDeps(
|
|
rootChange *treechangeproto.RawTreeChangeWithId,
|
|
storage Storage,
|
|
aclList list.AclList) objectTreeDeps {
|
|
changeBuilder := &nonVerifiableChangeBuilder{NewChangeBuilder(newMockKeyStorage(), rootChange)}
|
|
treeBuilder := newTreeBuilder(storage, changeBuilder)
|
|
return objectTreeDeps{
|
|
changeBuilder: changeBuilder,
|
|
treeBuilder: treeBuilder,
|
|
storage: storage,
|
|
validator: &noOpTreeValidator{},
|
|
aclList: aclList,
|
|
flusher: &defaultFlusher{},
|
|
}
|
|
}
|
|
|
|
func BuildEmptyDataObjectTree(storage Storage, aclList list.AclList) (ObjectTree, error) {
|
|
rootChange, err := storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deps := emptyDataTreeDeps(rootChange.RawTreeChangeWithId(), storage, aclList)
|
|
return buildObjectTree(deps)
|
|
}
|
|
|
|
func BuildTestableTree(storage Storage, aclList list.AclList) (ObjectTree, error) {
|
|
root, _ := storage.Root(context.Background())
|
|
changeBuilder := &nonVerifiableChangeBuilder{
|
|
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root.RawTreeChangeWithId()),
|
|
}
|
|
deps := objectTreeDeps{
|
|
changeBuilder: changeBuilder,
|
|
treeBuilder: newTreeBuilder(storage, changeBuilder),
|
|
storage: storage,
|
|
validator: &noOpTreeValidator{},
|
|
aclList: aclList,
|
|
flusher: &defaultFlusher{},
|
|
}
|
|
|
|
return buildObjectTree(deps)
|
|
}
|
|
|
|
func BuildEmptyDataTestableTree(storage Storage, aclList list.AclList) (ObjectTree, error) {
|
|
root, _ := storage.Root(context.Background())
|
|
changeBuilder := &nonVerifiableChangeBuilder{
|
|
ChangeBuilder: NewEmptyDataChangeBuilder(newMockKeyStorage(), root.RawTreeChangeWithId()),
|
|
}
|
|
deps := objectTreeDeps{
|
|
changeBuilder: changeBuilder,
|
|
treeBuilder: newTreeBuilder(storage, changeBuilder),
|
|
storage: storage,
|
|
validator: &noOpTreeValidator{},
|
|
aclList: aclList,
|
|
flusher: &defaultFlusher{},
|
|
}
|
|
|
|
return buildObjectTree(deps)
|
|
}
|
|
|
|
func BuildMigratableObjectTree(storage Storage, aclList list.AclList) (ObjectTree, error) {
|
|
rootChange, err := storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get root: %w", err)
|
|
}
|
|
deps := nonVerifiableEmptyDataTreeDeps(rootChange.RawTreeChangeWithId(), storage, aclList)
|
|
return buildObjectTree(deps)
|
|
}
|
|
|
|
func BuildKeyFilterableObjectTree(storage Storage, aclList list.AclList) (ObjectTree, error) {
|
|
rootChange, err := storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deps := defaultObjectTreeDeps(rootChange.RawTreeChangeWithId(), storage, aclList)
|
|
deps.validator = newTreeValidator(true, true)
|
|
return buildObjectTree(deps)
|
|
}
|
|
|
|
func BuildEmptyDataKeyFilterableObjectTree(storage Storage, aclList list.AclList) (ObjectTree, error) {
|
|
rootChange, err := storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deps := emptyDataTreeDeps(rootChange.RawTreeChangeWithId(), storage, aclList)
|
|
deps.validator = newTreeValidator(true, true)
|
|
return buildObjectTree(deps)
|
|
}
|
|
|
|
func BuildObjectTree(storage Storage, aclList list.AclList) (ObjectTree, error) {
|
|
rootChange, err := storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deps := defaultObjectTreeDeps(rootChange.RawTreeChangeWithId(), storage, aclList)
|
|
return buildObjectTree(deps)
|
|
}
|
|
|
|
func BuildNonVerifiableHistoryTree(params HistoryTreeParams) (HistoryTree, error) {
|
|
rootChange, err := params.Storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deps := nonVerifiableTreeDeps(rootChange.RawTreeChangeWithId(), params.Storage, params.AclList)
|
|
return buildHistoryTree(deps, params)
|
|
}
|
|
|
|
func BuildHistoryTree(params HistoryTreeParams) (HistoryTree, error) {
|
|
rootChange, err := params.Storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deps := defaultObjectTreeDeps(rootChange.RawTreeChangeWithId(), params.Storage, params.AclList)
|
|
return buildHistoryTree(deps, params)
|
|
}
|
|
|
|
func CreateObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) {
|
|
aclList.RLock()
|
|
aclHeadId := aclList.Head().Id
|
|
aclList.RUnlock()
|
|
cnt := InitialContent{
|
|
AclHeadId: aclHeadId,
|
|
PrivKey: payload.PrivKey,
|
|
SpaceId: payload.SpaceId,
|
|
ChangeType: payload.ChangeType,
|
|
ChangePayload: payload.ChangePayload,
|
|
Timestamp: payload.Timestamp,
|
|
Seed: payload.Seed,
|
|
}
|
|
|
|
_, root, err = NewChangeBuilder(crypto.NewKeyStorage(), nil).BuildRoot(cnt)
|
|
return
|
|
}
|
|
|
|
func DeriveObjectTreeRoot(payload ObjectTreeDerivePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) {
|
|
cnt := InitialDerivedContent{
|
|
SpaceId: payload.SpaceId,
|
|
ChangeType: payload.ChangeType,
|
|
ChangePayload: payload.ChangePayload,
|
|
}
|
|
_, root, err = NewChangeBuilder(crypto.NewKeyStorage(), nil).BuildDerivedRoot(cnt)
|
|
return
|
|
}
|
|
|
|
func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) {
|
|
objTree := &objectTree{
|
|
id: deps.storage.Id(),
|
|
storage: deps.storage,
|
|
treeBuilder: deps.treeBuilder,
|
|
validator: deps.validator,
|
|
aclList: deps.aclList,
|
|
changeBuilder: deps.changeBuilder,
|
|
keys: make(map[string]crypto.SymKey),
|
|
newChangesBuf: make([]*Change, 0, 10),
|
|
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
|
|
notSeenIdxBuf: make([]int, 0, 10),
|
|
newSnapshotsBuf: make([]*Change, 0, 10),
|
|
flusher: deps.flusher,
|
|
}
|
|
|
|
err := objTree.rebuildFromStorage(nil, nil, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to rebuild from storage: %w", err)
|
|
}
|
|
|
|
// TODO: think about contexts
|
|
root, err := objTree.storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objTree.rawRoot = root.RawTreeChangeWithId()
|
|
|
|
// verifying root
|
|
header, err := objTree.changeBuilder.Unmarshall(objTree.rawRoot, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objTree.root = header
|
|
|
|
return objTree, nil
|
|
}
|
|
|
|
func buildHistoryTree(deps objectTreeDeps, params HistoryTreeParams) (ht HistoryTree, err error) {
|
|
objTree := &objectTree{
|
|
id: deps.storage.Id(),
|
|
storage: deps.storage,
|
|
treeBuilder: deps.treeBuilder,
|
|
validator: deps.validator,
|
|
aclList: deps.aclList,
|
|
changeBuilder: deps.changeBuilder,
|
|
keys: make(map[string]crypto.SymKey),
|
|
newChangesBuf: make([]*Change, 0, 10),
|
|
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
|
|
notSeenIdxBuf: make([]int, 0, 10),
|
|
newSnapshotsBuf: make([]*Change, 0, 10),
|
|
flusher: deps.flusher,
|
|
}
|
|
|
|
hTree := &historyTree{objectTree: objTree}
|
|
err = hTree.rebuildFromStorage(params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objTree.id = objTree.storage.Id()
|
|
root, err := objTree.storage.Root(context.Background())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objTree.rawRoot = root.RawTreeChangeWithId()
|
|
|
|
header, err := objTree.changeBuilder.Unmarshall(objTree.rawRoot, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
objTree.root = header
|
|
return hTree, nil
|
|
}
|