Change ACLTree methods to include change builder
This commit is contained in:
parent
43797b1b84
commit
8372abb4e1
@ -1,13 +1,11 @@
|
|||||||
package acltree
|
package acltree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/account"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/account"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb"
|
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/thread"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/thread"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
|
|
||||||
"github.com/gogo/protobuf/proto"
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AddResultSummary int
|
type AddResultSummary int
|
||||||
@ -25,10 +23,9 @@ type AddResult struct {
|
|||||||
Summary AddResultSummary
|
Summary AddResultSummary
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Change add change content to include ACLChangeBuilder
|
|
||||||
type ACLTree interface {
|
type ACLTree interface {
|
||||||
ACLState() *ACLState
|
ACLState() *ACLState
|
||||||
AddContent(changeContent *ChangeContent) (*Change, error)
|
AddContent(f func(builder ChangeBuilder)) (*Change, error)
|
||||||
AddChanges(changes ...*Change) (AddResult, error)
|
AddChanges(changes ...*Change) (AddResult, error)
|
||||||
Heads() []string
|
Heads() []string
|
||||||
Iterate(func(change *Change) bool)
|
Iterate(func(change *Change) bool)
|
||||||
@ -41,13 +38,14 @@ type aclTree struct {
|
|||||||
accountData *account.AccountData
|
accountData *account.AccountData
|
||||||
|
|
||||||
fullTree *Tree
|
fullTree *Tree
|
||||||
aclTree *Tree // TODO: right now we don't use it, we can probably have only local var for now. This tree is built from start of the document
|
aclTreeFromStart *Tree // TODO: right now we don't use it, we can probably have only local var for now. This tree is built from start of the document
|
||||||
aclState *ACLState
|
aclState *ACLState
|
||||||
|
|
||||||
treeBuilder *treeBuilder
|
treeBuilder *treeBuilder
|
||||||
aclTreeBuilder *aclTreeBuilder
|
aclTreeBuilder *aclTreeBuilder
|
||||||
aclStateBuilder *aclStateBuilder
|
aclStateBuilder *aclStateBuilder
|
||||||
snapshotValidator *snapshotValidator
|
snapshotValidator *snapshotValidator
|
||||||
|
changeBuilder *changeBuilder
|
||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
@ -58,6 +56,7 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) {
|
|||||||
treeBuilder := newTreeBuilder(t, decoder)
|
treeBuilder := newTreeBuilder(t, decoder)
|
||||||
snapshotValidator := newSnapshotValidator(decoder, acc)
|
snapshotValidator := newSnapshotValidator(decoder, acc)
|
||||||
aclStateBuilder := newACLStateBuilder(decoder, acc)
|
aclStateBuilder := newACLStateBuilder(decoder, acc)
|
||||||
|
changeBuilder := newChangeBuilder()
|
||||||
|
|
||||||
aclTree := &aclTree{
|
aclTree := &aclTree{
|
||||||
thread: t,
|
thread: t,
|
||||||
@ -68,6 +67,7 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) {
|
|||||||
aclTreeBuilder: aclTreeBuilder,
|
aclTreeBuilder: aclTreeBuilder,
|
||||||
aclStateBuilder: aclStateBuilder,
|
aclStateBuilder: aclStateBuilder,
|
||||||
snapshotValidator: snapshotValidator,
|
snapshotValidator: snapshotValidator,
|
||||||
|
changeBuilder: changeBuilder,
|
||||||
}
|
}
|
||||||
err := aclTree.rebuildFromThread(false)
|
err := aclTree.rebuildFromThread(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -80,7 +80,7 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) {
|
|||||||
// TODO: this is not used for now, in future we should think about not making full tree rebuild
|
// TODO: this is not used for now, in future we should think about not making full tree rebuild
|
||||||
func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) {
|
func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) {
|
||||||
if validateSnapshot {
|
if validateSnapshot {
|
||||||
err = a.snapshotValidator.Init(a.aclTree)
|
err = a.snapshotValidator.Init(a.aclTreeFromStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -118,13 +118,13 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this from context as this is used only to validate snapshot
|
// TODO: remove this from context as this is used only to validate snapshot
|
||||||
a.aclTree, err = a.aclTreeBuilder.Build()
|
a.aclTreeFromStart, err = a.aclTreeBuilder.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fromStart {
|
if !fromStart {
|
||||||
err = a.snapshotValidator.Init(a.aclTree)
|
err = a.snapshotValidator.Init(a.aclTreeFromStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -150,98 +150,40 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this should not be the responsibility of ACLTree, move it somewhere else after testing
|
|
||||||
func (a *aclTree) getACLHeads() []string {
|
|
||||||
var aclTreeHeads []string
|
|
||||||
for _, head := range a.fullTree.Heads() {
|
|
||||||
if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
precedingHeads := a.getPrecedingACLHeads(head)
|
|
||||||
|
|
||||||
for _, aclHead := range precedingHeads {
|
|
||||||
if slice.FindPos(aclTreeHeads, aclHead) != -1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
aclTreeHeads = append(aclTreeHeads, aclHead)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return aclTreeHeads
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aclTree) getPrecedingACLHeads(head string) []string {
|
|
||||||
headChange := a.fullTree.attached[head]
|
|
||||||
|
|
||||||
if headChange.Content.GetAclData() != nil {
|
|
||||||
return []string{head}
|
|
||||||
} else {
|
|
||||||
return headChange.Content.AclHeadIds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aclTree) ACLState() *ACLState {
|
func (a *aclTree) ACLState() *ACLState {
|
||||||
return a.aclState
|
return a.aclState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aclTree) AddContent(changeContent *ChangeContent) (*Change, error) {
|
func (a *aclTree) AddContent(build func(builder ChangeBuilder)) (*Change, error) {
|
||||||
// TODO: add snapshot creation logic
|
// TODO: add snapshot creation logic
|
||||||
a.Lock()
|
a.Lock()
|
||||||
defer a.Unlock()
|
defer a.Unlock()
|
||||||
marshalled, err := changeContent.ChangesData.Marshal()
|
|
||||||
|
a.changeBuilder.Init(a.aclState, a.fullTree, a.accountData)
|
||||||
|
build(a.changeBuilder)
|
||||||
|
|
||||||
|
ch, marshalled, err := a.changeBuilder.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = a.aclState.applyChange(ch.Id, ch.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
encrypted, err := a.aclState.userReadKeys[a.aclState.currentReadKeyHash].
|
|
||||||
Encrypt(marshalled)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
aclChange := &pb.ACLChange{
|
|
||||||
TreeHeadIds: a.fullTree.Heads(),
|
|
||||||
AclHeadIds: a.getACLHeads(),
|
|
||||||
SnapshotBaseId: a.fullTree.RootId(),
|
|
||||||
AclData: changeContent.ACLData,
|
|
||||||
ChangesData: encrypted,
|
|
||||||
CurrentReadKeyHash: a.aclState.currentReadKeyHash,
|
|
||||||
Timestamp: 0,
|
|
||||||
Identity: a.accountData.Identity,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add CID creation logic based on content
|
|
||||||
ch := NewChange(changeContent.Id, aclChange)
|
|
||||||
ch.DecryptedDocumentChange = marshalled
|
|
||||||
|
|
||||||
fullMarshalledChange, err := proto.Marshal(aclChange)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
signature, err := a.accountData.SignKey.Sign(fullMarshalledChange)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if aclChange.AclData != nil {
|
|
||||||
// we can apply change right away without going through builder, because
|
|
||||||
err = a.aclState.applyChange(changeContent.Id, aclChange)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a.fullTree.AddFast(ch)
|
a.fullTree.AddFast(ch)
|
||||||
|
|
||||||
err = a.thread.AddRawChange(&thread.RawChange{
|
err = a.thread.AddRawChange(&thread.RawChange{
|
||||||
Payload: marshalled,
|
Payload: marshalled,
|
||||||
Signature: signature,
|
Signature: ch.Signature(),
|
||||||
Id: changeContent.Id,
|
Id: ch.Id,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
a.thread.SetHeads([]string{ch.Id})
|
a.thread.SetHeads([]string{ch.Id})
|
||||||
return a.fullTree.attached[changeContent.Id], nil
|
return ch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) {
|
func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) {
|
||||||
|
|||||||
101
acltree/changebuilder.go
Normal file
101
acltree/changebuilder.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package acltree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/account"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MarshalledChange = []byte
|
||||||
|
|
||||||
|
type ACLChangeBuilder interface {
|
||||||
|
UserAdd(identity string, encryptionKey keys.EncryptionPubKey)
|
||||||
|
AddId(id string) // TODO: this is only for testing
|
||||||
|
SetMakeSnapshot(bool) // TODO: who should decide this? probably ACLTree so we can delete it
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeBuilder interface {
|
||||||
|
ACLChangeBuilder
|
||||||
|
AddChangeContent(marshaler proto.Marshaler) // user code should be responsible for making regular snapshots
|
||||||
|
}
|
||||||
|
|
||||||
|
type changeBuilder struct {
|
||||||
|
aclState *ACLState
|
||||||
|
tree *Tree
|
||||||
|
acc *account.AccountData
|
||||||
|
|
||||||
|
aclData *pb.ACLChangeACLData
|
||||||
|
changeContent proto.Marshaler
|
||||||
|
id string
|
||||||
|
makeSnapshot bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChangeBuilder() *changeBuilder {
|
||||||
|
return &changeBuilder{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *changeBuilder) Init(state *ACLState, tree *Tree, acc *account.AccountData) {
|
||||||
|
c.aclState = state
|
||||||
|
c.tree = tree
|
||||||
|
c.acc = acc
|
||||||
|
|
||||||
|
c.aclData = &pb.ACLChangeACLData{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *changeBuilder) AddId(id string) {
|
||||||
|
c.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *changeBuilder) SetMakeSnapshot(b bool) {
|
||||||
|
c.makeSnapshot = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *changeBuilder) UserAdd(identity string, encryptionKey keys.EncryptionPubKey) {
|
||||||
|
//TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *changeBuilder) Build() (*Change, []byte, error) {
|
||||||
|
marshalled, err := c.changeContent.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted, err := c.aclState.userReadKeys[c.aclState.currentReadKeyHash].
|
||||||
|
Encrypt(marshalled)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
aclChange := &pb.ACLChange{
|
||||||
|
TreeHeadIds: c.tree.Heads(),
|
||||||
|
AclHeadIds: c.tree.ACLHeads(),
|
||||||
|
SnapshotBaseId: c.tree.RootId(), // TODO: add logic for ACL snapshot
|
||||||
|
AclData: c.aclData,
|
||||||
|
ChangesData: encrypted,
|
||||||
|
CurrentReadKeyHash: c.aclState.currentReadKeyHash,
|
||||||
|
Timestamp: 0,
|
||||||
|
Identity: c.acc.Identity,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add CID creation logic based on content
|
||||||
|
ch := NewChange(c.id, aclChange)
|
||||||
|
ch.DecryptedDocumentChange = marshalled
|
||||||
|
|
||||||
|
fullMarshalledChange, err := proto.Marshal(aclChange)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
signature, err := c.acc.SignKey.Sign(fullMarshalledChange)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
ch.Sign = signature
|
||||||
|
|
||||||
|
return ch, fullMarshalledChange, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *changeBuilder) AddChangeContent(marshaler proto.Marshaler) {
|
||||||
|
c.changeContent = marshaler
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -288,6 +289,34 @@ func (t *Tree) updateHeads() {
|
|||||||
sort.Strings(t.metaHeadIds)
|
sort.Strings(t.metaHeadIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tree) ACLHeads() []string {
|
||||||
|
var aclTreeHeads []string
|
||||||
|
for _, head := range t.Heads() {
|
||||||
|
if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
precedingHeads := t.getPrecedingACLHeads(head)
|
||||||
|
|
||||||
|
for _, aclHead := range precedingHeads {
|
||||||
|
if slice.FindPos(aclTreeHeads, aclHead) != -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
aclTreeHeads = append(aclTreeHeads, aclHead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aclTreeHeads
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tree) getPrecedingACLHeads(head string) []string {
|
||||||
|
headChange := t.attached[head]
|
||||||
|
|
||||||
|
if headChange.Content.GetAclData() != nil {
|
||||||
|
return []string{head}
|
||||||
|
} else {
|
||||||
|
return headChange.Content.AclHeadIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tree) iterate(start *Change, f func(c *Change) (isContinue bool)) {
|
func (t *Tree) iterate(start *Change, f func(c *Change) (isContinue bool)) {
|
||||||
it := newIterator()
|
it := newIterator()
|
||||||
defer freeIterator(it)
|
defer freeIterator(it)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user