diff --git a/acltree/aclstate.go b/acltree/aclstate.go index 7fce4f3e..f2eaad3e 100644 --- a/acltree/aclstate.go +++ b/acltree/aclstate.go @@ -17,7 +17,7 @@ var ErrUserRemoved = errors.New("user was removed from the document") var ErrDocumentForbidden = errors.New("your user was forbidden access to the document") var ErrUserAlreadyExists = errors.New("user already exists") -type aclState struct { +type ACLState struct { currentReadKeyHash uint64 userReadKeys map[uint64]*symmetric.Key userStates map[string]*pb.ACLChangeUserState @@ -27,12 +27,12 @@ type aclState struct { identity string } -func NewACLStateFromSnapshot( +func newACLStateFromSnapshot( snapshot *pb.ACLChangeACLSnapshot, identity string, encryptionKey keys.EncryptionPrivKey, - signingPubKeyDecoder keys.SigningPubKeyDecoder) (*aclState, error) { - st := &aclState{ + signingPubKeyDecoder keys.SigningPubKeyDecoder) (*ACLState, error) { + st := &ACLState{ identity: identity, encryptionKey: encryptionKey, userReadKeys: make(map[uint64]*symmetric.Key), @@ -47,7 +47,7 @@ func NewACLStateFromSnapshot( return st, nil } -func (st *aclState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) error { +func (st *ACLState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) error { state := snapshot.AclState for _, userState := range state.UserStates { st.userStates[userState.Identity] = userState @@ -74,21 +74,21 @@ func (st *aclState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) erro return nil } -func (st *aclState) ApplyChange(changeId string, change *pb.ACLChange) error { +func (st *ACLState) applyChange(changeId string, change *pb.ACLChange) error { // we can't check this for the user which is joining, because it will not be in our list if !st.isUserJoin(change) { - // we check signature when we add this to the tree, so no need to do it here + // we check signature when we add this to the Tree, so no need to do it here if _, exists := st.userStates[change.Identity]; !exists { return ErrNoSuchUser } - if !st.HasPermission(change.Identity, pb.ACLChange_Admin) { + if !st.hasPermission(change.Identity, pb.ACLChange_Admin) { return fmt.Errorf("user %s must have admin permissions", change.Identity) } } for _, ch := range change.GetAclData().GetAclContent() { - if err := st.applyChange(changeId, ch); err != nil { + if err := st.applyChangeContent(changeId, ch); err != nil { //log.Infof("error while applying changes: %v; ignore", err) return err } @@ -98,7 +98,7 @@ func (st *aclState) ApplyChange(changeId string, change *pb.ACLChange) error { } // TODO: remove changeId, because it is not needed -func (st *aclState) applyChange(changeId string, ch *pb.ACLChangeACLContentValue) error { +func (st *ACLState) applyChangeContent(changeId string, ch *pb.ACLChangeACLContentValue) error { switch { case ch.GetUserPermissionChange() != nil: return st.applyUserPermissionChange(ch.GetUserPermissionChange()) @@ -117,7 +117,7 @@ func (st *aclState) applyChange(changeId string, ch *pb.ACLChangeACLContentValue } } -func (st *aclState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChange) error { +func (st *ACLState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChange) error { if _, exists := st.userStates[ch.Identity]; !exists { return ErrNoSuchUser } @@ -126,12 +126,12 @@ func (st *aclState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChan return nil } -func (st *aclState) applyUserInvite(changeId string, ch *pb.ACLChangeUserInvite) error { +func (st *ACLState) applyUserInvite(changeId string, ch *pb.ACLChangeUserInvite) error { st.userInvites[changeId] = ch return nil } -func (st *aclState) applyUserJoin(ch *pb.ACLChangeUserJoin) error { +func (st *ACLState) applyUserJoin(ch *pb.ACLChangeUserJoin) error { invite, exists := st.userInvites[ch.UserInviteChangeId] if !exists { return fmt.Errorf("no such invite with id %s", ch.UserInviteChangeId) @@ -188,7 +188,7 @@ func (st *aclState) applyUserJoin(ch *pb.ACLChangeUserJoin) error { return nil } -func (st *aclState) applyUserAdd(ch *pb.ACLChangeUserAdd) error { +func (st *ACLState) applyUserAdd(ch *pb.ACLChangeUserAdd) error { if _, exists := st.userStates[ch.Identity]; exists { return ErrUserAlreadyExists } @@ -203,7 +203,7 @@ func (st *aclState) applyUserAdd(ch *pb.ACLChangeUserAdd) error { return nil } -func (st *aclState) applyUserRemove(ch *pb.ACLChangeUserRemove) error { +func (st *ACLState) applyUserRemove(ch *pb.ACLChangeUserRemove) error { if ch.Identity == st.identity { return ErrDocumentForbidden } @@ -235,7 +235,7 @@ func (st *aclState) applyUserRemove(ch *pb.ACLChangeUserRemove) error { return nil } -func (st *aclState) applyUserConfirm(ch *pb.ACLChangeUserConfirm) error { +func (st *ACLState) applyUserConfirm(ch *pb.ACLChangeUserConfirm) error { if _, exists := st.userStates[ch.Identity]; !exists { return ErrNoSuchUser } @@ -245,7 +245,7 @@ func (st *aclState) applyUserConfirm(ch *pb.ACLChangeUserConfirm) error { return nil } -func (st *aclState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) { +func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) { decrypted, err := st.encryptionKey.Decrypt(msg) if err != nil { return nil, 0, ErrFailedToDecrypt @@ -261,7 +261,7 @@ func (st *aclState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, e return key, hasher.Sum64(), nil } -func (st *aclState) HasPermission(identity string, permission pb.ACLChangeUserPermissions) bool { +func (st *ACLState) hasPermission(identity string, permission pb.ACLChangeUserPermissions) bool { state, exists := st.userStates[identity] if !exists { return false @@ -270,12 +270,12 @@ func (st *aclState) HasPermission(identity string, permission pb.ACLChangeUserPe return state.Permissions == permission } -func (st *aclState) isUserJoin(ch *pb.ACLChange) bool { +func (st *ACLState) isUserJoin(ch *pb.ACLChange) bool { // if we have a UserJoin, then it should always be the first one applied return ch.AclData.GetAclContent() != nil && ch.AclData.GetAclContent()[0].GetUserJoin() != nil } -func (st *aclState) GetPermissionDecreasedUsers(ch *pb.ACLChange) (identities []*pb.ACLChangeUserPermissionChange) { +func (st *ACLState) getPermissionDecreasedUsers(ch *pb.ACLChange) (identities []*pb.ACLChangeUserPermissionChange) { // this should be called after general checks are completed if ch.GetAclData().GetAclContent() == nil { return nil @@ -307,7 +307,7 @@ func (st *aclState) GetPermissionDecreasedUsers(ch *pb.ACLChange) (identities [] return identities } -func (st *aclState) Equal(other *aclState) bool { +func (st *ACLState) equal(other *ACLState) bool { if st == nil && other == nil { return true } @@ -350,3 +350,8 @@ func (st *aclState) Equal(other *aclState) bool { // TODO: add detailed user invites comparison + compare other stuff return true } + +func (st *ACLState) GetUserStates() map[string]*pb.ACLChangeUserState { + // TODO: we should provide better API that would not allow to change this map from the outside + return st.userStates +} diff --git a/acltree/aclstatebuilder.go b/acltree/aclstatebuilder.go index e09e2538..3eb62783 100644 --- a/acltree/aclstatebuilder.go +++ b/acltree/aclstatebuilder.go @@ -8,9 +8,9 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" ) -type ACLStateBuilder struct { +type aclStateBuilder struct { tree *Tree - aclState *aclState + aclState *ACLState identity string key keys.EncryptionPrivKey decoder keys.SigningPubKeyDecoder @@ -21,33 +21,33 @@ type decreasedPermissionsParameters struct { startChange string } -func NewACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *account.AccountData) *ACLStateBuilder { - return &ACLStateBuilder{ +func newACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *account.AccountData) *aclStateBuilder { + return &aclStateBuilder{ decoder: decoder, identity: accountData.Identity, key: accountData.EncKey, } } -func (sb *ACLStateBuilder) Build() (*aclState, error) { - state, _, err := sb.BuildBefore("") +func (sb *aclStateBuilder) build() (*ACLState, error) { + state, _, err := sb.buildBefore("") return state, err } -func (sb *ACLStateBuilder) Init(tree *Tree) error { +func (sb *aclStateBuilder) init(tree *Tree) error { root := tree.Root() if !root.IsSnapshot { return fmt.Errorf("root should always be a snapshot") } snapshot := root.Content.GetAclData().GetAclSnapshot() - state, err := NewACLStateFromSnapshot( + state, err := newACLStateFromSnapshot( snapshot, sb.identity, sb.key, sb.decoder) if err != nil { - return fmt.Errorf("could not build aclState from snapshot: %w", err) + return fmt.Errorf("could not build ACLState from snapshot: %w", err) } sb.tree = tree sb.aclState = state @@ -56,7 +56,7 @@ func (sb *ACLStateBuilder) Init(tree *Tree) error { } // TODO: we can probably have only one state builder, because we can build both at the same time -func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error) { +func (sb *aclStateBuilder) buildBefore(beforeId string) (*ACLState, bool, error) { var ( err error startChange = sb.tree.root @@ -83,8 +83,8 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error) } for { - // TODO: we should optimize this method to just remember last state of iterator and not iterate from the start and skip if nothing was removed from the tree - sb.tree.iterateSkip(sb.tree.root, startChange, func(c *Change) (isContinue bool) { + // TODO: we should optimize this method to just remember last state of iterator and not iterate from the start and skip if nothing was removed from the Tree + sb.tree.IterateSkip(sb.tree.root.Id, startChange.Id, func(c *Change) (isContinue bool) { defer func() { if err == nil { startChange = c @@ -101,13 +101,13 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error) idSeenMap[c.Content.Identity] = append(idSeenMap[c.Content.Identity], c) if c.Content.GetAclData() != nil { - err = sb.aclState.ApplyChange(c.Id, c.Content) + err = sb.aclState.applyChange(c.Id, c.Content) if err != nil { return false } // if we have some users who have less permissions now - users := sb.aclState.GetPermissionDecreasedUsers(c.Content) + users := sb.aclState.getPermissionDecreasedUsers(c.Content) if len(users) > 0 { decreasedPermissions = &decreasedPermissionsParameters{ users: users, @@ -118,7 +118,7 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error) } // the user can't make changes - if !sb.aclState.HasPermission(c.Content.Identity, pb.ACLChange_Writer) && !sb.aclState.HasPermission(c.Content.Identity, pb.ACLChange_Admin) { + if !sb.aclState.hasPermission(c.Content.Identity, pb.ACLChange_Writer) && !sb.aclState.hasPermission(c.Content.Identity, pb.ACLChange_Admin) { err = fmt.Errorf("user %s cannot make changes", c.Content.Identity) return false } @@ -168,8 +168,8 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error) decreasedPermissions = nil if removed { - // starting from the beginning but with updated tree - return sb.BuildBefore(beforeId) + // starting from the beginning but with updated Tree + return sb.buildBefore(beforeId) } } else if err == nil { // we can finish the acl state building process diff --git a/acltree/aclstatebuilder_test.go b/acltree/aclstatebuilder_test.go index 4cdc07f2..228744e5 100644 --- a/acltree/aclstatebuilder_test.go +++ b/acltree/aclstatebuilder_test.go @@ -14,13 +14,13 @@ import ( type ACLContext struct { Tree *Tree - ACLState *aclState + ACLState *ACLState } func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) { - treeBuilder := NewTreeBuilder(t, keys.NewEd25519Decoder()) - treeBuilder.Init() - return treeBuilder.Build(fromStart) + treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder()) + treeBuilder.init() + return treeBuilder.build(fromStart) } func createACLStateFromThread( @@ -39,33 +39,33 @@ func createACLStateFromThread( EncKey: key, } - aclTreeBuilder := NewACLTreeBuilder(t, decoder) - aclTreeBuilder.Init() - aclTree, err := aclTreeBuilder.Build() + aclTreeBuilder := newACLTreeBuilder(t, decoder) + aclTreeBuilder.init() + aclTree, err := aclTreeBuilder.build() if err != nil { return nil, err } if !fromStart { - snapshotValidator := NewSnapshotValidator(decoder, accountData) - snapshotValidator.Init(aclTree) - valid, err := snapshotValidator.ValidateSnapshot(tree.root) + snapshotValidator := newSnapshotValidator(decoder, accountData) + snapshotValidator.init(aclTree) + valid, err := snapshotValidator.validateSnapshot(tree.root) if err != nil { return nil, err } if !valid { - // TODO: think about what to do if the snapshot is invalid - should we rebuild the tree without it + // TODO: think about what to do if the snapshot is invalid - should we rebuild the Tree without it return createACLStateFromThread(t, identity, key, decoder, true) } } - aclBuilder := NewACLStateBuilder(decoder, accountData) - err = aclBuilder.Init(tree) + aclBuilder := newACLStateBuilder(decoder, accountData) + err = aclBuilder.init(tree) if err != nil { return nil, err } - aclState, err := aclBuilder.Build() + aclState, err := aclBuilder.build() if err != nil { return nil, err } @@ -88,7 +88,7 @@ func TestACLStateBuilder_UserJoinBuild(t *testing.T) { keys.NewEd25519Decoder(), false) if err != nil { - t.Fatalf("should build acl aclState without err: %v", err) + t.Fatalf("should build acl ACLState without err: %v", err) } aclState := ctx.ACLState //fmt.Println(ctx.Tree.Graph()) @@ -102,7 +102,7 @@ func TestACLStateBuilder_UserJoinBuild(t *testing.T) { assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) var changeIds []string - ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { changeIds = append(changeIds, c.Id) return true }) @@ -122,7 +122,7 @@ func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { keys.NewEd25519Decoder(), false) if err != nil { - t.Fatalf("should build acl aclState without err: %v", err) + t.Fatalf("should build acl ACLState without err: %v", err) } aclState := ctx.ACLState //fmt.Println(ctx.Tree.Graph()) @@ -132,7 +132,7 @@ func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) var changeIds []string - ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { changeIds = append(changeIds, c.Id) return true }) @@ -152,7 +152,7 @@ func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { keys.NewEd25519Decoder(), false) if err != nil { - t.Fatalf("should build acl aclState without err: %v", err) + t.Fatalf("should build acl ACLState without err: %v", err) } aclState := ctx.ACLState //fmt.Println(ctx.Tree.Graph()) @@ -163,7 +163,7 @@ func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")]) var changeIds []string - ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { changeIds = append(changeIds, c.Id) return true }) @@ -183,7 +183,7 @@ func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { keys.NewEd25519Decoder(), false) if err != nil { - t.Fatalf("should build acl aclState without err: %v", err) + t.Fatalf("should build acl ACLState without err: %v", err) } aclState := ctx.ACLState //fmt.Println(ctx.Tree.Graph()) @@ -193,7 +193,7 @@ func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) var changeIds []string - ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { changeIds = append(changeIds, c.Id) return true }) @@ -213,7 +213,7 @@ func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { keys.NewEd25519Decoder(), false) if err != nil { - t.Fatalf("should build acl aclState without err: %v", err) + t.Fatalf("should build acl ACLState without err: %v", err) } aclState := ctx.ACLState //fmt.Println(ctx.Tree.Graph()) @@ -223,7 +223,7 @@ func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) var changeIds []string - ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { changeIds = append(changeIds, c.Id) return true }) diff --git a/acltree/acltree.go b/acltree/acltree.go new file mode 100644 index 00000000..e8d9872e --- /dev/null +++ b/acltree/acltree.go @@ -0,0 +1,39 @@ +package acltree + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" +) + +func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) { + // build tree from thread + // validate snapshot + // build aclstate -> filter tree + // return ACLTree(aclstate+) + return nil, nil +} + +type AddResultSummary int + +const ( + AddResultSummaryNothing AddResultSummary = iota + AddResultSummaryAppend + AddResultSummaryRebuild +) + +type AddResult struct { + AttachedChanges []*Change + InvalidChanges []*Change + UnattachedChanges []*Change + + Summary AddResultSummary +} + +type ACLTree interface { + ACLState() *ACLState + AddChanges(changes ...*Change) (AddResult, error) + Heads() []string + Iterate(func(change *Change) bool) + IterateFrom(string, func(change *Change) bool) + HasChange(change *Change) +} diff --git a/acltree/acltreebuilder.go b/acltree/acltreebuilder.go index ca566359..4396da6e 100644 --- a/acltree/acltreebuilder.go +++ b/acltree/acltreebuilder.go @@ -9,7 +9,7 @@ import ( gothread "github.com/textileio/go-threads/core/thread" ) -type ACLTreeBuilder struct { +type aclTreeBuilder struct { cache map[string]*Change identityKeys map[string]keys.SigningPubKey signingPubKeyDecoder keys.SigningPubKeyDecoder @@ -19,8 +19,8 @@ type ACLTreeBuilder struct { *changeLoader } -func NewACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *ACLTreeBuilder { - return &ACLTreeBuilder{ +func newACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *aclTreeBuilder { + return &aclTreeBuilder{ signingPubKeyDecoder: decoder, thread: t, changeLoader: newChangeLoader( @@ -30,14 +30,14 @@ func NewACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *ACLT } } -func (tb *ACLTreeBuilder) Init() { +func (tb *aclTreeBuilder) init() { tb.cache = make(map[string]*Change) tb.identityKeys = make(map[string]keys.SigningPubKey) tb.tree = &Tree{} tb.changeLoader.init(tb.cache, tb.identityKeys) } -func (tb *ACLTreeBuilder) Build() (*Tree, error) { +func (tb *aclTreeBuilder) build() (*Tree, error) { heads := tb.thread.MaybeHeads() aclHeads, err := tb.getACLHeads(heads) if err != nil { @@ -52,10 +52,10 @@ func (tb *ACLTreeBuilder) Build() (*Tree, error) { return tb.tree, nil } -func (tb *ACLTreeBuilder) buildTreeFromStart(heads []string) (err error) { +func (tb *aclTreeBuilder) buildTreeFromStart(heads []string) (err error) { changes, possibleRoots, err := tb.dfsFromStart(heads) if len(possibleRoots) == 0 { - return fmt.Errorf("cannot have tree without root") + return fmt.Errorf("cannot have Tree without root") } root, err := tb.getRoot(possibleRoots) if err != nil { @@ -67,7 +67,7 @@ func (tb *ACLTreeBuilder) buildTreeFromStart(heads []string) (err error) { return } -func (tb *ACLTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) { +func (tb *aclTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) { stack := make([]string, len(heads), len(heads)*2) copy(stack, heads) @@ -98,7 +98,7 @@ func (tb *ACLTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleR return buf, possibleRoots, nil } -func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { +func (tb *aclTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { threadId, err := gothread.Decode(tb.thread.ID()) if err != nil { return nil, err @@ -123,7 +123,7 @@ func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { return nil, fmt.Errorf("could not find any root") } -func (tb *ACLTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, err error) { +func (tb *aclTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, err error) { for _, head := range heads { if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads continue @@ -147,7 +147,7 @@ func (tb *ACLTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, er return aclTreeHeads, nil } -func (tb *ACLTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) { +func (tb *aclTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) { headChange, err := tb.loadChange(head) if err != nil { return nil, err diff --git a/acltree/changeloader.go b/acltree/changeloader.go index 72004477..0dfe9e25 100644 --- a/acltree/changeloader.go +++ b/acltree/changeloader.go @@ -76,7 +76,7 @@ func (c *changeLoader) verify(identity string, payload, signature []byte) (isVer func (c *changeLoader) makeVerifiedACLChange(change *thread.RawChange) (aclChange *pb.ACLChange, err error) { aclChange = new(pb.ACLChange) - // TODO: think what should we do with such cases, because this can be used by attacker to break our tree + // TODO: think what should we do with such cases, because this can be used by attacker to break our Tree if err = proto.Unmarshal(change.Payload, aclChange); err != nil { return } diff --git a/acltree/snapshotvalidator.go b/acltree/snapshotvalidator.go index 5a7aeda1..b64f3d4a 100644 --- a/acltree/snapshotvalidator.go +++ b/acltree/snapshotvalidator.go @@ -7,44 +7,44 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" ) -type SnapshotValidator struct { +type snapshotValidator struct { aclTree *Tree identity string key keys.EncryptionPrivKey decoder keys.SigningPubKeyDecoder - stateBuilder *ACLStateBuilder + stateBuilder *aclStateBuilder } -func NewSnapshotValidator( +func newSnapshotValidator( decoder keys.SigningPubKeyDecoder, - accountData *account.AccountData) *SnapshotValidator { - return &SnapshotValidator{ + accountData *account.AccountData) *snapshotValidator { + return &snapshotValidator{ identity: accountData.Identity, key: accountData.EncKey, decoder: decoder, - stateBuilder: NewACLStateBuilder(decoder, accountData), + stateBuilder: newACLStateBuilder(decoder, accountData), } } -func (s *SnapshotValidator) Init(aclTree *Tree) error { +func (s *snapshotValidator) init(aclTree *Tree) error { s.aclTree = aclTree - return s.stateBuilder.Init(aclTree) + return s.stateBuilder.init(aclTree) } -func (s *SnapshotValidator) ValidateSnapshot(ch *Change) (bool, error) { - st, found, err := s.stateBuilder.BuildBefore(ch.Id) +func (s *snapshotValidator) validateSnapshot(ch *Change) (bool, error) { + st, found, err := s.stateBuilder.buildBefore(ch.Id) if err != nil { return false, err } if !found { - return false, fmt.Errorf("didn't find snapshot in ACL tree") + return false, fmt.Errorf("didn't find snapshot in ACL Tree") } - otherSt, err := NewACLStateFromSnapshot(ch.Content.GetAclData().GetAclSnapshot(), s.identity, s.key, s.decoder) + otherSt, err := newACLStateFromSnapshot(ch.Content.GetAclData().GetAclSnapshot(), s.identity, s.key, s.decoder) if err != nil { return false, err } - return st.Equal(otherSt), nil + return st.equal(otherSt), nil } diff --git a/acltree/tree.go b/acltree/tree.go index a437d42d..288929d2 100644 --- a/acltree/tree.go +++ b/acltree/tree.go @@ -15,6 +15,7 @@ const ( Nothing ) +// TODO: consider abstracting into separate package with iterator type Tree struct { root *Change headIds []string @@ -295,6 +296,12 @@ func (t *Tree) iterateSkip(start *Change, skipBefore *Change, f func(c *Change) it.iterateSkip(start, skipBefore, f) } +func (t *Tree) IterateSkip(startId string, skipBeforeId string, f func(c *Change) (isContinue bool)) { + it := newIterator() + defer freeIterator(it) + it.iterateSkip(t.attached[startId], t.attached[skipBeforeId], f) +} + func (t *Tree) Iterate(startId string, f func(c *Change) (isContinue bool)) { t.iterate(t.attached[startId], f) } diff --git a/acltree/treebuilder.go b/acltree/treebuilder.go index f56790d4..96de435f 100644 --- a/acltree/treebuilder.go +++ b/acltree/treebuilder.go @@ -17,7 +17,7 @@ var ( ErrEmpty = errors.New("logs empty") ) -type TreeBuilder struct { +type treeBuilder struct { cache map[string]*Change identityKeys map[string]keys.SigningPubKey signingPubKeyDecoder keys.SigningPubKeyDecoder @@ -27,8 +27,8 @@ type TreeBuilder struct { *changeLoader } -func NewTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *TreeBuilder { - return &TreeBuilder{ +func newTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *treeBuilder { + return &treeBuilder{ signingPubKeyDecoder: decoder, thread: t, changeLoader: newChangeLoader( @@ -38,14 +38,14 @@ func NewTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *TreeBui } } -func (tb *TreeBuilder) Init() { +func (tb *treeBuilder) init() { tb.cache = make(map[string]*Change) tb.identityKeys = make(map[string]keys.SigningPubKey) tb.tree = &Tree{} tb.changeLoader.init(tb.cache, tb.identityKeys) } -func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) { +func (tb *treeBuilder) build(fromStart bool) (*Tree, error) { heads := tb.thread.MaybeHeads() if fromStart { @@ -68,10 +68,10 @@ func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) { return tb.tree, nil } -func (tb *TreeBuilder) buildTreeFromStart(heads []string) (err error) { +func (tb *treeBuilder) buildTreeFromStart(heads []string) (err error) { changes, possibleRoots, err := tb.dfsFromStart(heads) if len(possibleRoots) == 0 { - return fmt.Errorf("cannot have tree without root") + return fmt.Errorf("cannot have Tree without root") } root, err := tb.getRoot(possibleRoots) if err != nil { @@ -83,7 +83,7 @@ func (tb *TreeBuilder) buildTreeFromStart(heads []string) (err error) { return } -func (tb *TreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) { +func (tb *treeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) { stack := make([]string, len(heads), len(heads)*2) copy(stack, heads) @@ -114,7 +114,7 @@ func (tb *TreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoot return buf, possibleRoots, nil } -func (tb *TreeBuilder) buildTree(heads []string, breakpoint string) (err error) { +func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error) { ch, err := tb.loadChange(breakpoint) if err != nil { return @@ -126,7 +126,7 @@ func (tb *TreeBuilder) buildTree(heads []string, breakpoint string) (err error) return } -func (tb *TreeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, err error) { +func (tb *treeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, err error) { stack := make([]string, len(heads), len(heads)*2) copy(stack, heads) @@ -154,7 +154,7 @@ func (tb *TreeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, er return buf, nil } -func (tb *TreeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) { +func (tb *treeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) { var ( ch *Change snapshotIds []string @@ -171,7 +171,7 @@ func (tb *TreeBuilder) findBreakpoint(heads []string) (breakpoint string, err er return tb.findCommonSnapshot(snapshotIds) } -func (tb *TreeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId string, err error) { +func (tb *treeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId string, err error) { if len(snapshotIds) == 1 { return snapshotIds[0], nil } else if len(snapshotIds) == 0 { @@ -190,7 +190,7 @@ func (tb *TreeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId stri return snapshotIds[0], nil } -func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err error) { +func (tb *treeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err error) { // fast cases if s1 == s2 { return s1, nil @@ -249,14 +249,14 @@ func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e } } - log.Warnf("changes build tree: possible versions split") + log.Warnf("changes build Tree: possible versions split") // prefer not first snapshot if len(ch1.PreviousIds) == 0 && len(ch2.PreviousIds) > 0 { - log.Warnf("changes build tree: prefer %s(%d prevIds) over %s(%d prevIds)", s2, len(ch2.PreviousIds), s1, len(ch1.PreviousIds)) + log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s2, len(ch2.PreviousIds), s1, len(ch1.PreviousIds)) return s2, nil } else if len(ch1.PreviousIds) > 0 && len(ch2.PreviousIds) == 0 { - log.Warnf("changes build tree: prefer %s(%d prevIds) over %s(%d prevIds)", s1, len(ch1.PreviousIds), s2, len(ch2.PreviousIds)) + log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s1, len(ch1.PreviousIds), s2, len(ch2.PreviousIds)) return s1, nil } @@ -268,25 +268,25 @@ func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e // TODO: can we even have empty snapshots? // prefer not empty snapshot if isEmptySnapshot(ch1) && !isEmptySnapshot(ch2) { - log.Warnf("changes build tree: prefer %s(not empty) over %s(empty)", s2, s1) + log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s2, s1) return s2, nil } else if isEmptySnapshot(ch2) && !isEmptySnapshot(ch1) { - log.Warnf("changes build tree: prefer %s(not empty) over %s(empty)", s1, s2) + log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s1, s2) return s1, nil } // TODO: add virtual change mechanics // unexpected behavior - just return lesser id if s1 < s2 { - log.Warnf("changes build tree: prefer %s (%s<%s)", s1, s1, s2) + log.Warnf("changes build Tree: prefer %s (%s<%s)", s1, s1, s2) return s1, nil } - log.Warnf("changes build tree: prefer %s (%s<%s)", s2, s2, s1) + log.Warnf("changes build Tree: prefer %s (%s<%s)", s2, s2, s1) return s2, nil } -func (tb *TreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { +func (tb *treeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { threadId, err := gothread.Decode(tb.thread.ID()) if err != nil { return nil, err diff --git a/acltree/treebuilder_test.go b/acltree/treebuilder_test.go index 311fbbc4..e7f8145a 100644 --- a/acltree/treebuilder_test.go +++ b/acltree/treebuilder_test.go @@ -8,11 +8,11 @@ package acltree // // res, err := createTreeFromThread(thread) // if err != nil { -// t.Fatalf("build tree should not result in an error: %v", res) +// t.Fatalf("build Tree should not result in an error: %v", res) // } // -// assert.Equal(t, res.Heads(), []string{"C.1.1"}) -// assert.Equal(t, res.Len(), 4) +// assert.equal(t, res.Heads(), []string{"C.1.1"}) +// assert.equal(t, res.Len(), 4) //} // //func TestTreeBuilder_UserJoinTestTreeIterate(t *testing.T) { @@ -23,17 +23,17 @@ package acltree // // res, err := createTreeFromThread(thread) // if err != nil { -// t.Fatalf("build tree should not result in an error: %v", res) +// t.Fatalf("build Tree should not result in an error: %v", res) // } // -// assert.Equal(t, res.Heads(), []string{"C.1.1"}) -// assert.Equal(t, res.Len(), 4) +// assert.equal(t, res.Heads(), []string{"C.1.1"}) +// assert.equal(t, res.Len(), 4) // var changeIds []string // res.iterate(res.root, func(c *Change) (isContinue bool) { // changeIds = append(changeIds, c.Id) // return true // }) -// assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "C.1.1"}) +// assert.equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "C.1.1"}) //} // //func TestTreeBuilder_UserRemoveTestTreeIterate(t *testing.T) { @@ -44,15 +44,15 @@ package acltree // // res, err := createTreeFromThread(thread) // if err != nil { -// t.Fatalf("build tree should not result in an error: %v", res) +// t.Fatalf("build Tree should not result in an error: %v", res) // } // -// assert.Equal(t, res.Heads(), []string{"A.1.3"}) -// assert.Equal(t, res.Len(), 4) +// assert.equal(t, res.Heads(), []string{"A.1.3"}) +// assert.equal(t, res.Len(), 4) // var changeIds []string // res.iterate(res.root, func(c *Change) (isContinue bool) { // changeIds = append(changeIds, c.Id) // return true // }) -// assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3"}) +// assert.equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3"}) //} diff --git a/acltree/treegraph_nix.go b/acltree/treegraph_nix.go index 175007aa..c180c7df 100644 --- a/acltree/treegraph_nix.go +++ b/acltree/treegraph_nix.go @@ -109,7 +109,7 @@ func (t *Tree) Graph() (data string, err error) { if n, ok := nodes[id]; ok { return n, nil } - n, err := graph.CreateNode(fmt.Sprintf("%s: not in tree", id)) + n, err := graph.CreateNode(fmt.Sprintf("%s: not in Tree", id)) if err != nil { return nil, err } diff --git a/exampledocument/document.go b/exampledocument/document.go index fe378a2d..f9f9e1d6 100644 --- a/exampledocument/document.go +++ b/exampledocument/document.go @@ -21,10 +21,10 @@ type Document struct { accountData *AccountData decoder threadmodels.SigningPubKeyDecoder - treeBuilder *acltree.TreeBuilder - aclTreeBuilder *acltree.ACLTreeBuilder - aclStateBuilder *acltree.ACLStateBuilder - snapshotValidator *acltree.SnapshotValidator + treeBuilder *acltree.treeBuilder + aclTreeBuilder *acltree.aclTreeBuilder + aclStateBuilder *acltree.aclStateBuilder + snapshotValidator *acltree.snapshotValidator docStateBuilder *acltree.documentStateBuilder docContext *acltree.documentContext @@ -55,10 +55,10 @@ func NewDocument( stateProvider: stateProvider, accountData: accountData, decoder: decoder, - aclTreeBuilder: acltree.NewACLTreeBuilder(thread, decoder), - treeBuilder: acltree.NewTreeBuilder(thread, decoder), - snapshotValidator: acltree.NewSnapshotValidator(decoder, accountData), - aclStateBuilder: acltree.NewACLStateBuilder(decoder, accountData), + aclTreeBuilder: acltree.newACLTreeBuilder(thread, decoder), + treeBuilder: acltree.newTreeBuilder(thread, decoder), + snapshotValidator: acltree.newSnapshotValidator(decoder, accountData), + aclStateBuilder: acltree.newACLStateBuilder(decoder, accountData), docStateBuilder: acltree.newDocumentStateBuilder(stateProvider), docContext: &acltree.documentContext{}, } @@ -242,28 +242,28 @@ func (d *Document) getPrecedingACLHeads(head string) []string { } func (d *Document) build(fromStart bool) (DocumentState, error) { - d.treeBuilder.Init() - d.aclTreeBuilder.Init() + d.treeBuilder.init() + d.aclTreeBuilder.init() var err error - d.docContext.fullTree, err = d.treeBuilder.Build(fromStart) + d.docContext.fullTree, err = d.treeBuilder.build(fromStart) if err != nil { return nil, err } // TODO: remove this from context as this is used only to validate snapshot - d.docContext.aclTree, err = d.aclTreeBuilder.Build() + d.docContext.aclTree, err = d.aclTreeBuilder.build() if err != nil { return nil, err } if !fromStart { - err = d.snapshotValidator.Init(d.docContext.aclTree) + err = d.snapshotValidator.init(d.docContext.aclTree) if err != nil { return nil, err } - valid, err := d.snapshotValidator.ValidateSnapshot(d.docContext.fullTree.root) + valid, err := d.snapshotValidator.validateSnapshot(d.docContext.fullTree.root) if err != nil { return nil, err } @@ -271,12 +271,12 @@ func (d *Document) build(fromStart bool) (DocumentState, error) { return d.build(true) } } - err = d.aclStateBuilder.Init(d.docContext.fullTree) + err = d.aclStateBuilder.init(d.docContext.fullTree) if err != nil { return nil, err } - d.docContext.aclState, err = d.aclStateBuilder.Build() + d.docContext.aclState, err = d.aclStateBuilder.build() if err != nil { return nil, err } diff --git a/exampledocument/documentcontext.go b/exampledocument/documentcontext.go index 08a415bb..bb165ab8 100644 --- a/exampledocument/documentcontext.go +++ b/exampledocument/documentcontext.go @@ -3,8 +3,8 @@ package exampledocument import "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" type documentContext struct { - aclTree *acltree.Tree // TODO: remove it, because we don't use it - fullTree *acltree.Tree + aclTree *acltree.tree // TODO: remove it, because we don't use it + fullTree *acltree.tree aclState *acltree.aclState docState DocumentState } diff --git a/exampledocument/documentstatebuilder.go b/exampledocument/documentstatebuilder.go index 8cff9dc0..4a6ac604 100644 --- a/exampledocument/documentstatebuilder.go +++ b/exampledocument/documentstatebuilder.go @@ -8,7 +8,7 @@ import ( // example -> type documentStateBuilder struct { - tree *acltree.Tree + tree *acltree.tree aclState *acltree.aclState // TODO: decide if this is needed or not stateProvider InitialStateProvider } @@ -19,7 +19,7 @@ func newDocumentStateBuilder(stateProvider InitialStateProvider) *documentStateB } } -func (d *documentStateBuilder) init(aclState *acltree.aclState, tree *acltree.Tree) { +func (d *documentStateBuilder) init(aclState *acltree.aclState, tree *acltree.tree) { d.tree = tree d.aclState = aclState } @@ -67,7 +67,7 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) { func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s DocumentState, err error) { // TODO: we should do something like state copy probably s = init - // TODO: we should have the same logic as in ACLStateBuilder, that means we should either pass in both methods state from the outside or save the state inside the builder + // TODO: we should have the same logic as in aclStateBuilder, that means we should either pass in both methods state from the outside or save the state inside the builder d.tree.Iterate(fromId, func(c *acltree.Change) (isContinue bool) { if c.Id == fromId { return true