Add logic update and create logic

This commit is contained in:
mcrakhman 2022-07-10 15:53:50 +02:00 committed by Mikhail Iudin
parent e6534e134b
commit 43797b1b84
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
9 changed files with 164 additions and 94 deletions

View File

@ -10,7 +10,6 @@ import (
type aclStateBuilder struct {
tree *Tree
aclState *ACLState
identity string
key keys.EncryptionPrivKey
decoder keys.SigningPubKeyDecoder
@ -29,15 +28,28 @@ func newACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *account.
}
}
func (sb *aclStateBuilder) build() (*ACLState, error) {
state, _, err := sb.buildBefore("")
func (sb *aclStateBuilder) Init(tree *Tree) error {
sb.tree = tree
return nil
}
func (sb *aclStateBuilder) Build() (*ACLState, error) {
state, _, err := sb.BuildBefore("")
return state, err
}
func (sb *aclStateBuilder) init(tree *Tree) error {
root := tree.Root()
// 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) {
var (
err error
startChange = sb.tree.root
foundId bool
idSeenMap = make(map[string][]*Change)
decreasedPermissions *decreasedPermissionsParameters
)
root := sb.tree.Root()
if !root.IsSnapshot {
return fmt.Errorf("root should always be a snapshot")
return nil, false, fmt.Errorf("root should always be a snapshot")
}
snapshot := root.Content.GetAclData().GetAclSnapshot()
@ -47,27 +59,13 @@ func (sb *aclStateBuilder) init(tree *Tree) error {
sb.key,
sb.decoder)
if err != nil {
return fmt.Errorf("could not build ACLState from snapshot: %w", err)
return nil, false, fmt.Errorf("could not build ACLState from snapshot: %w", err)
}
sb.tree = tree
sb.aclState = state
return nil
}
// 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) {
var (
err error
startChange = sb.tree.root
foundId bool
idSeenMap = make(map[string][]*Change)
decreasedPermissions *decreasedPermissionsParameters
)
idSeenMap[startChange.Content.Identity] = append(idSeenMap[startChange.Content.Identity], startChange)
if startChange.Content.GetChangesData() != nil {
key, exists := sb.aclState.userReadKeys[startChange.Content.CurrentReadKeyHash]
key, exists := state.userReadKeys[startChange.Content.CurrentReadKeyHash]
if !exists {
return nil, false, fmt.Errorf("no first snapshot")
}
@ -79,7 +77,7 @@ func (sb *aclStateBuilder) buildBefore(beforeId string) (*ACLState, bool, error)
}
if beforeId == startChange.Id {
return sb.aclState, true, nil
return state, true, nil
}
for {
@ -101,13 +99,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 = state.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 := state.getPermissionDecreasedUsers(c.Content)
if len(users) > 0 {
decreasedPermissions = &decreasedPermissionsParameters{
users: users,
@ -118,14 +116,14 @@ 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 !state.hasPermission(c.Content.Identity, pb.ACLChange_Writer) && !state.hasPermission(c.Content.Identity, pb.ACLChange_Admin) {
err = fmt.Errorf("user %s cannot make changes", c.Content.Identity)
return false
}
// decrypting contents on the fly
if c.Content.GetChangesData() != nil {
key, exists := sb.aclState.userReadKeys[c.Content.CurrentReadKeyHash]
key, exists := state.userReadKeys[c.Content.CurrentReadKeyHash]
if !exists {
err = fmt.Errorf("failed to find key with hash: %d", c.Content.CurrentReadKeyHash)
return false
@ -169,7 +167,7 @@ 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)
return sb.BuildBefore(beforeId)
}
} else if err == nil {
// we can finish the acl state building process
@ -185,5 +183,5 @@ func (sb *aclStateBuilder) buildBefore(beforeId string) (*ACLState, bool, error)
err = nil
}
return sb.aclState, foundId, err
return state, foundId, err
}

View File

@ -19,8 +19,8 @@ type ACLContext struct {
func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) {
treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder())
treeBuilder.init()
return treeBuilder.build(fromStart)
treeBuilder.Init()
return treeBuilder.Build(fromStart)
}
func createACLStateFromThread(
@ -40,16 +40,16 @@ func createACLStateFromThread(
}
aclTreeBuilder := newACLTreeBuilder(t, decoder)
aclTreeBuilder.init()
aclTree, err := aclTreeBuilder.build()
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.Init(aclTree)
valid, err := snapshotValidator.ValidateSnapshot(tree.root)
if err != nil {
return nil, err
}
@ -60,12 +60,12 @@ func createACLStateFromThread(
}
aclBuilder := newACLStateBuilder(decoder, accountData)
err = aclBuilder.init(tree)
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())
@ -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())
@ -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())
@ -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())
@ -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())

View File

@ -7,6 +7,7 @@ import (
"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
@ -18,17 +19,17 @@ const (
)
type AddResult struct {
AttachedChanges []*Change
InvalidChanges []*Change
UnattachedChanges []*Change
OldHeads []string
Heads []string
// TODO: add summary for changes
Summary AddResultSummary
}
// TODO: Change add change content to include ACLChangeBuilder
type ACLTree interface {
ACLState() *ACLState
AddContent(changeContent *ChangeContent) (*Change, error)
AddChanges(changes ...*Change) (AddResult, error) // TODO: Make change as interface
AddChanges(changes ...*Change) (AddResult, error)
Heads() []string
Iterate(func(change *Change) bool)
IterateFrom(string, func(change *Change) bool)
@ -40,13 +41,15 @@ type aclTree struct {
accountData *account.AccountData
fullTree *Tree
aclTree *Tree // this tree is built from start of the document
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
aclState *ACLState
treeBuilder *treeBuilder
aclTreeBuilder *aclTreeBuilder
aclStateBuilder *aclStateBuilder
snapshotValidator *snapshotValidator
sync.Mutex
}
func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) {
@ -74,14 +77,15 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) {
return aclTree, nil
}
// 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) {
if validateSnapshot {
err = a.snapshotValidator.init(a.aclTree)
err = a.snapshotValidator.Init(a.aclTree)
if err != nil {
return err
}
valid, err := a.snapshotValidator.validateSnapshot(a.fullTree.root)
valid, err := a.snapshotValidator.ValidateSnapshot(a.fullTree.root)
if err != nil {
return err
}
@ -90,12 +94,12 @@ func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) {
}
}
err = a.aclStateBuilder.init(a.fullTree)
err = a.aclStateBuilder.Init(a.fullTree)
if err != nil {
return err
}
a.aclState, err = a.aclStateBuilder.build()
a.aclState, err = a.aclStateBuilder.Build()
if err != nil {
return err
}
@ -104,28 +108,28 @@ func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) {
}
func (a *aclTree) rebuildFromThread(fromStart bool) error {
a.treeBuilder.init()
a.aclTreeBuilder.init()
a.treeBuilder.Init()
a.aclTreeBuilder.Init()
var err error
a.fullTree, err = a.treeBuilder.build(fromStart)
a.fullTree, err = a.treeBuilder.Build(fromStart)
if err != nil {
return err
}
// TODO: remove this from context as this is used only to validate snapshot
a.aclTree, err = a.aclTreeBuilder.build()
a.aclTree, err = a.aclTreeBuilder.Build()
if err != nil {
return err
}
if !fromStart {
err = a.snapshotValidator.init(a.aclTree)
err = a.snapshotValidator.Init(a.aclTree)
if err != nil {
return err
}
valid, err := a.snapshotValidator.validateSnapshot(a.fullTree.root)
valid, err := a.snapshotValidator.ValidateSnapshot(a.fullTree.root)
if err != nil {
return err
}
@ -133,12 +137,12 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error {
return a.rebuildFromThread(true)
}
}
err = a.aclStateBuilder.init(a.fullTree)
err = a.aclStateBuilder.Init(a.fullTree)
if err != nil {
return err
}
a.aclState, err = a.aclStateBuilder.build()
a.aclState, err = a.aclStateBuilder.Build()
if err != nil {
return err
}
@ -181,6 +185,8 @@ func (a *aclTree) ACLState() *ACLState {
func (a *aclTree) AddContent(changeContent *ChangeContent) (*Change, error) {
// TODO: add snapshot creation logic
a.Lock()
defer a.Unlock()
marshalled, err := changeContent.ChangesData.Marshal()
if err != nil {
return nil, err
@ -239,40 +245,100 @@ func (a *aclTree) AddContent(changeContent *ChangeContent) (*Change, error) {
}
func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) {
a.Lock()
defer a.Unlock()
// TODO: make proper error handling, because there are a lot of corner cases where this will break
var aclChanges []*Change
var err error
defer func() {
if err != nil {
return
}
// removing attached or invalid orphans
var toRemove []string
for _, orphan := range a.thread.Orphans() {
if _, exists := a.fullTree.attached[orphan]; exists {
toRemove = append(toRemove, orphan)
}
if _, exists := a.fullTree.invalidChanges[orphan]; exists {
toRemove = append(toRemove, orphan)
}
}
a.thread.RemoveOrphans(toRemove...)
}()
for _, ch := range changes {
if ch.IsACLChange() {
aclChanges = append(aclChanges, ch)
break
}
a.thread.A
err = a.thread.AddChange(ch)
if err != nil {
return AddResult{}, err
}
a.thread.AddOrphans(ch.Id)
}
// TODO: understand the common snapshot problem
prevHeads := a.fullTree.Heads()
mode := a.fullTree.Add(changes...)
switch mode {
case Nothing:
return AddResult{Summary: AddResultSummaryNothing}, nil
return AddResult{
OldHeads: prevHeads,
Heads: prevHeads,
Summary: AddResultSummaryNothing,
}, nil
case Rebuild:
res, err := d.Build()
return AddResult{Summary: Rebuild}, err
err = a.rebuildFromThread(false)
if err != nil {
return AddResult{}, err
}
return AddResult{
OldHeads: prevHeads,
Heads: a.fullTree.Heads(),
Summary: AddResultSummaryRebuild,
}, nil
default:
break
a.aclState, err = a.aclStateBuilder.Build()
if err != nil {
return AddResult{}, err
}
return AddResult{
OldHeads: prevHeads,
Heads: a.fullTree.Heads(),
Summary: AddResultSummaryAppend,
}, nil
}
}
func (a *aclTree) Iterate(f func(change *Change) bool) {
//TODO implement me
panic("implement me")
a.Lock()
defer a.Unlock()
a.fullTree.Iterate(a.fullTree.RootId(), f)
}
func (a *aclTree) IterateFrom(s string, f func(change *Change) bool) {
//TODO implement me
panic("implement me")
a.Lock()
defer a.Unlock()
a.fullTree.Iterate(s, f)
}
func (a *aclTree) HasChange(s string) bool {
//TODO implement me
panic("implement me")
a.Lock()
defer a.Unlock()
_, attachedExists := a.fullTree.attached[s]
_, unattachedExists := a.fullTree.unAttached[s]
_, invalidExists := a.fullTree.invalidChanges[s]
return attachedExists || unattachedExists || invalidExists
}
func (a *aclTree) Heads() []string {
a.Lock()
defer a.Unlock()
return a.fullTree.Heads()
}

View File

@ -30,16 +30,19 @@ 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)
tb.changeLoader.Init(tb.cache, tb.identityKeys)
}
func (tb *aclTreeBuilder) build() (*Tree, error) {
heads := tb.thread.PossibleHeads()
aclHeads, err := tb.getACLHeads(heads)
func (tb *aclTreeBuilder) Build() (*Tree, error) {
var headsAndOrphans []string
headsAndOrphans = append(headsAndOrphans, tb.thread.Orphans()...)
headsAndOrphans = append(headsAndOrphans, tb.thread.Heads()...)
aclHeads, err := tb.getACLHeads(headsAndOrphans)
if err != nil {
return nil, err
}

View File

@ -30,7 +30,7 @@ func newChangeLoader(
}
}
func (c *changeLoader) init(cache map[string]*Change,
func (c *changeLoader) Init(cache map[string]*Change,
identityKeys map[string]keys.SigningPubKey) {
c.cache = cache
c.identityKeys = identityKeys

View File

@ -26,13 +26,13 @@ func newSnapshotValidator(
}
}
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
}

View File

@ -38,27 +38,29 @@ 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)
tb.changeLoader.Init(tb.cache, tb.identityKeys)
}
func (tb *treeBuilder) build(fromStart bool) (*Tree, error) {
heads := tb.thread.PossibleHeads()
func (tb *treeBuilder) Build(fromStart bool) (*Tree, error) {
var headsAndOrphans []string
headsAndOrphans = append(headsAndOrphans, tb.thread.Orphans()...)
headsAndOrphans = append(headsAndOrphans, tb.thread.Heads()...)
if fromStart {
if err := tb.buildTreeFromStart(heads); err != nil {
if err := tb.buildTreeFromStart(headsAndOrphans); err != nil {
return nil, fmt.Errorf("buildTree error: %v", err)
}
} else {
breakpoint, err := tb.findBreakpoint(heads)
breakpoint, err := tb.findBreakpoint(headsAndOrphans)
if err != nil {
return nil, fmt.Errorf("findBreakpoint error: %v", err)
}
if err = tb.buildTree(heads, breakpoint); err != nil {
if err = tb.buildTree(headsAndOrphans, breakpoint); err != nil {
return nil, fmt.Errorf("buildTree error: %v", err)
}
}

View File

@ -109,7 +109,7 @@ func (t *ThreadBuilder) AddRawChange(change *thread.RawChange) error {
return nil
}
func (t *ThreadBuilder) AddPossibleHead(head string) {
func (t *ThreadBuilder) AddOrphans(head string) {
t.maybeHeads = append(t.maybeHeads, head)
}
@ -140,7 +140,7 @@ func (t *ThreadBuilder) AddChange(change aclchanges.Change) error {
return nil
}
func (t *ThreadBuilder) PossibleHeads() []string {
func (t *ThreadBuilder) Orphans() []string {
return t.maybeHeads
}

View File

@ -11,13 +11,14 @@ type Thread interface {
ID() string
Heads() []string
PossibleHeads() []string
Orphans() []string
SetHeads(heads []string)
SetPossibleHeads(heads []string)
AddPossibleHead(head string)
RemoveOrphans(orphan ...string)
AddOrphans(orphan ...string)
AddRawChange(change *RawChange) error
AddChange(change aclchanges.Change) error
// TODO: have methods with raw changes also
GetChange(ctx context.Context, recordID string) (*RawChange, error)
}