From 4beb0d29020745631f4deea62908faefcf64f58c Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 28 Jun 2022 15:37:09 +0200 Subject: [PATCH 01/53] Start adding ACLs --- data/aclstate.go | 351 ++++++++++++++ data/aclstatebuilder.go | 181 +++++++ data/aclstatebuilder_test.go | 164 +++++++ data/acltreebuilder.go | 237 ++++++++++ data/change.go | 70 +++ data/documentstatebuilder.go | 72 +++ data/documentstatebuilder_test.go | 62 +++ data/pb/protos/aclchanges.proto | 113 +++++ data/snapshotvalidator.go | 49 ++ data/threadbuilder/invalidsnapshotexample.yml | 122 +++++ data/threadbuilder/keychain.go | 138 ++++++ data/threadbuilder/threadbuilder.go | 444 ++++++++++++++++++ data/threadbuilder/threadbuildergraph.go | 11 + data/threadbuilder/threadbuildergraph_nix.go | 153 ++++++ data/threadbuilder/userjoinexample.yml | 109 +++++ .../threadbuilder/userremovebeforeexample.yml | 105 +++++ data/threadbuilder/userremoveexample.yml | 106 +++++ data/threadbuilder/validsnapshotexample.yml | 126 +++++ data/threadbuilder/ymlentities.go | 103 ++++ data/threadbuilder/ymlentities_test.go | 12 + data/threadhelpers.go | 81 ++++ data/threadmodels/keys.go | 255 ++++++++++ data/threadmodels/models.go | 34 ++ data/threadmodels/threadid.go | 71 +++ data/threadmodels/threadid_test.go | 27 ++ data/tree.go | 375 +++++++++++++++ data/treebuilder.go | 437 +++++++++++++++++ data/treebuilder_test.go | 58 +++ data/treegraph.go | 11 + data/treegraph_nix.go | 149 ++++++ data/treeiterator.go | 150 ++++++ go.mod | 9 + 32 files changed, 4385 insertions(+) create mode 100644 data/aclstate.go create mode 100644 data/aclstatebuilder.go create mode 100644 data/aclstatebuilder_test.go create mode 100644 data/acltreebuilder.go create mode 100644 data/change.go create mode 100644 data/documentstatebuilder.go create mode 100644 data/documentstatebuilder_test.go create mode 100644 data/pb/protos/aclchanges.proto create mode 100644 data/snapshotvalidator.go create mode 100644 data/threadbuilder/invalidsnapshotexample.yml create mode 100644 data/threadbuilder/keychain.go create mode 100644 data/threadbuilder/threadbuilder.go create mode 100644 data/threadbuilder/threadbuildergraph.go create mode 100644 data/threadbuilder/threadbuildergraph_nix.go create mode 100644 data/threadbuilder/userjoinexample.yml create mode 100644 data/threadbuilder/userremovebeforeexample.yml create mode 100644 data/threadbuilder/userremoveexample.yml create mode 100644 data/threadbuilder/validsnapshotexample.yml create mode 100644 data/threadbuilder/ymlentities.go create mode 100644 data/threadbuilder/ymlentities_test.go create mode 100644 data/threadhelpers.go create mode 100644 data/threadmodels/keys.go create mode 100644 data/threadmodels/models.go create mode 100644 data/threadmodels/threadid.go create mode 100644 data/threadmodels/threadid_test.go create mode 100644 data/tree.go create mode 100644 data/treebuilder.go create mode 100644 data/treebuilder_test.go create mode 100644 data/treegraph.go create mode 100644 data/treegraph_nix.go create mode 100644 data/treeiterator.go create mode 100644 go.mod diff --git a/data/aclstate.go b/data/aclstate.go new file mode 100644 index 00000000..934e29c5 --- /dev/null +++ b/data/aclstate.go @@ -0,0 +1,351 @@ +package data + +import ( + "bytes" + "errors" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" + "github.com/textileio/go-threads/crypto/symmetric" + "hash/fnv" +) + +var ErrNoSuchUser = errors.New("no such user") +var ErrFailedToDecrypt = errors.New("failed to decrypt key") +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 { + currentReadKeyHash uint64 + userReadKeys map[uint64]*symmetric.Key + userStates map[string]*pb.ACLChangeUserState + userInvites map[string]*pb.ACLChangeUserInvite + signingPubKeyDecoder threadmodels.SigningPubKeyDecoder + encryptionKey threadmodels.EncryptionPrivKey + identity string +} + +func NewACLStateFromSnapshot( + snapshot *pb.ACLChangeACLSnapshot, + identity string, + encryptionKey threadmodels.EncryptionPrivKey, + signingPubKeyDecoder threadmodels.SigningPubKeyDecoder) (*ACLState, error) { + st := &ACLState{ + identity: identity, + encryptionKey: encryptionKey, + userReadKeys: make(map[uint64]*symmetric.Key), + userStates: make(map[string]*pb.ACLChangeUserState), + userInvites: make(map[string]*pb.ACLChangeUserInvite), + signingPubKeyDecoder: signingPubKeyDecoder, + } + err := st.recreateFromSnapshot(snapshot) + if err != nil { + return nil, err + } + return st, nil +} + +func (st *ACLState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) error { + state := snapshot.AclState + for _, userState := range state.UserStates { + st.userStates[userState.Identity] = userState + } + + userState, exists := st.userStates[st.identity] + if !exists { + return ErrNoSuchUser + } + var lastKeyHash uint64 + for _, key := range userState.EncryptedReadKeys { + key, hash, err := st.decryptReadKeyAndHash(key) + if err != nil { + return ErrFailedToDecrypt + } + + st.userReadKeys[hash] = key + lastKeyHash = hash + } + st.currentReadKeyHash = lastKeyHash + if snapshot.GetAclState().GetInvites() != nil { + st.userInvites = snapshot.GetAclState().GetInvites() + } + return nil +} + +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 + if _, exists := st.userStates[change.Identity]; !exists { + return ErrNoSuchUser + } + + 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 { + log.Infof("error while applying changes: %v; ignore", err) + return err + } + } + + return nil +} + +// TODO: remove changeId, because it is not needed +func (st *ACLState) applyChange(changeId string, ch *pb.ACLChangeACLContentValue) error { + switch { + case ch.GetUserPermissionChange() != nil: + return st.applyUserPermissionChange(ch.GetUserPermissionChange()) + case ch.GetUserAdd() != nil: + return st.applyUserAdd(ch.GetUserAdd()) + case ch.GetUserRemove() != nil: + return st.applyUserRemove(ch.GetUserRemove()) + case ch.GetUserInvite() != nil: + return st.applyUserInvite(changeId, ch.GetUserInvite()) + case ch.GetUserJoin() != nil: + return st.applyUserJoin(ch.GetUserJoin()) + case ch.GetUserConfirm() != nil: + return st.applyUserConfirm(ch.GetUserConfirm()) + default: + return fmt.Errorf("unexpected change type: %v", ch) + } +} + +func (st *ACLState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChange) error { + if _, exists := st.userStates[ch.Identity]; !exists { + return ErrNoSuchUser + } + + st.userStates[ch.Identity].Permissions = ch.Permissions + return nil +} + +func (st *ACLState) applyUserInvite(changeId string, ch *pb.ACLChangeUserInvite) error { + st.userInvites[changeId] = ch + return nil +} + +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) + } + + if _, exists = st.userStates[ch.Identity]; exists { + return ErrUserAlreadyExists + } + + // validating signature + signature := ch.GetAcceptSignature() + verificationKey, err := st.signingPubKeyDecoder.DecodeFromBytes(invite.AcceptPublicKey) + if err != nil { + return fmt.Errorf("public key verifying invite accepts is given in incorrect format: %v", err) + } + + rawSignedId, err := st.signingPubKeyDecoder.DecodeFromStringIntoBytes(ch.Identity) + if err != nil { + return fmt.Errorf("failed to decode signing identity as bytes") + } + + res, err := verificationKey.Verify(rawSignedId, signature) + if err != nil { + return fmt.Errorf("verification returned error: %w", err) + } + if !res { + return fmt.Errorf("signature is invalid") + } + + // if ourselves -> we need to decrypt the read keys + if st.identity == ch.Identity { + var lastKeyHash uint64 + for _, key := range ch.EncryptedReadKeys { + key, hash, err := st.decryptReadKeyAndHash(key) + if err != nil { + return ErrFailedToDecrypt + } + + st.userReadKeys[hash] = key + lastKeyHash = hash + } + st.currentReadKeyHash = lastKeyHash + } + + // adding user to the list + userState := &pb.ACLChangeUserState{ + Identity: ch.Identity, + EncryptionKey: ch.EncryptionKey, + EncryptedReadKeys: ch.EncryptedReadKeys, + Permissions: invite.Permissions, + IsConfirmed: true, + } + st.userStates[ch.Identity] = userState + return nil +} + +func (st *ACLState) applyUserAdd(ch *pb.ACLChangeUserAdd) error { + if _, exists := st.userStates[ch.Identity]; exists { + return ErrUserAlreadyExists + } + + st.userStates[ch.Identity] = &pb.ACLChangeUserState{ + Identity: ch.Identity, + EncryptionKey: ch.EncryptionKey, + Permissions: ch.Permissions, + EncryptedReadKeys: ch.EncryptedReadKeys, + } + + return nil +} + +func (st *ACLState) applyUserRemove(ch *pb.ACLChangeUserRemove) error { + if ch.Identity == st.identity { + return ErrDocumentForbidden + } + + if _, exists := st.userStates[ch.Identity]; !exists { + return ErrNoSuchUser + } + + delete(st.userStates, ch.Identity) + + for _, replace := range ch.ReadKeyReplaces { + userState, exists := st.userStates[replace.Identity] + if !exists { + continue + } + + userState.EncryptedReadKeys = append(userState.EncryptedReadKeys, replace.EncryptedReadKey) + // if this is our identity then we have to decrypt the key + if replace.Identity == st.identity { + key, hash, err := st.decryptReadKeyAndHash(replace.EncryptedReadKey) + if err != nil { + return ErrFailedToDecrypt + } + + st.currentReadKeyHash = hash + st.userReadKeys[st.currentReadKeyHash] = key + } + } + return nil +} + +func (st *ACLState) applyUserConfirm(ch *pb.ACLChangeUserConfirm) error { + if _, exists := st.userStates[ch.Identity]; !exists { + return ErrNoSuchUser + } + + userState := st.userStates[ch.Identity] + userState.IsConfirmed = true + return nil +} + +func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) { + decrypted, err := st.encryptionKey.Decrypt(msg) + if err != nil { + return nil, 0, ErrFailedToDecrypt + } + + key, err := symmetric.FromBytes(decrypted) + if err != nil { + return nil, 0, ErrFailedToDecrypt + } + + hasher := fnv.New64() + hasher.Write(decrypted) + return key, hasher.Sum64(), nil +} + +func (st *ACLState) HasPermission(identity string, permission pb.ACLChangeUserPermissions) bool { + state, exists := st.userStates[identity] + if !exists { + return false + } + + return state.Permissions == permission +} + +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) { + // this should be called after general checks are completed + if ch.GetAclData().GetAclContent() == nil { + return nil + } + + contents := ch.GetAclData().GetAclContent() + for _, c := range contents { + if c.GetUserPermissionChange() != nil { + content := c.GetUserPermissionChange() + + currentState := st.userStates[content.Identity] + // the comparison works in different direction :-) + if content.Permissions > currentState.Permissions { + identities = append(identities, &pb.ACLChangeUserPermissionChange{ + Identity: content.Identity, + Permissions: content.Permissions, + }) + } + } + if c.GetUserRemove() != nil { + content := c.GetUserRemove() + identities = append(identities, &pb.ACLChangeUserPermissionChange{ + Identity: content.Identity, + Permissions: pb.ACLChange_Removed, + }) + } + } + + return identities +} + +func (st *ACLState) Equal(other *ACLState) bool { + if st == nil && other == nil { + return true + } + + if st == nil || other == nil { + return false + } + + if st.currentReadKeyHash != other.currentReadKeyHash { + return false + } + + if st.identity != other.identity { + return false + } + + if len(st.userStates) != len(other.userStates) { + return false + } + + for _, st := range st.userStates { + otherSt, exists := other.userStates[st.Identity] + if !exists { + return false + } + + if st.Permissions != otherSt.Permissions { + return false + } + + if bytes.Compare(st.EncryptionKey, otherSt.EncryptionKey) != 0 { + return false + } + } + + if len(st.userInvites) != len(other.userInvites) { + return false + } + + // TODO: add detailed user invites comparison + compare other stuff + return true +} diff --git a/data/aclstatebuilder.go b/data/aclstatebuilder.go new file mode 100644 index 00000000..ee1e1d9f --- /dev/null +++ b/data/aclstatebuilder.go @@ -0,0 +1,181 @@ +package data + +import ( + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" +) + +type ACLStateBuilder struct { + tree *Tree + aclState *ACLState + identity string + key threadmodels.EncryptionPrivKey +} + +type decreasedPermissionsParameters struct { + users []*pb.ACLChangeUserPermissionChange + startChange string +} + +func NewACLStateBuilder( + tree *Tree, + identity string, + key threadmodels.EncryptionPrivKey, + decoder threadmodels.SigningPubKeyDecoder) (*ACLStateBuilder, error) { + root := tree.Root() + if !root.IsSnapshot { + return nil, fmt.Errorf("root should always be a snapshot") + } + + snapshot := root.Content.GetAclData().GetAclSnapshot() + state, err := NewACLStateFromSnapshot(snapshot, identity, key, decoder) + if err != nil { + return nil, fmt.Errorf("could not build aclState from snapshot: %w", err) + } + + return &ACLStateBuilder{ + tree: tree, + aclState: state, + identity: identity, + key: key, + }, nil +} + +func (sb *ACLStateBuilder) Build() (*ACLState, error) { + state, _, err := sb.BuildBefore("") + return state, err +} + +// 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] + if !exists { + return nil, false, fmt.Errorf("no first snapshot") + } + + err = startChange.DecryptContents(key) + if err != nil { + return nil, false, fmt.Errorf("failed to decrypt contents of first snapshot") + } + } + + if beforeId == startChange.Id { + return sb.aclState, true, nil + } + + 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) { + defer func() { + if err == nil { + startChange = c + } else if err != ErrDocumentForbidden { + log.Errorf("marking change %s as invalid: %v", c.Id, err) + sb.tree.RemoveInvalidChange(c.Id) + } + }() + + // not applying root change + if c.Id == startChange.Id { + return true + } + + idSeenMap[c.Content.Identity] = append(idSeenMap[c.Content.Identity], c) + if c.Content.GetAclData() != nil { + 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) + if len(users) > 0 { + decreasedPermissions = &decreasedPermissionsParameters{ + users: users, + startChange: c.Id, + } + return false + } + } + + // 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) { + 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] + if !exists { + err = fmt.Errorf("failed to find key with hash: %d", c.Content.CurrentReadKeyHash) + return false + } + + err = c.DecryptContents(key) + if err != nil { + err = fmt.Errorf("failed to decrypt contents for hash: %d", c.Content.CurrentReadKeyHash) + return false + } + } + + if c.Id == beforeId { + foundId = true + return false + } + + return true + }) + + // if we have users with decreased permissions + if decreasedPermissions != nil { + var removed bool + validChanges := sb.tree.dfs(decreasedPermissions.startChange) + + for _, permChange := range decreasedPermissions.users { + seenChanges := idSeenMap[permChange.Identity] + + for _, seen := range seenChanges { + // if we find some invalid changes + if _, exists := validChanges[seen.Id]; !exists { + // if the user didn't have enough permission to make changes + if seen.IsACLChange() || permChange.Permissions > pb.ACLChange_Writer { + removed = true + sb.tree.RemoveInvalidChange(seen.Id) + } + } + } + } + + decreasedPermissions = nil + if removed { + // starting from the beginning but with updated tree + return sb.BuildBefore(beforeId) + } + } else if err == nil { + // we can finish the acl state building process + break + } + + // the user is forbidden to access the document + if err == ErrDocumentForbidden { + return nil, foundId, err + } + + // otherwise we have to continue from the change which we had + err = nil + } + + return sb.aclState, foundId, err +} diff --git a/data/aclstatebuilder_test.go b/data/aclstatebuilder_test.go new file mode 100644 index 00000000..c8f68756 --- /dev/null +++ b/data/aclstatebuilder_test.go @@ -0,0 +1,164 @@ +package data + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestACLStateBuilder_UserJoinBuild(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + ctx, err := createACLStateFromThread( + thread, + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"], + threadmodels.NewEd25519Decoder(), + false) + if err != nil { + t.Fatalf("should build acl aclState without err: %v", err) + } + aclState := ctx.ACLState + //fmt.Println(ctx.Tree.Graph()) + aId := keychain.GeneratedIdentities["A"] + bId := keychain.GeneratedIdentities["B"] + cId := keychain.GeneratedIdentities["C"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + assert.Equal(t, aclState.userStates[bId].Permissions, pb.ACLChange_Writer) + assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) + + var changeIds []string + ctx.Tree.iterate(ctx.Tree.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", "B.1.2"}) +} + +func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + ctx, err := createACLStateFromThread( + thread, + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"], + threadmodels.NewEd25519Decoder(), + false) + if err != nil { + t.Fatalf("should build acl aclState without err: %v", err) + } + aclState := ctx.ACLState + //fmt.Println(ctx.Tree.Graph()) + aId := keychain.GeneratedIdentities["A"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + + var changeIds []string + ctx.Tree.iterate(ctx.Tree.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", "A.1.4"}) +} + +func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremovebeforeexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + ctx, err := createACLStateFromThread( + thread, + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"], + threadmodels.NewEd25519Decoder(), + false) + if err != nil { + t.Fatalf("should build acl aclState without err: %v", err) + } + aclState := ctx.ACLState + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "C", "E"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")]) + + var changeIds []string + ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, changeIds, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3"}) +} + +func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/invalidsnapshotexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + ctx, err := createACLStateFromThread( + thread, + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"], + threadmodels.NewEd25519Decoder(), + false) + if err != nil { + t.Fatalf("should build acl aclState without err: %v", err) + } + aclState := ctx.ACLState + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "B", "C", "D", "E", "F"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + + var changeIds []string + ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3", "B.1.2"}, changeIds) +} + +func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/validsnapshotexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + ctx, err := createACLStateFromThread( + thread, + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"], + threadmodels.NewEd25519Decoder(), + false) + if err != nil { + t.Fatalf("should build acl aclState without err: %v", err) + } + aclState := ctx.ACLState + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "B", "C", "D", "E", "F"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + + var changeIds []string + ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, []string{"A.1.2", "A.1.3", "B.1.2"}, changeIds) +} diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go new file mode 100644 index 00000000..1583eed7 --- /dev/null +++ b/data/acltreebuilder.go @@ -0,0 +1,237 @@ +package data + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" + "github.com/gogo/protobuf/proto" + "github.com/textileio/go-threads/core/thread" + "sort" + "time" +) + +type ACLTreeBuilder struct { + cache map[string]*Change + logHeads map[string]*Change + identityKeys map[string]threadmodels.SigningPubKey + signingPubKeyDecoder threadmodels.SigningPubKeyDecoder + tree *Tree + thread threadmodels.Thread +} + +func NewACLTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *ACLTreeBuilder { + return &ACLTreeBuilder{ + cache: make(map[string]*Change), + logHeads: make(map[string]*Change), + identityKeys: make(map[string]threadmodels.SigningPubKey), + signingPubKeyDecoder: decoder, + tree: &Tree{}, // TODO: add NewTree method + thread: t, + } +} + +func (tb *ACLTreeBuilder) loadChange(id string) (ch *Change, err error) { + if ch, ok := tb.cache[id]; ok { + return ch, nil + } + + // TODO: Add virtual changes logic + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + record, err := tb.thread.GetRecord(ctx, id) + if err != nil { + return nil, err + } + + aclChange := new(pb.ACLChange) + + // TODO: think what should we do with such cases, because this can be used by attacker to break our tree + if err = proto.Unmarshal(record.Signed.Payload, aclChange); err != nil { + return + } + var verified bool + verified, err = tb.verify(aclChange.Identity, record.Signed.Payload, record.Signed.Signature) + if err != nil { + return + } + if !verified { + err = fmt.Errorf("the signature of the payload cannot be verified") + return + } + + ch, err = NewACLChange(id, aclChange) + tb.cache[id] = ch + + return ch, nil +} + +func (tb *ACLTreeBuilder) verify(identity string, payload, signature []byte) (isVerified bool, err error) { + identityKey, exists := tb.identityKeys[identity] + if !exists { + identityKey, err = tb.signingPubKeyDecoder.DecodeFromString(identity) + if err != nil { + return + } + tb.identityKeys[identity] = identityKey + } + return identityKey.Verify(payload, signature) +} + +func (tb *ACLTreeBuilder) getLogs() (logs []threadmodels.ThreadLog, err error) { + // TODO: Add beforeId building logic + logs, err = tb.thread.GetLogs() + if err != nil { + return nil, fmt.Errorf("GetLogs error: %w", err) + } + + log.Debugf("build tree: logs: %v", logs) + if len(logs) == 0 || len(logs) == 1 && len(logs[0].Head) <= 1 { + return nil, ErrEmpty + } + var nonEmptyLogs = logs[:0] + for _, l := range logs { + if len(l.Head) == 0 { + continue + } + if ch, err := tb.loadChange(l.Head); err != nil { + log.Errorf("loading head %s of the log %s failed: %v", l.Head, l.ID, err) + } else { + tb.logHeads[l.ID] = ch + } + nonEmptyLogs = append(nonEmptyLogs, l) + } + return nonEmptyLogs, nil +} + +func (tb *ACLTreeBuilder) Build() (*Tree, error) { + logs, err := tb.getLogs() + if err != nil { + return nil, err + } + + heads, err := tb.getACLHeads(logs) + if err != nil { + return nil, fmt.Errorf("get acl heads error: %v", err) + } + + if err = tb.buildTreeFromStart(heads); err != nil { + return nil, fmt.Errorf("buildTree error: %v", err) + } + tb.cache = nil + + return tb.tree, nil +} + +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") + } + root, err := tb.getRoot(possibleRoots) + if err != nil { + return err + } + + tb.tree.AddFast(root) + tb.tree.AddFast(changes...) + return +} + +func (tb *ACLTreeBuilder) dfsFromStart(stack []string) (buf []*Change, possibleRoots []*Change, err error) { + buf = make([]*Change, 0, len(stack)*2) + uniqMap := make(map[string]struct{}) + + for len(stack) > 0 { + id := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if _, exists := uniqMap[id]; exists { + continue + } + + ch, err := tb.loadChange(id) + if err != nil { + continue + } + + uniqMap[id] = struct{}{} + buf = append(buf, ch) + + for _, prev := range ch.PreviousIds { + stack = append(stack, prev) + } + if len(ch.PreviousIds) == 0 { + possibleRoots = append(possibleRoots, ch) + } + } + return buf, possibleRoots, nil +} + +func (tb *ACLTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) { + headChange, err := tb.loadChange(head) + if err != nil { + return nil, err + } + + if headChange.Content.GetAclData() != nil { + return []string{head}, nil + } else { + return headChange.PreviousIds, nil + } +} + +func (tb *ACLTreeBuilder) getACLHeads(logs []threadmodels.ThreadLog) (aclTreeHeads []string, err error) { + sort.Slice(logs, func(i, j int) bool { + return logs[i].ID < logs[j].ID + }) + + // get acl tree heads from log heads + for _, l := range logs { + if slice.FindPos(aclTreeHeads, l.Head) != -1 { // do not scan known heads + continue + } + precedingHeads, err := tb.getPrecedingACLHeads(l.Head) + if err != nil { + return nil, err + } + + for _, head := range precedingHeads { + if slice.FindPos(aclTreeHeads, l.Head) != -1 { + continue + } + aclTreeHeads = append(aclTreeHeads, head) + } + } + + if len(aclTreeHeads) == 0 { + return nil, fmt.Errorf("no usable ACL heads in thread") + } + return aclTreeHeads, nil +} + +func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { + threadId, err := thread.Decode(tb.thread.ID()) + if err != nil { + return nil, err + } + + for _, r := range possibleRoots { + id := r.Content.Identity + sk, err := tb.signingPubKeyDecoder.DecodeFromString(id) + if err != nil { + continue + } + + res, err := threadmodels.VerifyACLThreadID(sk, threadId) + if err != nil { + continue + } + + if res { + return r, nil + } + } + return nil, fmt.Errorf("could not find any root") +} diff --git a/data/change.go b/data/change.go new file mode 100644 index 00000000..1d33e57b --- /dev/null +++ b/data/change.go @@ -0,0 +1,70 @@ +package data + +import ( + "fmt" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" + "github.com/gogo/protobuf/proto" + "github.com/textileio/go-threads/crypto/symmetric" +) + +// Change is an abstract type for all types of changes +type Change struct { + Next []*Change + Unattached []*Change + PreviousIds []string + Id string + SnapshotId string + LogHeads map[string]string + IsSnapshot bool + DecryptedDocumentChange *pb.ACLChangeChangeData + + Content *pb.ACLChange +} + +func (ch *Change) DecryptContents(key *symmetric.Key) error { + if ch.Content.ChangesData == nil { + return nil + } + + var changesData pb.ACLChangeChangeData + decrypted, err := key.Decrypt(ch.Content.ChangesData) + if err != nil { + return fmt.Errorf("failed to decrypt changes data: %w", err) + } + + err = proto.Unmarshal(decrypted, &changesData) + if err != nil { + return fmt.Errorf("failed to umarshall into ChangesData: %w", err) + } + ch.DecryptedDocumentChange = &changesData + return nil +} + +func (ch *Change) IsACLChange() bool { + return ch.Content.GetAclData() != nil +} + +func NewChange(id string, ch *pb.ACLChange) (*Change, error) { + return &Change{ + Next: nil, + PreviousIds: ch.TreeHeadIds, + Id: id, + Content: ch, + SnapshotId: ch.SnapshotBaseId, + IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, + LogHeads: ch.GetLogHeads(), + }, nil +} + +func NewACLChange(id string, ch *pb.ACLChange) (*Change, error) { + return &Change{ + Next: nil, + PreviousIds: ch.AclHeadIds, + Id: id, + Content: ch, + SnapshotId: ch.SnapshotBaseId, + IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, + LogHeads: ch.GetLogHeads(), + }, nil +} diff --git a/data/documentstatebuilder.go b/data/documentstatebuilder.go new file mode 100644 index 00000000..ed6d1852 --- /dev/null +++ b/data/documentstatebuilder.go @@ -0,0 +1,72 @@ +package data + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/core/block/editor/state" + "time" +) + +type documentStateBuilder struct { + tree *Tree + aclState *ACLState // TODO: decide if this is needed or not +} + +func newDocumentStateBuilder(tree *Tree, state *ACLState) *documentStateBuilder { + return &documentStateBuilder{ + tree: tree, + aclState: state, + } +} + +// TODO: we should probably merge the two builders into one +func (d *documentStateBuilder) build() (s *state.State, err error) { + var ( + startId string + applyRoot bool + st = time.Now() + lastChange *Change + count int + ) + rootChange := d.tree.Root() + root := state.NewDocFromSnapshot("root", rootChange.DecryptedDocumentChange.Snapshot).(*state.State) + root.SetChangeId(rootChange.Id) + + t := d.tree + if startId = root.ChangeId(); startId == "" { + startId = t.RootId() + applyRoot = true + } + + t.Iterate(startId, func(c *Change) (isContinue bool) { + count++ + lastChange = c + if startId == c.Id { + s = root.NewState() + if applyRoot && c.DecryptedDocumentChange != nil { + s.ApplyChangeIgnoreErr(c.DecryptedDocumentChange.Content...) + s.SetChangeId(c.Id) + s.AddFileKeys(c.DecryptedDocumentChange.FileKeys...) + } + return true + } + if c.DecryptedDocumentChange != nil { + ns := s.NewState() + ns.ApplyChangeIgnoreErr(c.DecryptedDocumentChange.Content...) + ns.SetChangeId(c.Id) + ns.AddFileKeys(c.DecryptedDocumentChange.FileKeys...) + _, _, err = state.ApplyStateFastOne(ns) + if err != nil { + return false + } + } + return true + }) + if err != nil { + return nil, err + } + if lastChange != nil { + s.SetLastModified(lastChange.Content.Timestamp, lastChange.Content.Identity) + } + + log.Infof("build state (crdt): changes: %d; dur: %v;", count, time.Since(st)) + return s, err +} diff --git a/data/documentstatebuilder_test.go b/data/documentstatebuilder_test.go new file mode 100644 index 00000000..e84b3805 --- /dev/null +++ b/data/documentstatebuilder_test.go @@ -0,0 +1,62 @@ +package data + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestDocumentStateBuilder_UserJoinBuild(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + ctx, err := createDocumentStateFromThread( + thread, + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"], + threadmodels.NewEd25519Decoder()) + if err != nil { + t.Fatalf("should build acl aclState without err: %v", err) + } + + st := ctx.DocState + allIds := make(map[string]bool) + for _, b := range st.Blocks() { + allIds[b.Id] = true + } + if err != nil { + t.Fatalf("iterate should not return error: %v", err) + } + assert.True(t, allIds["root"]) + assert.True(t, allIds["first"]) +} + +func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + ctx, err := createDocumentStateFromThread( + thread, + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"], + threadmodels.NewEd25519Decoder()) + if err != nil { + t.Fatalf("should build acl aclState without err: %v", err) + } + + st := ctx.DocState + allIds := make(map[string]bool) + for _, b := range st.Blocks() { + allIds[b.Id] = true + } + if err != nil { + t.Fatalf("iterate should not return error: %v", err) + } + assert.True(t, allIds["root"]) + assert.True(t, allIds["second"]) +} diff --git a/data/pb/protos/aclchanges.proto b/data/pb/protos/aclchanges.proto new file mode 100644 index 00000000..d0c6d955 --- /dev/null +++ b/data/pb/protos/aclchanges.proto @@ -0,0 +1,113 @@ +syntax = "proto3"; +package anytype; +option go_package = "pb"; + +import "pb/protos/changes.proto"; + +// the element of change tree used to store and internal apply smartBlock history +message ACLChange { + repeated string treeHeadIds = 1; + repeated string aclHeadIds = 2; + string snapshotBaseId = 3; // we will only have one base snapshot for both + ACLData aclData = 4; + // the data is encoded with read key and should be read in ChangesData format + bytes changesData = 5; + uint64 currentReadKeyHash = 6; + int64 timestamp = 7; + string identity = 8; + map logHeads = 9; + + message ACLContentValue { + oneof value { + UserAdd userAdd = 1; + UserRemove userRemove = 2; + UserPermissionChange userPermissionChange = 3; + UserInvite userInvite = 4; + UserJoin userJoin = 5; + UserConfirm userConfirm = 6; + } + } + + message ChangeData { + anytype.Change.Snapshot snapshot = 1; + repeated anytype.Change.Content content = 2; + repeated anytype.Change.FileKeys fileKeys = 3; + } + + message ACLData { + ACLSnapshot aclSnapshot = 1; + repeated ACLContentValue aclContent = 2; + } + + message ACLSnapshot { + // We don't need ACLState as a separate message now, because we simplified the snapshot model + ACLState aclState = 1; + } + + message ACLState { + repeated uint64 readKeyHashes = 1; + repeated UserState userStates = 2; + map invites = 3; // TODO: later + // repeated string unconfirmedUsers = 4; // TODO: later + } + + message UserState { + string identity = 1; + bytes encryptionKey = 2; + repeated bytes encryptedReadKeys = 3; // all read keys that we know + UserPermissions permissions = 4; + bool IsConfirmed = 5; + } + + // we already know identity and encryptionKey + message UserAdd { + string identity = 1; // public signing key + bytes encryptionKey = 2; // public encryption key + repeated bytes encryptedReadKeys = 3; // all read keys that we know for the user + UserPermissions permissions = 4; + } + + // TODO: this is not used as of now + message UserConfirm { // not needed for read permissions + string identity = 1; // not needed + string userAddId = 2; + } + + message UserInvite { + bytes acceptPublicKey = 1; + bytes encryptPublicKey = 2; + repeated bytes encryptedReadKeys = 3; // all read keys that we know for the user + UserPermissions permissions = 4; + } + + message UserJoin { + string identity = 1; + bytes encryptionKey = 2; + bytes acceptSignature = 3; // sign acceptPublicKey + string userInviteChangeId = 4; + repeated bytes encryptedReadKeys = 5; // the idea is that user should itself reencrypt the keys with the pub key + } + + message UserRemove { + string identity = 1; + repeated ReadKeyReplace readKeyReplaces = 3; // new read key encrypted for all users + } + + message ReadKeyReplace { + string identity = 1; + bytes encryptionKey = 2; + bytes encryptedReadKey = 3; + } + + message UserPermissionChange { + string identity = 1; + UserPermissions permissions = 2; + } + + enum UserPermissions { + Admin = 0; + Writer = 1; + Reader = 2; + Removed = 3; + } +} diff --git a/data/snapshotvalidator.go b/data/snapshotvalidator.go new file mode 100644 index 00000000..29a7ee43 --- /dev/null +++ b/data/snapshotvalidator.go @@ -0,0 +1,49 @@ +package data + +import ( + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" +) + +type SnapshotValidator struct { + aclTree *Tree + identity string + key threadmodels.EncryptionPrivKey + decoder threadmodels.SigningPubKeyDecoder +} + +func NewSnapshotValidator( + aclTree *Tree, + identity string, + key threadmodels.EncryptionPrivKey, + decoder threadmodels.SigningPubKeyDecoder) *SnapshotValidator { + return &SnapshotValidator{ + aclTree: aclTree, + identity: identity, + key: key, + decoder: decoder, + } +} + +func (s *SnapshotValidator) ValidateSnapshot(ch *Change) (bool, error) { + stateBuilder, err := NewACLStateBuilder(s.aclTree, s.identity, s.key, s.decoder) + if err != nil { + return false, err + } + + st, found, err := stateBuilder.BuildBefore(ch.Id) + if err != nil { + return false, err + } + + if !found { + return false, fmt.Errorf("didn't find snapshot in ACL tree") + } + + otherSt, err := NewACLStateFromSnapshot(ch.Content.GetAclData().GetAclSnapshot(), s.identity, s.key, s.decoder) + if err != nil { + return false, err + } + + return st.Equal(otherSt), nil +} diff --git a/data/threadbuilder/invalidsnapshotexample.yml b/data/threadbuilder/invalidsnapshotexample.yml new file mode 100644 index 00000000..366a9ced --- /dev/null +++ b/data/threadbuilder/invalidsnapshotexample.yml @@ -0,0 +1,122 @@ +thread: + author: A +logs: + - id: A.1 + identity: A + records: + - id: A.1.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + - userAdd: + identity: B + permission: admin + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: A.1.2 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: D + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: D + permission: admin + encryptionKey: key.Enc.D + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: A.1.3 + aclChanges: + - userAdd: + identity: E + permission: admin + encryptionKey: key.Enc.E + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1 + identity: B + records: + - id: B.1.1 + aclChanges: + - userAdd: + identity: C + permission: admin + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: B.1.2 + aclChanges: + - userAdd: + identity: F + permission: admin + encryptionKey: key.Enc.F + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 +keys: + Enc: + - A + - B + - C + - D + - E + - F + Sign: + - A + - B + - C + - D + - E + - F + Read: + - 1 + - 2 +graph: + - id: A.1.1 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + - id: A.1.2 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: B.1.1 + baseSnapshot: A.1.1 + aclHeads: [A.1.1] + treeHeads: [A.1.1] + - id: B.1.2 + baseSnapshot: A.1.2 + aclHeads: [A.1.2] + treeHeads: [A.1.2] + - id: A.1.3 + baseSnapshot: A.1.2 + aclHeads: [A.1.2] + treeHeads: [A.1.2] diff --git a/data/threadbuilder/keychain.go b/data/threadbuilder/keychain.go new file mode 100644 index 00000000..f048d1d6 --- /dev/null +++ b/data/threadbuilder/keychain.go @@ -0,0 +1,138 @@ +package threadbuilder + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/textileio/go-threads/crypto/symmetric" + "hash/fnv" + "strings" +) + +type SymKey struct { + Hash uint64 + Key *symmetric.Key +} + +type Keychain struct { + SigningKeys map[string]threadmodels.SigningPrivKey + EncryptionKeys map[string]threadmodels.EncryptionPrivKey + ReadKeys map[string]*SymKey + GeneratedIdentities map[string]string + coder *threadmodels.Ed25519SigningPubKeyDecoder +} + +func NewKeychain() *Keychain { + return &Keychain{ + SigningKeys: map[string]threadmodels.SigningPrivKey{}, + EncryptionKeys: map[string]threadmodels.EncryptionPrivKey{}, + GeneratedIdentities: map[string]string{}, + ReadKeys: map[string]*SymKey{}, + coder: threadmodels.NewEd25519Decoder(), + } +} + +func (k *Keychain) ParseKeys(keys *Keys) { + for _, encKey := range keys.Enc { + k.AddEncryptionKey(encKey) + } + + for _, signKey := range keys.Sign { + k.AddSigningKey(signKey) + } + + for _, readKey := range keys.Read { + k.AddReadKey(readKey) + } +} + +func (k *Keychain) AddEncryptionKey(name string) { + if _, exists := k.EncryptionKeys[name]; exists { + return + } + newPrivKey, _, err := threadmodels.GenerateRandomRSAKeyPair(2048) + if err != nil { + panic(err) + } + + k.EncryptionKeys[name] = newPrivKey +} + +func (k *Keychain) AddSigningKey(name string) { + if _, exists := k.SigningKeys[name]; exists { + return + } + newPrivKey, pubKey, err := threadmodels.GenerateRandomEd25519KeyPair() + if err != nil { + panic(err) + } + + k.SigningKeys[name] = newPrivKey + res, err := k.coder.EncodeToString(pubKey) + if err != nil { + panic(err) + } + k.GeneratedIdentities[name] = res +} + +func (k *Keychain) AddReadKey(name string) { + if _, exists := k.ReadKeys[name]; exists { + return + } + key, _ := symmetric.NewRandom() + + hasher := fnv.New64() + hasher.Write(key.Bytes()) + + k.ReadKeys[name] = &SymKey{ + Hash: hasher.Sum64(), + Key: key, + } +} + +func (k *Keychain) AddKey(key string) { + parts := strings.Split(key, ".") + if len(parts) != 3 { + panic("cannot parse a key") + } + name := parts[2] + + switch parts[1] { + case "Sign": + k.AddSigningKey(name) + case "Enc": + k.AddEncryptionKey(name) + case "Read": + k.AddReadKey(name) + default: + panic("incorrect format") + } +} + +func (k *Keychain) GetKey(key string) interface{} { + parts := strings.Split(key, ".") + if len(parts) != 3 { + panic("cannot parse a key") + } + name := parts[2] + + switch parts[1] { + case "Sign": + if key, exists := k.SigningKeys[name]; exists { + return key + } + case "Enc": + if key, exists := k.EncryptionKeys[name]; exists { + return key + } + case "Read": + if key, exists := k.ReadKeys[name]; exists { + return key + } + default: + panic("incorrect format") + } + return nil +} + +func (k *Keychain) GetIdentity(name string) string { + return k.GeneratedIdentities[name] +} diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go new file mode 100644 index 00000000..f391e8ad --- /dev/null +++ b/data/threadbuilder/threadbuilder.go @@ -0,0 +1,444 @@ +package threadbuilder + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/core/smartblock" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/pb/model" + "github.com/gogo/protobuf/proto" + "io/ioutil" + "sort" +) + +type threadRecord struct { + *pb.ACLChange + id string + logId string + readKey *SymKey + signKey threadmodels.SigningPrivKey + + prevRecord *threadRecord + changesData *pb.ACLChangeChangeData +} + +type threadLog struct { + id string + owner string + records []*threadRecord +} + +type ThreadBuilder struct { + threadId string + logs map[string]*threadLog + allRecords map[string]*threadRecord + keychain *Keychain +} + +func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { + return &ThreadBuilder{ + logs: make(map[string]*threadLog), + allRecords: make(map[string]*threadRecord), + keychain: keychain, + } +} + +func NewThreadBuilderFromFile(file string) (*ThreadBuilder, error) { + content, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + + thread := YMLThread{} + err = yaml.Unmarshal(content, &thread) + if err != nil { + return nil, err + } + + tb := NewThreadBuilder(NewKeychain()) + tb.Parse(&thread) + + return tb, nil +} + +func (t *ThreadBuilder) ID() string { + return t.threadId +} + +func (t *ThreadBuilder) GetKeychain() *Keychain { + return t.keychain +} + +func (t *ThreadBuilder) GetLogs() ([]threadmodels.ThreadLog, error) { + var logs []threadmodels.ThreadLog + for _, l := range t.logs { + logs = append(logs, threadmodels.ThreadLog{ + ID: l.id, + Head: l.records[len(l.records)-1].id, + Counter: int64(len(l.records)), + }) + } + sort.Slice(logs, func(i, j int) bool { + return logs[i].ID < logs[j].ID + }) + return logs, nil +} + +func (t *ThreadBuilder) GetRecord(ctx context.Context, recordID string) (*threadmodels.ThreadRecord, error) { + rec := t.allRecords[recordID] + prevId := "" + if rec.prevRecord != nil { + prevId = rec.prevRecord.id + } + + var encrypted []byte + if rec.changesData != nil { + m, err := proto.Marshal(rec.changesData) + if err != nil { + panic("should be able to marshal data!") + } + + encrypted, err = rec.readKey.Key.Encrypt(m) + if err != nil { + panic("should be able to encrypt data with read key!") + } + + rec.ChangesData = encrypted + } + + aclMarshaled, err := proto.Marshal(rec.ACLChange) + if err != nil { + panic("should be able to marshal final acl message!") + } + + signature, err := rec.signKey.Sign(aclMarshaled) + if err != nil { + panic("should be able to sign final acl message!") + } + + transformedRec := &threadmodels.ThreadRecord{ + PrevId: prevId, + Id: rec.id, + LogId: rec.logId, + Signed: &threadmodels.SignedPayload{ + Payload: aclMarshaled, + Signature: signature, + }, + } + return transformedRec, nil +} + +func (t *ThreadBuilder) PushRecord(payload proto.Marshaler) (id string, err error) { + panic("implement me") +} + +func (t *ThreadBuilder) Parse(thread *YMLThread) { + // Just to clarify - we are generating new identities for the ones that + // are specified in the yml file, because our identities should be Ed25519 + // the same thing is happening for the encryption keys + t.keychain.ParseKeys(&thread.Keys) + t.threadId = t.parseThreadId(thread.Description) + for _, l := range thread.Logs { + newLog := &threadLog{ + id: l.Id, + owner: t.keychain.GetIdentity(l.Identity), + } + var records []*threadRecord + for _, r := range l.Records { + newRecord := &threadRecord{ + id: r.Id, + logId: newLog.id, + } + if len(records) > 0 { + newRecord.prevRecord = records[len(records)-1] + } + k := t.keychain.GetKey(r.ReadKey).(*SymKey) + newRecord.readKey = k + newRecord.signKey = t.keychain.SigningKeys[l.Identity] + + aclChange := &pb.ACLChange{} + aclChange.Identity = newLog.owner + if len(r.AclChanges) > 0 || r.AclSnapshot != nil { + aclChange.AclData = &pb.ACLChangeACLData{} + if r.AclSnapshot != nil { + aclChange.AclData.AclSnapshot = t.parseACLSnapshot(r.AclSnapshot) + } + if r.AclChanges != nil { + var aclChangeContents []*pb.ACLChangeACLContentValue + for _, ch := range r.AclChanges { + aclChangeContent := t.parseACLChange(ch) + aclChangeContents = append(aclChangeContents, aclChangeContent) + } + aclChange.AclData.AclContent = aclChangeContents + } + } + if len(r.Changes) > 0 || r.Snapshot != nil { + newRecord.changesData = &pb.ACLChangeChangeData{} + if r.Snapshot != nil { + newRecord.changesData.Snapshot = t.parseChangeSnapshot(r.Snapshot) + } + if len(r.Changes) > 0 { + var changeContents []*pb.ChangeContent + for _, ch := range r.Changes { + aclChangeContent := t.parseDocumentChange(ch) + changeContents = append(changeContents, aclChangeContent) + } + newRecord.changesData.Content = changeContents + } + } + aclChange.CurrentReadKeyHash = k.Hash + newRecord.ACLChange = aclChange + t.allRecords[newRecord.id] = newRecord + records = append(records, newRecord) + } + newLog.records = records + t.logs[newLog.id] = newLog + } + t.parseGraph(thread) +} + +func (t *ThreadBuilder) parseThreadId(description *ThreadDescription) string { + if description == nil { + panic("no author in thread") + } + key := t.keychain.SigningKeys[description.Author] + id, err := threadmodels.CreateACLThreadID(key.GetPublic(), smartblock.SmartBlockTypeWorkspace) + if err != nil { + panic(err) + } + + return id.String() +} + +func (t *ThreadBuilder) parseChangeSnapshot(s *ChangeSnapshot) *pb.ChangeSnapshot { + data := &model.SmartBlockSnapshotBase{} + var blocks []*model.Block + for _, b := range s.Blocks { + modelBlock := &model.Block{Id: b.Id, ChildrenIds: b.ChildrenIds} + blocks = append(blocks, modelBlock) + } + data.Blocks = blocks + return &pb.ChangeSnapshot{ + Data: data, + } +} + +func (t *ThreadBuilder) parseACLSnapshot(s *ACLSnapshot) *pb.ACLChangeACLSnapshot { + newState := &pb.ACLChangeACLState{} + for _, state := range s.UserStates { + aclUserState := &pb.ACLChangeUserState{} + aclUserState.Identity = t.keychain.GetIdentity(state.Identity) + + encKey := t.keychain. + GetKey(state.EncryptionKey).(threadmodels.EncryptionPrivKey) + rawKey, _ := encKey.GetPublic().Raw() + aclUserState.EncryptionKey = rawKey + + aclUserState.EncryptedReadKeys = t.encryptReadKeys(state.EncryptedReadKeys, encKey) + aclUserState.Permissions = t.convertPermission(state.Permissions) + newState.UserStates = append(newState.UserStates, aclUserState) + } + return &pb.ACLChangeACLSnapshot{ + AclState: newState, + } +} + +func (t *ThreadBuilder) parseDocumentChange(ch *DocumentChange) (convCh *pb.ChangeContent) { + switch { + case ch.BlockAdd != nil: + blockAdd := ch.BlockAdd + + convCh = &pb.ChangeContent{ + Value: &pb.ChangeContentValueOfBlockCreate{ + BlockCreate: &pb.ChangeBlockCreate{ + TargetId: blockAdd.TargetId, + Position: model.Block_Inner, + Blocks: []*model.Block{&model.Block{Id: blockAdd.Id}}, + }}} + } + if convCh == nil { + panic("cannot have empty document change") + } + + return convCh +} + +func (t *ThreadBuilder) parseACLChange(ch *ACLChange) (convCh *pb.ACLChangeACLContentValue) { + switch { + case ch.UserAdd != nil: + add := ch.UserAdd + + encKey := t.keychain. + GetKey(add.EncryptionKey).(threadmodels.EncryptionPrivKey) + rawKey, _ := encKey.GetPublic().Raw() + + convCh = &pb.ACLChangeACLContentValue{ + Value: &pb.ACLChangeACLContentValueValueOfUserAdd{ + UserAdd: &pb.ACLChangeUserAdd{ + Identity: t.keychain.GetIdentity(add.Identity), + EncryptionKey: rawKey, + EncryptedReadKeys: t.encryptReadKeys(add.EncryptedReadKeys, encKey), + Permissions: t.convertPermission(add.Permission), + }, + }, + } + case ch.UserJoin != nil: + join := ch.UserJoin + + encKey := t.keychain. + GetKey(join.EncryptionKey).(threadmodels.EncryptionPrivKey) + rawKey, _ := encKey.GetPublic().Raw() + + idKey, _ := t.keychain.SigningKeys[join.Identity].GetPublic().Raw() + signKey := t.keychain.GetKey(join.AcceptSignature).(threadmodels.SigningPrivKey) + signature, err := signKey.Sign(idKey) + if err != nil { + panic(err) + } + + convCh = &pb.ACLChangeACLContentValue{ + Value: &pb.ACLChangeACLContentValueValueOfUserJoin{ + UserJoin: &pb.ACLChangeUserJoin{ + Identity: t.keychain.GetIdentity(join.Identity), + EncryptionKey: rawKey, + AcceptSignature: signature, + UserInviteChangeId: join.InviteId, + EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), + }, + }, + } + case ch.UserInvite != nil: + invite := ch.UserInvite + rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(threadmodels.SigningPrivKey).GetPublic().Raw() + encKey := t.keychain. + GetKey(invite.EncryptionKey).(threadmodels.EncryptionPrivKey) + rawEncKey, _ := encKey.GetPublic().Raw() + + convCh = &pb.ACLChangeACLContentValue{ + Value: &pb.ACLChangeACLContentValueValueOfUserInvite{ + UserInvite: &pb.ACLChangeUserInvite{ + AcceptPublicKey: rawAcceptKey, + EncryptPublicKey: rawEncKey, + EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey), + Permissions: t.convertPermission(invite.Permissions), + }, + }, + } + case ch.UserConfirm != nil: + confirm := ch.UserConfirm + + convCh = &pb.ACLChangeACLContentValue{ + Value: &pb.ACLChangeACLContentValueValueOfUserConfirm{ + UserConfirm: &pb.ACLChangeUserConfirm{ + Identity: t.keychain.GetIdentity(confirm.Identity), + UserAddId: confirm.UserAddId, + }, + }, + } + case ch.UserPermissionChange != nil: + permissionChange := ch.UserPermissionChange + + convCh = &pb.ACLChangeACLContentValue{ + Value: &pb.ACLChangeACLContentValueValueOfUserPermissionChange{ + UserPermissionChange: &pb.ACLChangeUserPermissionChange{ + Identity: t.keychain.GetIdentity(permissionChange.Identity), + Permissions: t.convertPermission(permissionChange.Permission), + }, + }, + } + case ch.UserRemove != nil: + remove := ch.UserRemove + + newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey) + + var replaces []*pb.ACLChangeReadKeyReplace + for _, id := range remove.IdentitiesLeft { + identity := t.keychain.GetIdentity(id) + encKey := t.keychain.EncryptionKeys[id] + rawEncKey, _ := encKey.GetPublic().Raw() + encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes()) + if err != nil { + panic(err) + } + replaces = append(replaces, &pb.ACLChangeReadKeyReplace{ + Identity: identity, + EncryptionKey: rawEncKey, + EncryptedReadKey: encReadKey, + }) + } + + convCh = &pb.ACLChangeACLContentValue{ + Value: &pb.ACLChangeACLContentValueValueOfUserRemove{ + UserRemove: &pb.ACLChangeUserRemove{ + Identity: t.keychain.GetIdentity(remove.RemovedIdentity), + ReadKeyReplaces: replaces, + }, + }, + } + } + if convCh == nil { + panic("cannot have empty acl change") + } + + return convCh +} + +func (t *ThreadBuilder) encryptReadKeys(keys []string, encKey threadmodels.EncryptionPrivKey) (enc [][]byte) { + for _, k := range keys { + realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes() + res, err := encKey.GetPublic().Encrypt(realKey) + if err != nil { + panic(err) + } + + enc = append(enc, res) + } + return +} + +func (t *ThreadBuilder) convertPermission(perm string) pb.ACLChangeUserPermissions { + switch perm { + case "admin": + return pb.ACLChange_Admin + case "writer": + return pb.ACLChange_Writer + case "reader": + return pb.ACLChange_Reader + default: + panic(fmt.Sprintf("incorrect permission: %s", perm)) + } +} + +func (t *ThreadBuilder) traverseFromHeads(f func(t *threadRecord) error) error { + allLogs, err := t.GetLogs() + if err != nil { + return err + } + + for _, log := range allLogs { + head := t.allRecords[log.Head] + err = f(head) + if err != nil { + return err + } + + for head.prevRecord != nil { + head = head.prevRecord + err = f(head) + if err != nil { + return err + } + } + } + return nil +} + +func (t *ThreadBuilder) parseGraph(thread *YMLThread) { + for _, node := range thread.Graph { + rec := t.allRecords[node.Id] + rec.AclHeadIds = node.ACLHeads + rec.TreeHeadIds = node.TreeHeads + rec.SnapshotBaseId = node.BaseSnapshot + } +} diff --git a/data/threadbuilder/threadbuildergraph.go b/data/threadbuilder/threadbuildergraph.go new file mode 100644 index 00000000..edb98971 --- /dev/null +++ b/data/threadbuilder/threadbuildergraph.go @@ -0,0 +1,11 @@ +//go:build ((!linux && !darwin) || android || ios || nographviz) && !amd64 +// +build !linux,!darwin android ios nographviz +// +build !amd64 + +package threadbuilder + +import "fmt" + +func (t *ThreadBuilder) Graph() (string, error) { + return "", fmt.Errorf("building graphs is not supported") +} diff --git a/data/threadbuilder/threadbuildergraph_nix.go b/data/threadbuilder/threadbuildergraph_nix.go new file mode 100644 index 00000000..75c7e06d --- /dev/null +++ b/data/threadbuilder/threadbuildergraph_nix.go @@ -0,0 +1,153 @@ +//go:build (linux || darwin) && !android && !ios && !nographviz && (amd64 || arm64) +// +build linux darwin +// +build !android +// +build !ios +// +build !nographviz +// +build amd64 arm64 + +package threadbuilder + +import ( + "fmt" + "strings" + "unicode" + + "github.com/awalterschulze/gographviz" +) + +// To quickly look at visualized string you can use https://dreampuf.github.io/GraphvizOnline + +type EdgeParameters struct { + style string + color string + label string +} + +func (t *ThreadBuilder) Graph() (string, error) { + // TODO: check updates on https://github.com/goccy/go-graphviz/issues/52 or make a fix yourself to use better library here + graph := gographviz.NewGraph() + graph.SetName("G") + graph.SetDir(true) + var nodes = make(map[string]struct{}) + + var addNodes = func(r *threadRecord) error { + // TODO: revisit function after checking + + style := "solid" + if r.GetAclData() != nil { + style = "filled" + } else if r.changesData != nil { + style = "dashed" + } + + var chSymbs []string + if r.changesData != nil { + for _, chc := range r.changesData.Content { + tp := fmt.Sprintf("%T", chc.Value) + tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) + res := "" + for _, ts := range tp { + if unicode.IsUpper(ts) { + res += string(ts) + } + } + chSymbs = append(chSymbs, res) + } + } + if r.GetAclData() != nil { + for _, chc := range r.GetAclData().AclContent { + tp := fmt.Sprintf("%T", chc.Value) + tp = strings.Replace(tp, "ACLChangeACLContentValueValueOf", "", 1) + res := "" + for _, ts := range tp { + if unicode.IsUpper(ts) { + res += string(ts) + } + } + chSymbs = append(chSymbs, res) + } + } + + shortId := r.id + label := fmt.Sprintf("Id: %s\nChanges: %s\n", + shortId, + strings.Join(chSymbs, ","), + ) + e := graph.AddNode("G", "\""+r.id+"\"", map[string]string{ + "label": "\"" + label + "\"", + "style": "\"" + style + "\"", + }) + if e != nil { + return e + } + nodes[r.id] = struct{}{} + return nil + } + + var createEdge = func(firstId, secondId string, params EdgeParameters) error { + _, exists := nodes[firstId] + if !exists { + return fmt.Errorf("no such node") + } + _, exists = nodes[secondId] + if !exists { + return fmt.Errorf("no previous node") + } + + err := graph.AddEdge("\""+firstId+"\"", "\""+secondId+"\"", true, map[string]string{ + "color": params.color, + "style": params.style, + }) + if err != nil { + return err + } + + return nil + } + + var addLinks = func(t *threadRecord) error { + for _, prevId := range t.AclHeadIds { + err := createEdge(t.id, prevId, EdgeParameters{ + style: "dashed", + color: "red", + }) + if err != nil { + return err + } + } + + for _, prevId := range t.TreeHeadIds { + err := createEdge(t.id, prevId, EdgeParameters{ + style: "dashed", + color: "blue", + }) + if err != nil { + return err + } + } + + if t.SnapshotBaseId != "" { + err := createEdge(t.id, t.SnapshotBaseId, EdgeParameters{ + style: "bold", + color: "blue", + }) + if err != nil { + return err + } + } + + return nil + } + + err := t.traverseFromHeads(addNodes) + if err != nil { + return "", err + } + + err = t.traverseFromHeads(addLinks) + if err != nil { + return "", err + } + + return graph.String(), nil +} diff --git a/data/threadbuilder/userjoinexample.yml b/data/threadbuilder/userjoinexample.yml new file mode 100644 index 00000000..e70c9fc3 --- /dev/null +++ b/data/threadbuilder/userjoinexample.yml @@ -0,0 +1,109 @@ +thread: + author: A +logs: + - id: A.1 + identity: A + records: + - id: A.1.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + changes: + - blockAdd: + id: root + readKey: key.Read.1 + - id: A.1.2 + aclChanges: + - userInvite: + acceptKey: key.Sign.Onetime1 + encryptionKey: key.Enc.Onetime1 + encryptedReadKeys: [key.Read.1] + permissions: writer + - userAdd: + identity: C + permission: reader + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: A.1.3 + changes: + - blockAdd: + id: second + targetId: root + readKey: key.Read.1 + - id: B.1 + identity: B + records: + - id: B.1.1 + aclChanges: + - userJoin: + identity: B + encryptionKey: key.Enc.B + acceptSignature: key.Sign.Onetime1 + inviteId: A.1.2 + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1.2 + changes: + - blockAdd: + id: first + targetId: root + readKey: key.Read.1 + - id: C.1 + identity: C + records: + - id: C.1.1 + changes: + - blockAdd: + id: third + targetId: root + readKey: key.Read.1 +keys: + Enc: + - A + - B + - C + - Onetime1 + Sign: + - A + - B + - C + - Onetime1 + Read: + - 1 +graph: + - id: A.1.1 + baseSnapshot: A.1.1 + - id: A.1.2 + baseSnapshot: A.1.1 + aclHeads: [A.1.1] + treeHeads: [A.1.1] + - id: B.1.1 + baseSnapshot: A.1.1 + aclHeads: [A.1.2] + treeHeads: [A.1.2] + - id: B.1.2 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: A.1.3 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.2, C.1.1] + - id: C.1.1 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + diff --git a/data/threadbuilder/userremovebeforeexample.yml b/data/threadbuilder/userremovebeforeexample.yml new file mode 100644 index 00000000..a5abfd13 --- /dev/null +++ b/data/threadbuilder/userremovebeforeexample.yml @@ -0,0 +1,105 @@ +thread: + author: A +logs: + - id: A.1 + identity: A + records: + - id: A.1.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + - userAdd: + identity: B + permission: admin + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + changes: + - blockAdd: + id: root + readKey: key.Read.1 + - id: A.1.2 + aclChanges: + - userRemove: + removedIdentity: B + newReadKey: key.Read.2 + identitiesLeft: [A, C] + readKey: key.Read.2 + - id: A.1.3 + aclChanges: + - userAdd: + identity: E + permission: admin + encryptionKey: key.Enc.E + encryptedReadKeys: [key.Read.1, key.Read.2] + readKey: key.Read.2 + - id: B.1 + identity: B + records: + - id: B.1.1 + aclChanges: + - userAdd: + identity: C + permission: admin + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: B.1.2 + aclChanges: + - userAdd: + identity: D + permission: admin + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 +keys: + Enc: + - A + - B + - C + - D + - E + Sign: + - A + - B + - C + - D + - E + Read: + - 1 + - 2 +graph: + - id: A.1.1 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + - id: A.1.2 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: B.1.1 + baseSnapshot: A.1.1 + aclHeads: [A.1.1] + treeHeads: [A.1.1] + - id: B.1.2 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: A.1.3 + baseSnapshot: A.1.1 + aclHeads: [A.1.2] + treeHeads: [A.1.2] diff --git a/data/threadbuilder/userremoveexample.yml b/data/threadbuilder/userremoveexample.yml new file mode 100644 index 00000000..6176b0ac --- /dev/null +++ b/data/threadbuilder/userremoveexample.yml @@ -0,0 +1,106 @@ +thread: + author: A +logs: + - id: A.1 + identity: A + records: + - id: A.1.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + changes: + - blockAdd: + id: root + readKey: key.Read.1 + - id: A.1.2 + aclChanges: + - userInvite: + acceptKey: key.Sign.Onetime1 + encryptionKey: key.Enc.Onetime1 + encryptedReadKeys: [key.Read.1] + permissions: writer + readKey: key.Read.1 + - id: A.1.3 + aclChanges: + - userRemove: + removedIdentity: B + newReadKey: key.Read.2 + identitiesLeft: [A] + readKey: key.Read.2 + - id: A.1.4 + changes: + - blockAdd: + id: second + targetId: root + readKey: key.Read.2 + - id: B.1 + identity: B + records: + - id: B.1.1 + aclChanges: + - userJoin: + identity: B + encryptionKey: key.Enc.B + acceptSignature: key.Sign.Onetime1 + inviteId: A.1.2 + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1.2 + changes: + - blockAdd: + id: first + targetId: root + readKey: key.Read.1 +keys: + Enc: + - A + - B + - Onetime1 + Sign: + - A + - B + - Onetime1 + Read: + - 1 + - 2 +graph: + - id: A.1.1 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + - id: A.1.2 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + aclHeads: [A.1.1] + treeHeads: [A.1.1] + - id: B.1.1 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + aclHeads: [A.1.2] + treeHeads: [A.1.2] + - id: B.1.2 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: A.1.3 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: A.1.4 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + aclHeads: [A.1.3] + treeHeads: [A.1.3] diff --git a/data/threadbuilder/validsnapshotexample.yml b/data/threadbuilder/validsnapshotexample.yml new file mode 100644 index 00000000..abdcd093 --- /dev/null +++ b/data/threadbuilder/validsnapshotexample.yml @@ -0,0 +1,126 @@ +thread: + author: A +logs: + - id: A.1 + identity: A + records: + - id: A.1.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + - userAdd: + identity: B + permission: admin + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: A.1.2 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: C + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + permission: admin + - identity: D + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: D + permission: admin + encryptionKey: key.Enc.D + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: A.1.3 + aclChanges: + - userAdd: + identity: E + permission: admin + encryptionKey: key.Enc.E + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1 + identity: B + records: + - id: B.1.1 + aclChanges: + - userAdd: + identity: C + permission: admin + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: B.1.2 + aclChanges: + - userAdd: + identity: F + permission: admin + encryptionKey: key.Enc.F + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 +keys: + Enc: + - A + - B + - C + - D + - E + - F + Sign: + - A + - B + - C + - D + - E + - F + Read: + - 1 + - 2 +graph: + - id: A.1.1 + baseSnapshot: A.1.1 + aclSnapshot: A.1.1 + - id: A.1.2 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: B.1.1 + baseSnapshot: A.1.1 + aclHeads: [A.1.1] + treeHeads: [A.1.1] + - id: B.1.2 + baseSnapshot: A.1.2 + aclHeads: [A.1.2] + treeHeads: [A.1.2] + - id: A.1.3 + baseSnapshot: A.1.2 + aclHeads: [A.1.2] + treeHeads: [A.1.2] diff --git a/data/threadbuilder/ymlentities.go b/data/threadbuilder/ymlentities.go new file mode 100644 index 00000000..771bbf27 --- /dev/null +++ b/data/threadbuilder/ymlentities.go @@ -0,0 +1,103 @@ +package threadbuilder + +type ThreadDescription struct { + Author string `yaml:"author"` +} + +type Keys struct { + Enc []string `yaml:"Enc"` + Sign []string `yaml:"Sign"` + Read []string `yaml:"Read"` +} + +type ACLSnapshot struct { + UserStates []struct { + Identity string `yaml:"identity"` + EncryptionKey string `yaml:"encryptionKey"` + EncryptedReadKeys []string `yaml:"encryptedReadKeys"` + Permissions string `yaml:"permission"` + IsConfirmed bool `yaml:"isConfirmed"` + } `yaml:"userStates"` +} + +type ChangeSnapshot struct { + Blocks []struct { + Id string `yaml:"id"` + ChildrenIds []string `yaml:"childrenIds"` + } `yaml:"blocks"` +} + +type ACLChange struct { + UserAdd *struct { + Identity string `yaml:"identity"` + EncryptionKey string `yaml:"encryptionKey"` + EncryptedReadKeys []string `yaml:"encryptedReadKeys"` + Permission string `yaml:"permission"` + } `yaml:"userAdd"` + + UserJoin *struct { + Identity string `yaml:"identity"` + EncryptionKey string `yaml:"encryptionKey"` + AcceptSignature string `yaml:"acceptSignature"` + InviteId string `yaml:"inviteId"` + EncryptedReadKeys []string `yaml:"encryptedReadKeys"` + } `yaml:"userJoin"` + + UserInvite *struct { + AcceptKey string `yaml:"acceptKey"` + EncryptionKey string `yaml:"encryptionKey"` + EncryptedReadKeys []string `yaml:"encryptedReadKeys"` + Permissions string `yaml:"permissions"` + } `yaml:"userInvite"` + + UserConfirm *struct { + Identity string `yaml:"identity"` + UserAddId string `yaml:"UserAddId"` + } `yaml:"userConfirm"` + + UserRemove *struct { + RemovedIdentity string `yaml:"removedIdentity"` + NewReadKey string `yaml:"newReadKey"` + IdentitiesLeft []string `yaml:"identitiesLeft"` + } `yaml:"userRemove"` + + UserPermissionChange *struct { + Identity string `yaml:"identity"` + Permission string `yaml:"permission"` + } +} + +type DocumentChange struct { + BlockAdd *struct { + Id string `yaml:"id"` + TargetId string `yaml:"targetId"` + } `yaml:"blockAdd"` +} + +type YMLThread struct { + Description *ThreadDescription `yaml:"thread"` + Logs []struct { + Id string `yaml:"id"` + Identity string `yaml:"identity"` + Records []struct { + Id string `yaml:"id"` + + AclSnapshot *ACLSnapshot `yaml:"aclSnapshot"` + Snapshot *ChangeSnapshot `yaml:"snapshot"` + AclChanges []*ACLChange `yaml:"aclChanges"` + Changes []*DocumentChange `yaml:"changes"` + + ReadKey string `yaml:"readKey"` + } `yaml:"records"` + } `yaml:"logs"` + + Keys Keys `yaml:"keys"` + + Graph []struct { + Id string `yaml:"id"` + BaseSnapshot string `yaml:"baseSnapshot"` + AclSnapshot string `yaml:"aclSnapshot"` + ACLHeads []string `yaml:"aclHeads"` + TreeHeads []string `yaml:"treeHeads"` + } `yaml:"graph"` +} diff --git a/data/threadbuilder/ymlentities_test.go b/data/threadbuilder/ymlentities_test.go new file mode 100644 index 00000000..d2420eae --- /dev/null +++ b/data/threadbuilder/ymlentities_test.go @@ -0,0 +1,12 @@ +package threadbuilder + +import ( + "fmt" + "testing" +) + +func Test_YamlParse(t *testing.T) { + tb, _ := NewThreadBuilderFromFile("userjoinexample.yml") + gr, _ := tb.Graph() + fmt.Println(gr) +} diff --git a/data/threadhelpers.go b/data/threadhelpers.go new file mode 100644 index 00000000..78b8df3d --- /dev/null +++ b/data/threadhelpers.go @@ -0,0 +1,81 @@ +package data + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/core/block/editor/state" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" +) + +type ACLContext struct { + Tree *Tree + ACLState *ACLState + DocState *state.State +} + +func createTreeFromThread(t threadmodels.Thread, fromStart bool) (*Tree, error) { + treeBuilder := NewTreeBuilder(t, threadmodels.NewEd25519Decoder()) + return treeBuilder.Build(fromStart) +} + +func createACLStateFromThread( + t threadmodels.Thread, + identity string, + key threadmodels.EncryptionPrivKey, + decoder threadmodels.SigningPubKeyDecoder, + fromStart bool) (*ACLContext, error) { + tree, err := createTreeFromThread(t, fromStart) + if err != nil { + return nil, err + } + + aclTreeBuilder := NewACLTreeBuilder(t, decoder) + aclTree, err := aclTreeBuilder.Build() + if err != nil { + return nil, err + } + + if !fromStart { + snapshotValidator := NewSnapshotValidator(aclTree, identity, key, decoder) + 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 + return createACLStateFromThread(t, identity, key, decoder, true) + } + } + + aclBuilder, err := NewACLStateBuilder(tree, identity, key, decoder) + if err != nil { + return nil, err + } + + aclState, err := aclBuilder.Build() + if err != nil { + return nil, err + } + return &ACLContext{ + Tree: tree, + ACLState: aclState, + }, nil +} + +func createDocumentStateFromThread( + t threadmodels.Thread, + identity string, + key threadmodels.EncryptionPrivKey, + decoder threadmodels.SigningPubKeyDecoder) (*ACLContext, error) { + context, err := createACLStateFromThread(t, identity, key, decoder, false) + if err != nil { + return nil, err + } + + docStateBuilder := newDocumentStateBuilder(context.Tree, context.ACLState) + docState, err := docStateBuilder.build() + if err != nil { + return nil, err + } + context.DocState = docState + + return context, nil +} diff --git a/data/threadmodels/keys.go b/data/threadmodels/keys.go new file mode 100644 index 00000000..95a7254b --- /dev/null +++ b/data/threadmodels/keys.go @@ -0,0 +1,255 @@ +package threadmodels + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha512" + "crypto/subtle" + "crypto/x509" + "errors" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/strkey" + "github.com/libp2p/go-libp2p-core/crypto" + crypto_pb "github.com/libp2p/go-libp2p-core/crypto/pb" + "io" +) + +type SigningPubKey crypto.PubKey +type SigningPrivKey crypto.PrivKey + +var MinRsaKeyBits = 2048 + +var ErrKeyLengthTooSmall = errors.New("error key length too small") + +type Key interface { + Equals(Key) bool + + Raw() ([]byte, error) +} + +type EncryptionPrivKey interface { + Key + + Decrypt([]byte) ([]byte, error) + GetPublic() EncryptionPubKey +} + +type EncryptionPubKey interface { + Key + + Encrypt(data []byte) ([]byte, error) +} + +type EncryptionRsaPrivKey struct { + privKey rsa.PrivateKey +} + +type EncryptionRsaPubKey struct { + pubKey rsa.PublicKey +} + +func (e *EncryptionRsaPubKey) Equals(key Key) bool { + other, ok := (key).(*EncryptionRsaPubKey) + if !ok { + return keyEquals(e, key) + } + + return e.pubKey.N.Cmp(other.pubKey.N) == 0 && e.pubKey.E == other.pubKey.E +} + +func (e *EncryptionRsaPubKey) Raw() ([]byte, error) { + return x509.MarshalPKIXPublicKey(&e.pubKey) +} + +func (e *EncryptionRsaPubKey) Encrypt(data []byte) ([]byte, error) { + hash := sha512.New() + return rsa.EncryptOAEP(hash, rand.Reader, &e.pubKey, data, nil) +} + +func (e *EncryptionRsaPrivKey) Equals(key Key) bool { + other, ok := (key).(*EncryptionRsaPrivKey) + if !ok { + return keyEquals(e, key) + } + + return e.privKey.N.Cmp(other.privKey.N) == 0 && e.privKey.E == other.privKey.E +} + +func (e *EncryptionRsaPrivKey) Raw() ([]byte, error) { + b := x509.MarshalPKCS1PrivateKey(&e.privKey) + return b, nil +} + +func (e *EncryptionRsaPrivKey) Decrypt(bytes []byte) ([]byte, error) { + hash := sha512.New() + return rsa.DecryptOAEP(hash, rand.Reader, &e.privKey, bytes, nil) +} + +func (e *EncryptionRsaPrivKey) GetPublic() EncryptionPubKey { + return &EncryptionRsaPubKey{pubKey: e.privKey.PublicKey} +} + +func GenerateRandomRSAKeyPair(bits int) (EncryptionPrivKey, EncryptionPubKey, error) { + return GenerateRSAKeyPair(bits, rand.Reader) +} + +func GenerateRSAKeyPair(bits int, src io.Reader) (EncryptionPrivKey, EncryptionPubKey, error) { + if bits < MinRsaKeyBits { + return nil, nil, ErrKeyLengthTooSmall + } + priv, err := rsa.GenerateKey(src, bits) + if err != nil { + return nil, nil, err + } + pk := priv.PublicKey + return &EncryptionRsaPrivKey{privKey: *priv}, &EncryptionRsaPubKey{pubKey: pk}, nil +} + +func NewEncryptionRsaPrivKeyFromBytes(bytes []byte) (EncryptionPrivKey, error) { + sk, err := x509.ParsePKCS1PrivateKey(bytes) + if err != nil { + return nil, err + } + if sk.N.BitLen() < MinRsaKeyBits { + return nil, ErrKeyLengthTooSmall + } + return &EncryptionRsaPrivKey{privKey: *sk}, nil +} + +func NewEncryptionRsaPubKeyFromBytes(bytes []byte) (EncryptionPubKey, error) { + pub, err := x509.ParsePKIXPublicKey(bytes) + if err != nil { + return nil, err + } + pk, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, errors.New("not actually an rsa public key") + } + if pk.N.BitLen() < MinRsaKeyBits { + return nil, ErrKeyLengthTooSmall + } + + return &EncryptionRsaPubKey{pubKey: *pk}, nil +} + +func NewSigningEd25519PubKeyFromBytes(bytes []byte) (SigningPubKey, error) { + return crypto.UnmarshalEd25519PublicKey(bytes) +} + +func GenerateRandomEd25519KeyPair() (SigningPrivKey, SigningPubKey, error) { + return crypto.GenerateEd25519Key(rand.Reader) +} + +func keyEquals(k1, k2 Key) bool { + a, err := k1.Raw() + if err != nil { + return false + } + b, err := k2.Raw() + if err != nil { + return false + } + return subtle.ConstantTimeCompare(a, b) == 1 +} + +type Ed25519SigningPubKeyDecoder struct{} + +func NewEd25519Decoder() *Ed25519SigningPubKeyDecoder { + return &Ed25519SigningPubKeyDecoder{} +} + +func (e *Ed25519SigningPubKeyDecoder) DecodeFromBytes(bytes []byte) (SigningPubKey, error) { + return NewSigningEd25519PubKeyFromBytes(bytes) +} + +func (e *Ed25519SigningPubKeyDecoder) DecodeFromString(identity string) (SigningPubKey, error) { + pubKeyRaw, err := strkey.Decode(0x5b, identity) + if err != nil { + return nil, err + } + + return e.DecodeFromBytes(pubKeyRaw) +} + +func (e *Ed25519SigningPubKeyDecoder) DecodeFromStringIntoBytes(identity string) ([]byte, error) { + return strkey.Decode(0x5b, identity) +} + +func (e *Ed25519SigningPubKeyDecoder) EncodeToString(pubkey crypto.PubKey) (string, error) { + raw, err := pubkey.Raw() + if err != nil { + return "", err + } + return strkey.Encode(0x5b, raw) +} + +type SigningPubKeyDecoder interface { + DecodeFromBytes(bytes []byte) (SigningPubKey, error) + DecodeFromString(identity string) (SigningPubKey, error) + DecodeFromStringIntoBytes(identity string) ([]byte, error) +} + +// Below keys are required for testing and mocking purposes + +type EmptyRecorderEncryptionKey struct { + recordedEncrypted [][]byte + recordedDecrypted [][]byte +} + +func (f *EmptyRecorderEncryptionKey) Equals(key Key) bool { + return true +} + +func (f *EmptyRecorderEncryptionKey) Raw() ([]byte, error) { + panic("can't get bytes from this key") +} + +func (f *EmptyRecorderEncryptionKey) GetPublic() EncryptionPubKey { + panic("this key doesn't have a public key") +} + +func (f *EmptyRecorderEncryptionKey) Encrypt(msg []byte) ([]byte, error) { + f.recordedEncrypted = append(f.recordedEncrypted, msg) + return msg, nil +} + +func (f *EmptyRecorderEncryptionKey) Decrypt(msg []byte) ([]byte, error) { + f.recordedDecrypted = append(f.recordedDecrypted, msg) + return msg, nil +} + +type SignatureVerificationPayload struct { + message []byte + signature []byte +} + +type EmptyRecorderVerificationKey struct { + verifications []SignatureVerificationPayload +} + +func (e *EmptyRecorderVerificationKey) Bytes() ([]byte, error) { + panic("can't get bytes from this key") +} + +func (e *EmptyRecorderVerificationKey) Equals(key crypto.Key) bool { + return true +} + +func (e *EmptyRecorderVerificationKey) Raw() ([]byte, error) { + panic("can't get bytes from this key") +} + +func (e *EmptyRecorderVerificationKey) Type() crypto_pb.KeyType { + panic("can't get type from this key") +} + +func (e *EmptyRecorderVerificationKey) Verify(data []byte, sig []byte) (bool, error) { + e.verifications = append(e.verifications, SignatureVerificationPayload{ + message: data, + signature: sig, + }) + return true, nil +} + +func NewMockSigningPubKeyFromBytes(bytes []byte) (SigningPubKey, error) { + return &EmptyRecorderVerificationKey{}, nil +} diff --git a/data/threadmodels/models.go b/data/threadmodels/models.go new file mode 100644 index 00000000..a9ef39cc --- /dev/null +++ b/data/threadmodels/models.go @@ -0,0 +1,34 @@ +package threadmodels + +import ( + "context" + + "github.com/gogo/protobuf/proto" +) + +type Thread interface { + ID() string + GetLogs() ([]ThreadLog, error) + GetRecord(ctx context.Context, recordID string) (*ThreadRecord, error) + PushRecord(payload proto.Marshaler) (id string, err error) + + // SubscribeForRecords() +} + +type SignedPayload struct { + Payload []byte + Signature []byte +} + +type ThreadRecord struct { + PrevId string + Id string + LogId string + Signed *SignedPayload +} + +type ThreadLog struct { + ID string + Head string + Counter int64 +} diff --git a/data/threadmodels/threadid.go b/data/threadmodels/threadid.go new file mode 100644 index 00000000..4d2f0392 --- /dev/null +++ b/data/threadmodels/threadid.go @@ -0,0 +1,71 @@ +package threadmodels + +import ( + "crypto/rand" + "encoding/binary" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/core/smartblock" + "github.com/textileio/go-threads/core/thread" + "hash/fnv" +) + +func CreateACLThreadID(k SigningPubKey, blockType smartblock.SmartBlockType) (thread.ID, error) { + rndlen := 32 + buf := make([]byte, 8+rndlen) + + // adding random bytes in the end + _, err := rand.Read(buf[8 : 8+rndlen]) + if err != nil { + panic("random read failed") + } + + keyBytes, err := k.Bytes() + if err != nil { + return thread.Undef, err + } + + hasher := fnv.New64() + hasher.Write(keyBytes) + res := hasher.Sum64() + + // putting hash of the pubkey in the beginning + binary.LittleEndian.PutUint64(buf[:8], res) + + return threadIDFromBytes(blockType, buf) +} + +func VerifyACLThreadID(k SigningPubKey, threadId thread.ID) (bool, error) { + bytes := threadId.Bytes() + pubKeyBytes := threadId.Bytes()[len(bytes)-40 : len(bytes)-32] + hash := binary.LittleEndian.Uint64(pubKeyBytes) + + keyBytes, err := k.Bytes() + if err != nil { + return false, err + } + + hasher := fnv.New64() + hasher.Write(keyBytes) + realHash := hasher.Sum64() + + return hash == realHash, nil +} + +func threadIDFromBytes( + blockType smartblock.SmartBlockType, + b []byte) (thread.ID, error) { + blen := len(b) + + // two 8 bytes (max) numbers plus num + buf := make([]byte, 2*binary.MaxVarintLen64+blen) + n := binary.PutUvarint(buf, thread.V1) + n += binary.PutUvarint(buf[n:], uint64(thread.AccessControlled)) + n += binary.PutUvarint(buf[n:], uint64(blockType)) + + cn := copy(buf[n:], b) + if cn != blen { + return thread.Undef, fmt.Errorf("copy length is inconsistent") + } + + return thread.Cast(buf[:n+blen]) +} diff --git a/data/threadmodels/threadid_test.go b/data/threadmodels/threadid_test.go new file mode 100644 index 00000000..c6516aae --- /dev/null +++ b/data/threadmodels/threadid_test.go @@ -0,0 +1,27 @@ +package threadmodels + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/core/smartblock" + "testing" +) + +func TestCreateACLThreadIDVerify(t *testing.T) { + _, pubKey, err := GenerateRandomEd25519KeyPair() + if err != nil { + t.Fatalf("should not return error after generating key pair: %v", err) + } + + thread, err := CreateACLThreadID(pubKey, smartblock.SmartBlockTypeWorkspace) + if err != nil { + t.Fatalf("should not return error after generating thread: %v", err) + } + + verified, err := VerifyACLThreadID(pubKey, thread) + if err != nil { + t.Fatalf("verification should not return error: %v", err) + } + + if !verified { + t.Fatalf("the thread should be verified") + } +} diff --git a/data/tree.go b/data/tree.go new file mode 100644 index 00000000..0e58823d --- /dev/null +++ b/data/tree.go @@ -0,0 +1,375 @@ +package data + +import ( + "bytes" + "crypto/md5" + "fmt" + "sort" +) + +type Mode int + +const ( + Append Mode = iota + Rebuild + Nothing +) + +type Tree struct { + root *Change + headIds []string + metaHeadIds []string + attached map[string]*Change + unAttached map[string]*Change + // missed id -> list of dependency ids + waitList map[string][]string + invalidChanges map[string]struct{} + + // bufs + iterCompBuf []*Change + iterQueue []*Change + + duplicateEvents int +} + +func (t *Tree) RootId() string { + if t.root != nil { + return t.root.Id + } + return "" +} + +func (t *Tree) Root() *Change { + return t.root +} + +func (t *Tree) AddFast(changes ...*Change) { + for _, c := range changes { + // ignore existing + if _, ok := t.attached[c.Id]; ok { + continue + } else if _, ok := t.unAttached[c.Id]; ok { + continue + } + t.add(c) + } + t.updateHeads() +} + +func (t *Tree) Add(changes ...*Change) (mode Mode) { + var beforeHeadIds = t.headIds + var attached bool + var empty = t.Len() == 0 + for _, c := range changes { + // ignore existing + if _, ok := t.attached[c.Id]; ok { + continue + } else if _, ok := t.unAttached[c.Id]; ok { + continue + } + if t.add(c) { + attached = true + } + } + if !attached { + return Nothing + } + t.updateHeads() + if empty { + return Rebuild + } + for _, hid := range beforeHeadIds { + for _, newCh := range changes { + if _, ok := t.attached[newCh.Id]; ok { + if !t.after(newCh.Id, hid) { + return Rebuild + } + } + } + } + return Append +} + +func (t *Tree) RemoveInvalidChange(id string) { + stack := []string{id} + // removing all children of this id (either next or unattached) + for len(stack) > 0 { + var exists bool + top := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + if _, exists = t.invalidChanges[top]; exists { + continue + } + + var rem *Change + t.invalidChanges[top] = struct{}{} + if rem, exists = t.unAttached[top]; exists { + delete(t.unAttached, top) + } else if rem, exists = t.attached[top]; exists { + // remove from all prev changes + for _, id := range rem.PreviousIds { + prev, exists := t.attached[id] + if !exists { + continue + } + for i, next := range prev.Next { + if next.Id == top { + prev.Next[i] = nil + prev.Next = append(prev.Next[:i], prev.Next[i+1:]...) + break + } + } + } + delete(t.attached, top) + } + for _, el := range rem.Unattached { + stack = append(stack, el.Id) + } + for _, el := range rem.Next { + stack = append(stack, el.Id) + } + } +} + +func (t *Tree) add(c *Change) (attached bool) { + if c == nil { + return false + } + if _, exists := t.invalidChanges[c.Id]; exists { + return false + } + + if t.root == nil { // first element + t.root = c + t.attached = map[string]*Change{ + c.Id: c, + } + t.unAttached = make(map[string]*Change) + t.waitList = make(map[string][]string) + t.invalidChanges = make(map[string]struct{}) + return true + } + if len(c.PreviousIds) > 1 { + sort.Strings(c.PreviousIds) + } + // attaching only if all prev ids are attached + attached = true + for _, pid := range c.PreviousIds { + if prev, ok := t.attached[pid]; ok { + prev.Unattached = append(prev.Unattached, c) + continue + } + attached = false + if prev, ok := t.unAttached[pid]; ok { + prev.Unattached = append(prev.Unattached, c) + continue + } + wl := t.waitList[pid] + wl = append(wl, c.Id) + t.waitList[pid] = wl + } + if attached { + t.attach(c, true) + } else { + // clearing wait list + for _, wid := range t.waitList[c.Id] { + c.Unattached = append(c.Unattached, t.unAttached[wid]) + } + delete(t.waitList, c.Id) + t.unAttached[c.Id] = c + } + return +} + +func (t *Tree) canAttach(c *Change) (attach bool) { + if c == nil { + return false + } + attach = true + for _, id := range c.PreviousIds { + if _, exists := t.attached[id]; !exists { + attach = false + } + } + return +} + +func (t *Tree) attach(c *Change, newEl bool) { + t.attached[c.Id] = c + if !newEl { + delete(t.unAttached, c.Id) + } + + // add next to all prev changes + for _, id := range c.PreviousIds { + // prev id must be attached if we attach this id + prev := t.attached[id] + prev.Next = append(prev.Next, c) + if len(prev.Next) > 1 { + sort.Sort(sortChanges(prev.Next)) + } + for i, next := range prev.Unattached { + if next.Id == c.Id { + prev.Unattached[i] = nil + prev.Unattached = append(prev.Unattached[:i], prev.Unattached[i+1:]...) + break + } + } + } + + // clearing wait list + if waitIds, ok := t.waitList[c.Id]; ok { + for _, wid := range waitIds { + next := t.unAttached[wid] + if t.canAttach(next) { + t.attach(next, false) + } + } + delete(t.waitList, c.Id) + } + + for _, next := range c.Unattached { + if t.canAttach(next) { + t.attach(next, false) + } + } +} + +func (t *Tree) after(id1, id2 string) (found bool) { + t.iterate(t.attached[id2], func(c *Change) (isContinue bool) { + if c.Id == id1 { + found = true + return false + } + return true + }) + return +} + +func (t *Tree) dfs(startChange string) (uniqMap map[string]*Change) { + stack := make([]*Change, 0, 10) + stack = append(stack, t.attached[startChange]) + uniqMap = map[string]*Change{} + + for len(stack) > 0 { + ch := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if _, exists := uniqMap[ch.Id]; exists { + continue + } + + uniqMap[ch.Id] = ch + + for _, prev := range ch.PreviousIds { + stack = append(stack, t.attached[prev]) + } + } + return uniqMap +} + +func (t *Tree) updateHeads() { + var newHeadIds, newMetaHeadIds []string + t.iterate(t.root, func(c *Change) (isContinue bool) { + if len(c.Next) == 0 { + newHeadIds = append(newHeadIds, c.Id) + } + return true + }) + t.headIds = newHeadIds + t.metaHeadIds = newMetaHeadIds + sort.Strings(t.headIds) + sort.Strings(t.metaHeadIds) +} + +func (t *Tree) iterate(start *Change, f func(c *Change) (isContinue bool)) { + it := newIterator() + defer freeIterator(it) + it.iterate(start, f) +} + +func (t *Tree) iterateSkip(start *Change, skipBefore *Change, f func(c *Change) (isContinue bool)) { + it := newIterator() + defer freeIterator(it) + it.iterateSkip(start, skipBefore, f) +} + +func (t *Tree) Iterate(startId string, f func(c *Change) (isContinue bool)) { + t.iterate(t.attached[startId], f) +} + +func (t *Tree) IterateBranching(startId string, f func(c *Change, branchLevel int) (isContinue bool)) { + // branchLevel indicates the number of parallel branches + var bc int + t.iterate(t.attached[startId], func(c *Change) (isContinue bool) { + if pl := len(c.PreviousIds); pl > 1 { + bc -= pl - 1 + } + bl := bc + if nl := len(c.Next); nl > 1 { + bc += nl - 1 + } + return f(c, bl) + }) +} + +func (t *Tree) Hash() string { + h := md5.New() + n := 0 + t.iterate(t.root, func(c *Change) (isContinue bool) { + n++ + fmt.Fprintf(h, "-%s", c.Id) + return true + }) + return fmt.Sprintf("%d-%x", n, h.Sum(nil)) +} + +func (t *Tree) GetDuplicateEvents() int { + return t.duplicateEvents +} + +func (t *Tree) ResetDuplicateEvents() { + t.duplicateEvents = 0 +} + +func (t *Tree) Len() int { + return len(t.attached) +} + +func (t *Tree) Heads() []string { + return t.headIds +} + +func (t *Tree) String() string { + var buf = bytes.NewBuffer(nil) + t.Iterate(t.RootId(), func(c *Change) (isContinue bool) { + buf.WriteString(c.Id) + if len(c.Next) > 1 { + buf.WriteString("-<") + } else if len(c.Next) > 0 { + buf.WriteString("->") + } else { + buf.WriteString("-|") + } + return true + }) + return buf.String() +} + +func (t *Tree) Get(id string) *Change { + return t.attached[id] +} + +type sortChanges []*Change + +func (s sortChanges) Len() int { + return len(s) +} + +func (s sortChanges) Less(i, j int) bool { + return s[i].Id < s[j].Id +} + +func (s sortChanges) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} diff --git a/data/treebuilder.go b/data/treebuilder.go new file mode 100644 index 00000000..0a226d70 --- /dev/null +++ b/data/treebuilder.go @@ -0,0 +1,437 @@ +package data + +import ( + "context" + "errors" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" + "github.com/gogo/protobuf/proto" + "github.com/textileio/go-threads/core/thread" + "sort" + "time" +) + +var ( + log = logging.Logger("anytype-data") + ErrEmpty = errors.New("logs empty") +) + +type TreeBuilder struct { + cache map[string]*Change + logHeads map[string]*Change + identityKeys map[string]threadmodels.SigningPubKey + signingPubKeyDecoder threadmodels.SigningPubKeyDecoder + tree *Tree + thread threadmodels.Thread +} + +func NewTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *TreeBuilder { + return &TreeBuilder{ + cache: make(map[string]*Change), + logHeads: make(map[string]*Change), + identityKeys: make(map[string]threadmodels.SigningPubKey), + signingPubKeyDecoder: decoder, + tree: &Tree{}, // TODO: add NewTree method + thread: t, + } +} + +func (tb *TreeBuilder) loadChange(id string) (ch *Change, err error) { + if ch, ok := tb.cache[id]; ok { + return ch, nil + } + + // TODO: Add virtual changes logic + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + record, err := tb.thread.GetRecord(ctx, id) + if err != nil { + return nil, err + } + + aclChange := new(pb.ACLChange) + + // TODO: think what should we do with such cases, because this can be used by attacker to break our tree + if err = proto.Unmarshal(record.Signed.Payload, aclChange); err != nil { + return + } + var verified bool + verified, err = tb.verify(aclChange.Identity, record.Signed.Payload, record.Signed.Signature) + if err != nil { + return + } + if !verified { + err = fmt.Errorf("the signature of the payload cannot be verified") + return + } + + ch, err = NewChange(id, aclChange) + tb.cache[id] = ch + + return ch, nil +} + +func (tb *TreeBuilder) verify(identity string, payload, signature []byte) (isVerified bool, err error) { + identityKey, exists := tb.identityKeys[identity] + if !exists { + identityKey, err = tb.signingPubKeyDecoder.DecodeFromString(identity) + if err != nil { + return + } + tb.identityKeys[identity] = identityKey + } + return identityKey.Verify(payload, signature) +} + +func (tb *TreeBuilder) getLogs() (logs []threadmodels.ThreadLog, err error) { + // TODO: Add beforeId building logic + logs, err = tb.thread.GetLogs() + if err != nil { + return nil, fmt.Errorf("GetLogs error: %w", err) + } + + log.Debugf("build tree: logs: %v", logs) + if len(logs) == 0 || len(logs) == 1 && len(logs[0].Head) <= 1 { + return nil, ErrEmpty + } + var nonEmptyLogs = logs[:0] + for _, l := range logs { + if len(l.Head) == 0 { + continue + } + if ch, err := tb.loadChange(l.Head); err != nil { + log.Errorf("loading head %s of the log %s failed: %v", l.Head, l.ID, err) + } else { + tb.logHeads[l.ID] = ch + } + nonEmptyLogs = append(nonEmptyLogs, l) + } + return nonEmptyLogs, nil +} + +func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) { + logs, err := tb.getLogs() + if err != nil { + return nil, err + } + + // TODO: check if this should be changed if we are building from start + heads, err := tb.getActualHeads(logs) + if err != nil { + return nil, fmt.Errorf("get acl heads error: %v", err) + } + + if fromStart { + if err = tb.buildTreeFromStart(heads); err != nil { + return nil, fmt.Errorf("buildTree error: %v", err) + } + } else { + breakpoint, err := tb.findBreakpoint(heads) + if err != nil { + return nil, fmt.Errorf("findBreakpoint error: %v", err) + } + + if err = tb.buildTree(heads, breakpoint); err != nil { + return nil, fmt.Errorf("buildTree error: %v", err) + } + } + + tb.cache = nil + + return tb.tree, nil +} + +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") + } + root, err := tb.getRoot(possibleRoots) + if err != nil { + return err + } + + tb.tree.AddFast(root) + tb.tree.AddFast(changes...) + return +} + +func (tb *TreeBuilder) dfsFromStart(stack []string) (buf []*Change, possibleRoots []*Change, err error) { + buf = make([]*Change, 0, len(stack)*2) + uniqMap := make(map[string]struct{}) + + for len(stack) > 0 { + id := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if _, exists := uniqMap[id]; exists { + continue + } + + ch, err := tb.loadChange(id) + if err != nil { + continue + } + + uniqMap[id] = struct{}{} + buf = append(buf, ch) + + for _, prev := range ch.PreviousIds { + stack = append(stack, prev) + } + if len(ch.PreviousIds) == 0 { + possibleRoots = append(possibleRoots, ch) + } + } + return buf, possibleRoots, nil +} + +func (tb *TreeBuilder) buildTree(heads []string, breakpoint string) (err error) { + ch, err := tb.loadChange(breakpoint) + if err != nil { + return + } + tb.tree.AddFast(ch) + changes, err := tb.dfs(heads, breakpoint) + + tb.tree.AddFast(changes...) + return +} + +func (tb *TreeBuilder) dfs(stack []string, breakpoint string) (buf []*Change, err error) { + buf = make([]*Change, 0, len(stack)*2) + uniqMap := map[string]struct{}{breakpoint: {}} + for len(stack) > 0 { + id := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if _, exists := uniqMap[id]; exists { + continue + } + + ch, err := tb.loadChange(id) + if err != nil { + continue + } + + uniqMap[id] = struct{}{} + buf = append(buf, ch) + + for _, prev := range ch.PreviousIds { + stack = append(stack, prev) + } + } + return buf, nil +} + +func (tb *TreeBuilder) getActualHeads(logs []threadmodels.ThreadLog) (heads []string, err error) { + sort.Slice(logs, func(i, j int) bool { + return logs[i].ID < logs[j].ID + }) + var knownHeads []string + var validLogs = logs[:0] + for _, l := range logs { + if slice.FindPos(knownHeads, l.Head) != -1 { // do not scan known heads + continue + } + sh, err := tb.getNearSnapshot(l.Head) + if err != nil { + log.Warnf("can't get near snapshot: %v; ignore", err) + continue + } + if sh.LogHeads != nil { + for _, headId := range sh.LogHeads { + knownHeads = append(knownHeads, headId) + } + } + validLogs = append(validLogs, l) + } + for _, l := range validLogs { + if slice.FindPos(knownHeads, l.Head) != -1 { // do not scan known heads + continue + } else { + heads = append(heads, l.Head) + } + } + if len(heads) == 0 { + return nil, fmt.Errorf("no usable logs in head") + } + return +} + +func (tb *TreeBuilder) getNearSnapshot(id string) (sh *Change, err error) { + ch, err := tb.loadChange(id) + if err != nil { + return + } + + if ch.IsSnapshot { + sh = ch + } else { + sh, err = tb.loadChange(ch.SnapshotId) + if err != nil { + return nil, err + } + } + + return sh, nil +} + +func (tb *TreeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) { + var ( + ch *Change + snapshotIds []string + ) + for _, head := range heads { + if ch, err = tb.loadChange(head); err != nil { + return + } + shId := ch.SnapshotId + if slice.FindPos(snapshotIds, shId) == -1 { + snapshotIds = append(snapshotIds, shId) + } + } + return tb.findCommonSnapshot(snapshotIds) +} + +func (tb *TreeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId string, err error) { + if len(snapshotIds) == 1 { + return snapshotIds[0], nil + } else if len(snapshotIds) == 0 { + return "", fmt.Errorf("snapshots not found") + } + + for len(snapshotIds) > 1 { + l := len(snapshotIds) + shId, e := tb.findCommonForTwoSnapshots(snapshotIds[l-2], snapshotIds[l-1]) + if e != nil { + return "", e + } + snapshotIds[l-2] = shId + snapshotIds = snapshotIds[:l-1] + } + return snapshotIds[0], nil +} + +func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err error) { + // fast cases + if s1 == s2 { + return s1, nil + } + ch1, err := tb.loadChange(s1) + if err != nil { + return "", err + } + if ch1.SnapshotId == s2 { + return s2, nil + } + ch2, err := tb.loadChange(s2) + if err != nil { + return "", err + } + if ch2.SnapshotId == s1 { + return s1, nil + } + if ch1.SnapshotId == ch2.SnapshotId && ch1.SnapshotId != "" { + return ch1.SnapshotId, nil + } + // traverse + var t1 = make([]string, 0, 5) + var t2 = make([]string, 0, 5) + t1 = append(t1, ch1.Id, ch1.SnapshotId) + t2 = append(t2, ch2.Id, ch2.SnapshotId) + for { + lid1 := t1[len(t1)-1] + if lid1 != "" { + l1, e := tb.loadChange(lid1) + if e != nil { + return "", e + } + if l1.SnapshotId != "" { + if slice.FindPos(t2, l1.SnapshotId) != -1 { + return l1.SnapshotId, nil + } + } + t1 = append(t1, l1.SnapshotId) + } + lid2 := t2[len(t2)-1] + if lid2 != "" { + l2, e := tb.loadChange(t2[len(t2)-1]) + if e != nil { + return "", e + } + if l2.SnapshotId != "" { + if slice.FindPos(t1, l2.SnapshotId) != -1 { + return l2.SnapshotId, nil + } + } + t2 = append(t2, l2.SnapshotId) + } + if lid1 == "" && lid2 == "" { + break + } + } + + 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)) + 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)) + return s1, nil + } + + isEmptySnapshot := func(ch *Change) bool { + // TODO: add more sophisticated checks in Change for snapshots + return !ch.IsSnapshot + } + + // 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) + return s2, nil + } else if isEmptySnapshot(ch2) && !isEmptySnapshot(ch1) { + 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) + return s1, nil + } + log.Warnf("changes build tree: prefer %s (%s<%s)", s2, s2, s1) + + return s2, nil +} + +func (tb *TreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { + threadId, err := thread.Decode(tb.thread.ID()) + if err != nil { + return nil, err + } + + for _, r := range possibleRoots { + id := r.Content.Identity + sk, err := tb.signingPubKeyDecoder.DecodeFromString(id) + if err != nil { + continue + } + + res, err := threadmodels.VerifyACLThreadID(sk, threadId) + if err != nil { + continue + } + + if res { + return r, nil + } + } + return nil, fmt.Errorf("could not find any root") +} diff --git a/data/treebuilder_test.go b/data/treebuilder_test.go new file mode 100644 index 00000000..fa4a8971 --- /dev/null +++ b/data/treebuilder_test.go @@ -0,0 +1,58 @@ +package data + +//func TestACLTreeBuilder_UserJoinCorrectHeadsAndLen(t *testing.T) { +// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// if err != nil { +// t.Fatal(err) +// } +// +// res, err := createTreeFromThread(thread) +// if err != nil { +// 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) +//} +// +//func TestTreeBuilder_UserJoinTestTreeIterate(t *testing.T) { +// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// if err != nil { +// t.Fatal(err) +// } +// +// res, err := createTreeFromThread(thread) +// if err != nil { +// 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) +// 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"}) +//} +// +//func TestTreeBuilder_UserRemoveTestTreeIterate(t *testing.T) { +// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") +// if err != nil { +// t.Fatal(err) +// } +// +// res, err := createTreeFromThread(thread) +// if err != nil { +// 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) +// 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"}) +//} diff --git a/data/treegraph.go b/data/treegraph.go new file mode 100644 index 00000000..60dc9967 --- /dev/null +++ b/data/treegraph.go @@ -0,0 +1,11 @@ +//go:build ((!linux && !darwin) || android || ios || nographviz) && !amd64 +// +build !linux,!darwin android ios nographviz +// +build !amd64 + +package data + +import "fmt" + +func (t *Tree) Graph() (data string, err error) { + return "", fmt.Errorf("not supported") +} diff --git a/data/treegraph_nix.go b/data/treegraph_nix.go new file mode 100644 index 00000000..b06f7435 --- /dev/null +++ b/data/treegraph_nix.go @@ -0,0 +1,149 @@ +//go:build (linux || darwin) && !android && !ios && !nographviz && (amd64 || arm64) +// +build linux darwin +// +build !android +// +build !ios +// +build !nographviz +// +build amd64 arm64 + +package data + +import ( + "bytes" + "fmt" + "github.com/goccy/go-graphviz" + "github.com/goccy/go-graphviz/cgraph" + "strings" + "time" + "unicode" +) + +func (t *Tree) Graph() (data string, err error) { + var order = make(map[string]string) + var seq = 0 + t.Iterate(t.RootId(), func(c *Change) (isContinue bool) { + v := order[c.Id] + if v == "" { + order[c.Id] = fmt.Sprint(seq) + } else { + order[c.Id] = fmt.Sprintf("%s,%d", v, seq) + } + seq++ + return true + }) + g := graphviz.New() + defer g.Close() + graph, err := g.Graph() + if err != nil { + return + } + defer func() { + err = graph.Close() + }() + var nodes = make(map[string]*cgraph.Node) + var addChange = func(c *Change) error { + n, e := graph.CreateNode(c.Id) + if e != nil { + return e + } + if c.Content.GetAclData() != nil { + n.SetStyle(cgraph.FilledNodeStyle) + } else if c.IsSnapshot { + n.SetStyle(cgraph.DashedNodeStyle) + } + nodes[c.Id] = n + ord := order[c.Id] + if ord == "" { + ord = "miss" + } + var chSymbs []string + if c.Content.AclData != nil { + for _, chc := range c.Content.AclData.AclContent { + tp := fmt.Sprintf("%T", chc.Value) + tp = strings.Replace(tp, "ACLChangeACLContentValueValueOf", "", 1) + res := "" + for _, ts := range tp { + if unicode.IsUpper(ts) { + res += string(ts) + } + } + chSymbs = append(chSymbs, res) + } + } + if c.DecryptedDocumentChange != nil { + for _, chc := range c.DecryptedDocumentChange.Content { + tp := fmt.Sprintf("%T", chc.Value) + tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) + res := "" + for _, ts := range tp { + if unicode.IsUpper(ts) { + res += string(ts) + } + } + chSymbs = append(chSymbs, res) + } + } + + shortId := c.Id + label := fmt.Sprintf("Id: %s\nOrd: %s\nTime: %s\nChanges: %s\n", + shortId, + ord, + time.Unix(c.Content.Timestamp, 0).Format("02.01.06 15:04:05"), + strings.Join(chSymbs, ","), + ) + n.SetLabel(label) + return nil + } + for _, c := range t.attached { + if err = addChange(c); err != nil { + return + } + } + for _, c := range t.unAttached { + if err = addChange(c); err != nil { + return + } + } + var getNode = func(id string) (*cgraph.Node, error) { + if n, ok := nodes[id]; ok { + return n, nil + } + n, err := graph.CreateNode(fmt.Sprintf("%s: not in tree", id)) + if err != nil { + return nil, err + } + nodes[id] = n + return n, nil + } + var addLinks = func(c *Change) error { + for _, prevId := range c.PreviousIds { + self, e := getNode(c.Id) + if e != nil { + return e + } + prev, e := getNode(prevId) + if e != nil { + return e + } + _, e = graph.CreateEdge("", self, prev) + if e != nil { + return e + } + } + return nil + } + for _, c := range t.attached { + if err = addLinks(c); err != nil { + return + } + } + for _, c := range t.unAttached { + if err = addLinks(c); err != nil { + return + } + } + var buf bytes.Buffer + if err = g.Render(graph, "dot", &buf); err != nil { + return + } + return buf.String(), nil +} diff --git a/data/treeiterator.go b/data/treeiterator.go new file mode 100644 index 00000000..dd08b6df --- /dev/null +++ b/data/treeiterator.go @@ -0,0 +1,150 @@ +package data + +import "sync" + +var itPool = &sync.Pool{ + New: func() interface{} { + return &iterator{} + }, +} + +func newIterator() *iterator { + return itPool.Get().(*iterator) +} + +func freeIterator(i *iterator) { + itPool.Put(i) +} + +type iterator struct { + compBuf []*Change + queue []*Change + doneMap map[*Change]struct{} + breakpoint *Change + f func(c *Change) bool +} + +func (i *iterator) iterateSkip(start *Change, skipBefore *Change, f func(c *Change) (isContinue bool)) { + skipping := true + i.iterate(start, func(c *Change) (isContinue bool) { + if skipping && c != skipBefore { + return true + } + skipping = false + return f(c) + }) +} + +func (i *iterator) iterate(start *Change, f func(c *Change) (isContinue bool)) { + if start == nil { + return + } + // reset + i.queue = i.queue[:0] + i.compBuf = i.compBuf[:0] + i.doneMap = make(map[*Change]struct{}) + i.queue = append(i.queue, start) + i.breakpoint = nil + i.f = f + + for len(i.queue) > 0 { + c := i.queue[0] + i.queue = i.queue[1:] + nl := len(c.Next) + if nl == 1 { + if !i.iterateLin(c) { + return + } + if i.breakpoint != nil { + i.toQueue(i.breakpoint) + i.breakpoint = nil + } + } else { + _, done := i.doneMap[c] + if !done { + if !f(c) { + return + } + i.doneMap[c] = struct{}{} + } + if nl != 0 { + for _, next := range c.Next { + i.toQueue(next) + } + } + } + } +} + +func (i *iterator) iterateLin(c *Change) bool { + for len(c.Next) == 1 { + _, done := i.doneMap[c] + if !done { + if !i.f(c) { + return false + } + i.doneMap[c] = struct{}{} + } + + c = c.Next[0] + if len(c.PreviousIds) > 1 { + break + } + } + i.breakpoint = c + return true +} + +func (i *iterator) comp(c1, c2 *Change) uint8 { + if c1.Id == c2.Id { + return 0 + } + i.compBuf = i.compBuf[:0] + i.compBuf = append(i.compBuf, c1.Next...) + var uniq = make(map[*Change]struct{}) + var appendUniqueToBuf = func(next []*Change) { + for _, n := range next { + if _, ok := uniq[n]; !ok { + i.compBuf = append(i.compBuf, n) + uniq[n] = struct{}{} + } + } + } + var used int + for len(i.compBuf)-used > 0 { + l := len(i.compBuf) - used + for _, n := range i.compBuf[used:] { + delete(uniq, n) + if n.Id == c2.Id { + return 1 + } else { + appendUniqueToBuf(n.Next) + } + } + used += l + } + return 2 +} + +func (i *iterator) toQueue(c *Change) { + var pos = -1 +For: + for idx, qc := range i.queue { + switch i.comp(c, qc) { + // exists + case 0: + return + // + case 1: + pos = idx + break For + } + } + if pos == -1 { + i.queue = append(i.queue, c) + } else if pos == 0 { + i.queue = append([]*Change{c}, i.queue...) + } else { + i.queue = append(i.queue[:pos], append([]*Change{c}, i.queue[pos:]...)...) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..dd9e3e9e --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/anytypeio/go-anytype-infrastructure-experiments + +go 1.18 + +require ( + github.com/textileio/go-threads v1.0.2-0.20210304072541-d0f91da84404 +) + +replace github.com/textileio/go-threads => github.com/anytypeio/go-threads v1.1.0-rc1.0.20220223104843-a67245cee80e From 6e98744aab6d3d7b26d10db92baab0a619c0dd50 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Wed, 29 Jun 2022 18:30:44 +0200 Subject: [PATCH 02/53] Add makefile and proto generating --- Makefile | 22 + data/pb/aclchanges.pb.go | 4732 +++++++++++++++++++++++++++++++ data/pb/protos/aclchanges.proto | 8 - 3 files changed, 4754 insertions(+), 8 deletions(-) create mode 100644 Makefile create mode 100644 data/pb/aclchanges.pb.go diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f0ac4769 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +export GOPRIVATE=github.com/anytypeio + +ifndef $(GOPATH) + GOPATH=$(shell go env GOPATH) + export GOPATH +endif + +ifndef $(GOROOT) + GOROOT=$(shell go env GOROOT) + export GOROOT +endif + +export PATH=$(GOPATH)/bin:$(shell echo $$PATH) + +protos-go: + @echo 'Generating protobuf packages (Go)...' + $(eval P_TIMESTAMP := Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types) + $(eval P_STRUCT := Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types) + @$(eval P_PROTOS := Mdata/pb/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb) + + $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT),$$(P_PROTOS)) + GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1 protoc --gogofaster_out=$(PKGMAP):./data/pb data/pb/protos/*.*; mv data/pb/data/pb/protos/*.go data/pb; rm -rf data/pb/data diff --git a/data/pb/aclchanges.pb.go b/data/pb/aclchanges.pb.go new file mode 100644 index 00000000..ced412d1 --- /dev/null +++ b/data/pb/aclchanges.pb.go @@ -0,0 +1,4732 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: data/pb/protos/aclchanges.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type ACLChangeUserPermissions int32 + +const ( + ACLChange_Admin ACLChangeUserPermissions = 0 + ACLChange_Writer ACLChangeUserPermissions = 1 + ACLChange_Reader ACLChangeUserPermissions = 2 + ACLChange_Removed ACLChangeUserPermissions = 3 +) + +var ACLChangeUserPermissions_name = map[int32]string{ + 0: "Admin", + 1: "Writer", + 2: "Reader", + 3: "Removed", +} + +var ACLChangeUserPermissions_value = map[string]int32{ + "Admin": 0, + "Writer": 1, + "Reader": 2, + "Removed": 3, +} + +func (x ACLChangeUserPermissions) String() string { + return proto.EnumName(ACLChangeUserPermissions_name, int32(x)) +} + +func (ACLChangeUserPermissions) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 0} +} + +// the element of change tree used to store and internal apply smartBlock history +type ACLChange struct { + TreeHeadIds []string `protobuf:"bytes,1,rep,name=treeHeadIds,proto3" json:"treeHeadIds,omitempty"` + AclHeadIds []string `protobuf:"bytes,2,rep,name=aclHeadIds,proto3" json:"aclHeadIds,omitempty"` + SnapshotBaseId string `protobuf:"bytes,3,opt,name=snapshotBaseId,proto3" json:"snapshotBaseId,omitempty"` + AclData *ACLChangeACLData `protobuf:"bytes,4,opt,name=aclData,proto3" json:"aclData,omitempty"` + // the data is encoded with read key and should be read in ChangesData format + ChangesData []byte `protobuf:"bytes,5,opt,name=changesData,proto3" json:"changesData,omitempty"` + CurrentReadKeyHash uint64 `protobuf:"varint,6,opt,name=currentReadKeyHash,proto3" json:"currentReadKeyHash,omitempty"` + Timestamp int64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Identity string `protobuf:"bytes,8,opt,name=identity,proto3" json:"identity,omitempty"` + LogHeads map[string]string `protobuf:"bytes,9,rep,name=logHeads,proto3" json:"logHeads,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *ACLChange) Reset() { *m = ACLChange{} } +func (m *ACLChange) String() string { return proto.CompactTextString(m) } +func (*ACLChange) ProtoMessage() {} +func (*ACLChange) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0} +} +func (m *ACLChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChange.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChange.Merge(m, src) +} +func (m *ACLChange) XXX_Size() int { + return m.Size() +} +func (m *ACLChange) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChange.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChange proto.InternalMessageInfo + +func (m *ACLChange) GetTreeHeadIds() []string { + if m != nil { + return m.TreeHeadIds + } + return nil +} + +func (m *ACLChange) GetAclHeadIds() []string { + if m != nil { + return m.AclHeadIds + } + return nil +} + +func (m *ACLChange) GetSnapshotBaseId() string { + if m != nil { + return m.SnapshotBaseId + } + return "" +} + +func (m *ACLChange) GetAclData() *ACLChangeACLData { + if m != nil { + return m.AclData + } + return nil +} + +func (m *ACLChange) GetChangesData() []byte { + if m != nil { + return m.ChangesData + } + return nil +} + +func (m *ACLChange) GetCurrentReadKeyHash() uint64 { + if m != nil { + return m.CurrentReadKeyHash + } + return 0 +} + +func (m *ACLChange) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *ACLChange) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChange) GetLogHeads() map[string]string { + if m != nil { + return m.LogHeads + } + return nil +} + +type ACLChangeACLContentValue struct { + // Types that are valid to be assigned to Value: + // *ACLChangeACLContentValueValueOfUserAdd + // *ACLChangeACLContentValueValueOfUserRemove + // *ACLChangeACLContentValueValueOfUserPermissionChange + // *ACLChangeACLContentValueValueOfUserInvite + // *ACLChangeACLContentValueValueOfUserJoin + // *ACLChangeACLContentValueValueOfUserConfirm + Value IsACLChangeACLContentValueValue `protobuf_oneof:"value"` +} + +func (m *ACLChangeACLContentValue) Reset() { *m = ACLChangeACLContentValue{} } +func (m *ACLChangeACLContentValue) String() string { return proto.CompactTextString(m) } +func (*ACLChangeACLContentValue) ProtoMessage() {} +func (*ACLChangeACLContentValue) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 1} +} +func (m *ACLChangeACLContentValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeACLContentValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeACLContentValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeACLContentValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeACLContentValue.Merge(m, src) +} +func (m *ACLChangeACLContentValue) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeACLContentValue) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeACLContentValue.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeACLContentValue proto.InternalMessageInfo + +type IsACLChangeACLContentValueValue interface { + IsACLChangeACLContentValueValue() + MarshalTo([]byte) (int, error) + Size() int +} + +type ACLChangeACLContentValueValueOfUserAdd struct { + UserAdd *ACLChangeUserAdd `protobuf:"bytes,1,opt,name=userAdd,proto3,oneof" json:"userAdd,omitempty"` +} +type ACLChangeACLContentValueValueOfUserRemove struct { + UserRemove *ACLChangeUserRemove `protobuf:"bytes,2,opt,name=userRemove,proto3,oneof" json:"userRemove,omitempty"` +} +type ACLChangeACLContentValueValueOfUserPermissionChange struct { + UserPermissionChange *ACLChangeUserPermissionChange `protobuf:"bytes,3,opt,name=userPermissionChange,proto3,oneof" json:"userPermissionChange,omitempty"` +} +type ACLChangeACLContentValueValueOfUserInvite struct { + UserInvite *ACLChangeUserInvite `protobuf:"bytes,4,opt,name=userInvite,proto3,oneof" json:"userInvite,omitempty"` +} +type ACLChangeACLContentValueValueOfUserJoin struct { + UserJoin *ACLChangeUserJoin `protobuf:"bytes,5,opt,name=userJoin,proto3,oneof" json:"userJoin,omitempty"` +} +type ACLChangeACLContentValueValueOfUserConfirm struct { + UserConfirm *ACLChangeUserConfirm `protobuf:"bytes,6,opt,name=userConfirm,proto3,oneof" json:"userConfirm,omitempty"` +} + +func (*ACLChangeACLContentValueValueOfUserAdd) IsACLChangeACLContentValueValue() {} +func (*ACLChangeACLContentValueValueOfUserRemove) IsACLChangeACLContentValueValue() {} +func (*ACLChangeACLContentValueValueOfUserPermissionChange) IsACLChangeACLContentValueValue() {} +func (*ACLChangeACLContentValueValueOfUserInvite) IsACLChangeACLContentValueValue() {} +func (*ACLChangeACLContentValueValueOfUserJoin) IsACLChangeACLContentValueValue() {} +func (*ACLChangeACLContentValueValueOfUserConfirm) IsACLChangeACLContentValueValue() {} + +func (m *ACLChangeACLContentValue) GetValue() IsACLChangeACLContentValueValue { + if m != nil { + return m.Value + } + return nil +} + +func (m *ACLChangeACLContentValue) GetUserAdd() *ACLChangeUserAdd { + if x, ok := m.GetValue().(*ACLChangeACLContentValueValueOfUserAdd); ok { + return x.UserAdd + } + return nil +} + +func (m *ACLChangeACLContentValue) GetUserRemove() *ACLChangeUserRemove { + if x, ok := m.GetValue().(*ACLChangeACLContentValueValueOfUserRemove); ok { + return x.UserRemove + } + return nil +} + +func (m *ACLChangeACLContentValue) GetUserPermissionChange() *ACLChangeUserPermissionChange { + if x, ok := m.GetValue().(*ACLChangeACLContentValueValueOfUserPermissionChange); ok { + return x.UserPermissionChange + } + return nil +} + +func (m *ACLChangeACLContentValue) GetUserInvite() *ACLChangeUserInvite { + if x, ok := m.GetValue().(*ACLChangeACLContentValueValueOfUserInvite); ok { + return x.UserInvite + } + return nil +} + +func (m *ACLChangeACLContentValue) GetUserJoin() *ACLChangeUserJoin { + if x, ok := m.GetValue().(*ACLChangeACLContentValueValueOfUserJoin); ok { + return x.UserJoin + } + return nil +} + +func (m *ACLChangeACLContentValue) GetUserConfirm() *ACLChangeUserConfirm { + if x, ok := m.GetValue().(*ACLChangeACLContentValueValueOfUserConfirm); ok { + return x.UserConfirm + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ACLChangeACLContentValue) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ACLChangeACLContentValueValueOfUserAdd)(nil), + (*ACLChangeACLContentValueValueOfUserRemove)(nil), + (*ACLChangeACLContentValueValueOfUserPermissionChange)(nil), + (*ACLChangeACLContentValueValueOfUserInvite)(nil), + (*ACLChangeACLContentValueValueOfUserJoin)(nil), + (*ACLChangeACLContentValueValueOfUserConfirm)(nil), + } +} + +type ACLChangeACLData struct { + AclSnapshot *ACLChangeACLSnapshot `protobuf:"bytes,1,opt,name=aclSnapshot,proto3" json:"aclSnapshot,omitempty"` + AclContent []*ACLChangeACLContentValue `protobuf:"bytes,2,rep,name=aclContent,proto3" json:"aclContent,omitempty"` +} + +func (m *ACLChangeACLData) Reset() { *m = ACLChangeACLData{} } +func (m *ACLChangeACLData) String() string { return proto.CompactTextString(m) } +func (*ACLChangeACLData) ProtoMessage() {} +func (*ACLChangeACLData) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 2} +} +func (m *ACLChangeACLData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeACLData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeACLData.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeACLData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeACLData.Merge(m, src) +} +func (m *ACLChangeACLData) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeACLData) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeACLData.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeACLData proto.InternalMessageInfo + +func (m *ACLChangeACLData) GetAclSnapshot() *ACLChangeACLSnapshot { + if m != nil { + return m.AclSnapshot + } + return nil +} + +func (m *ACLChangeACLData) GetAclContent() []*ACLChangeACLContentValue { + if m != nil { + return m.AclContent + } + return nil +} + +type ACLChangeACLSnapshot struct { + // We don't need ACLState as a separate message now, because we simplified the snapshot model + AclState *ACLChangeACLState `protobuf:"bytes,1,opt,name=aclState,proto3" json:"aclState,omitempty"` +} + +func (m *ACLChangeACLSnapshot) Reset() { *m = ACLChangeACLSnapshot{} } +func (m *ACLChangeACLSnapshot) String() string { return proto.CompactTextString(m) } +func (*ACLChangeACLSnapshot) ProtoMessage() {} +func (*ACLChangeACLSnapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 3} +} +func (m *ACLChangeACLSnapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeACLSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeACLSnapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeACLSnapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeACLSnapshot.Merge(m, src) +} +func (m *ACLChangeACLSnapshot) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeACLSnapshot) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeACLSnapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeACLSnapshot proto.InternalMessageInfo + +func (m *ACLChangeACLSnapshot) GetAclState() *ACLChangeACLState { + if m != nil { + return m.AclState + } + return nil +} + +type ACLChangeACLState struct { + ReadKeyHashes []uint64 `protobuf:"varint,1,rep,packed,name=readKeyHashes,proto3" json:"readKeyHashes,omitempty"` + UserStates []*ACLChangeUserState `protobuf:"bytes,2,rep,name=userStates,proto3" json:"userStates,omitempty"` + Invites map[string]*ACLChangeUserInvite `protobuf:"bytes,3,rep,name=invites,proto3" json:"invites,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *ACLChangeACLState) Reset() { *m = ACLChangeACLState{} } +func (m *ACLChangeACLState) String() string { return proto.CompactTextString(m) } +func (*ACLChangeACLState) ProtoMessage() {} +func (*ACLChangeACLState) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 4} +} +func (m *ACLChangeACLState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeACLState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeACLState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeACLState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeACLState.Merge(m, src) +} +func (m *ACLChangeACLState) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeACLState) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeACLState.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeACLState proto.InternalMessageInfo + +func (m *ACLChangeACLState) GetReadKeyHashes() []uint64 { + if m != nil { + return m.ReadKeyHashes + } + return nil +} + +func (m *ACLChangeACLState) GetUserStates() []*ACLChangeUserState { + if m != nil { + return m.UserStates + } + return nil +} + +func (m *ACLChangeACLState) GetInvites() map[string]*ACLChangeUserInvite { + if m != nil { + return m.Invites + } + return nil +} + +type ACLChangeUserState struct { + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + EncryptionKey []byte `protobuf:"bytes,2,opt,name=encryptionKey,proto3" json:"encryptionKey,omitempty"` + EncryptedReadKeys [][]byte `protobuf:"bytes,3,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` + Permissions ACLChangeUserPermissions `protobuf:"varint,4,opt,name=permissions,proto3,enum=anytype.ACLChangeUserPermissions" json:"permissions,omitempty"` + IsConfirmed bool `protobuf:"varint,5,opt,name=IsConfirmed,proto3" json:"IsConfirmed,omitempty"` +} + +func (m *ACLChangeUserState) Reset() { *m = ACLChangeUserState{} } +func (m *ACLChangeUserState) String() string { return proto.CompactTextString(m) } +func (*ACLChangeUserState) ProtoMessage() {} +func (*ACLChangeUserState) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 5} +} +func (m *ACLChangeUserState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeUserState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeUserState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeUserState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeUserState.Merge(m, src) +} +func (m *ACLChangeUserState) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeUserState) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeUserState.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeUserState proto.InternalMessageInfo + +func (m *ACLChangeUserState) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChangeUserState) GetEncryptionKey() []byte { + if m != nil { + return m.EncryptionKey + } + return nil +} + +func (m *ACLChangeUserState) GetEncryptedReadKeys() [][]byte { + if m != nil { + return m.EncryptedReadKeys + } + return nil +} + +func (m *ACLChangeUserState) GetPermissions() ACLChangeUserPermissions { + if m != nil { + return m.Permissions + } + return ACLChange_Admin +} + +func (m *ACLChangeUserState) GetIsConfirmed() bool { + if m != nil { + return m.IsConfirmed + } + return false +} + +// we already know identity and encryptionKey +type ACLChangeUserAdd struct { + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + EncryptionKey []byte `protobuf:"bytes,2,opt,name=encryptionKey,proto3" json:"encryptionKey,omitempty"` + EncryptedReadKeys [][]byte `protobuf:"bytes,3,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` + Permissions ACLChangeUserPermissions `protobuf:"varint,4,opt,name=permissions,proto3,enum=anytype.ACLChangeUserPermissions" json:"permissions,omitempty"` +} + +func (m *ACLChangeUserAdd) Reset() { *m = ACLChangeUserAdd{} } +func (m *ACLChangeUserAdd) String() string { return proto.CompactTextString(m) } +func (*ACLChangeUserAdd) ProtoMessage() {} +func (*ACLChangeUserAdd) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 6} +} +func (m *ACLChangeUserAdd) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeUserAdd) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeUserAdd.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeUserAdd) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeUserAdd.Merge(m, src) +} +func (m *ACLChangeUserAdd) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeUserAdd) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeUserAdd.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeUserAdd proto.InternalMessageInfo + +func (m *ACLChangeUserAdd) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChangeUserAdd) GetEncryptionKey() []byte { + if m != nil { + return m.EncryptionKey + } + return nil +} + +func (m *ACLChangeUserAdd) GetEncryptedReadKeys() [][]byte { + if m != nil { + return m.EncryptedReadKeys + } + return nil +} + +func (m *ACLChangeUserAdd) GetPermissions() ACLChangeUserPermissions { + if m != nil { + return m.Permissions + } + return ACLChange_Admin +} + +// TODO: this is not used as of now +type ACLChangeUserConfirm struct { + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + UserAddId string `protobuf:"bytes,2,opt,name=userAddId,proto3" json:"userAddId,omitempty"` +} + +func (m *ACLChangeUserConfirm) Reset() { *m = ACLChangeUserConfirm{} } +func (m *ACLChangeUserConfirm) String() string { return proto.CompactTextString(m) } +func (*ACLChangeUserConfirm) ProtoMessage() {} +func (*ACLChangeUserConfirm) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 7} +} +func (m *ACLChangeUserConfirm) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeUserConfirm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeUserConfirm.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeUserConfirm) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeUserConfirm.Merge(m, src) +} +func (m *ACLChangeUserConfirm) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeUserConfirm) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeUserConfirm.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeUserConfirm proto.InternalMessageInfo + +func (m *ACLChangeUserConfirm) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChangeUserConfirm) GetUserAddId() string { + if m != nil { + return m.UserAddId + } + return "" +} + +type ACLChangeUserInvite struct { + AcceptPublicKey []byte `protobuf:"bytes,1,opt,name=acceptPublicKey,proto3" json:"acceptPublicKey,omitempty"` + EncryptPublicKey []byte `protobuf:"bytes,2,opt,name=encryptPublicKey,proto3" json:"encryptPublicKey,omitempty"` + EncryptedReadKeys [][]byte `protobuf:"bytes,3,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` + Permissions ACLChangeUserPermissions `protobuf:"varint,4,opt,name=permissions,proto3,enum=anytype.ACLChangeUserPermissions" json:"permissions,omitempty"` +} + +func (m *ACLChangeUserInvite) Reset() { *m = ACLChangeUserInvite{} } +func (m *ACLChangeUserInvite) String() string { return proto.CompactTextString(m) } +func (*ACLChangeUserInvite) ProtoMessage() {} +func (*ACLChangeUserInvite) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 8} +} +func (m *ACLChangeUserInvite) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeUserInvite) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeUserInvite.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeUserInvite) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeUserInvite.Merge(m, src) +} +func (m *ACLChangeUserInvite) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeUserInvite) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeUserInvite.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeUserInvite proto.InternalMessageInfo + +func (m *ACLChangeUserInvite) GetAcceptPublicKey() []byte { + if m != nil { + return m.AcceptPublicKey + } + return nil +} + +func (m *ACLChangeUserInvite) GetEncryptPublicKey() []byte { + if m != nil { + return m.EncryptPublicKey + } + return nil +} + +func (m *ACLChangeUserInvite) GetEncryptedReadKeys() [][]byte { + if m != nil { + return m.EncryptedReadKeys + } + return nil +} + +func (m *ACLChangeUserInvite) GetPermissions() ACLChangeUserPermissions { + if m != nil { + return m.Permissions + } + return ACLChange_Admin +} + +type ACLChangeUserJoin struct { + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + EncryptionKey []byte `protobuf:"bytes,2,opt,name=encryptionKey,proto3" json:"encryptionKey,omitempty"` + AcceptSignature []byte `protobuf:"bytes,3,opt,name=acceptSignature,proto3" json:"acceptSignature,omitempty"` + UserInviteChangeId string `protobuf:"bytes,4,opt,name=userInviteChangeId,proto3" json:"userInviteChangeId,omitempty"` + EncryptedReadKeys [][]byte `protobuf:"bytes,5,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` +} + +func (m *ACLChangeUserJoin) Reset() { *m = ACLChangeUserJoin{} } +func (m *ACLChangeUserJoin) String() string { return proto.CompactTextString(m) } +func (*ACLChangeUserJoin) ProtoMessage() {} +func (*ACLChangeUserJoin) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 9} +} +func (m *ACLChangeUserJoin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeUserJoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeUserJoin.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeUserJoin) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeUserJoin.Merge(m, src) +} +func (m *ACLChangeUserJoin) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeUserJoin) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeUserJoin.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeUserJoin proto.InternalMessageInfo + +func (m *ACLChangeUserJoin) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChangeUserJoin) GetEncryptionKey() []byte { + if m != nil { + return m.EncryptionKey + } + return nil +} + +func (m *ACLChangeUserJoin) GetAcceptSignature() []byte { + if m != nil { + return m.AcceptSignature + } + return nil +} + +func (m *ACLChangeUserJoin) GetUserInviteChangeId() string { + if m != nil { + return m.UserInviteChangeId + } + return "" +} + +func (m *ACLChangeUserJoin) GetEncryptedReadKeys() [][]byte { + if m != nil { + return m.EncryptedReadKeys + } + return nil +} + +type ACLChangeUserRemove struct { + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + ReadKeyReplaces []*ACLChangeReadKeyReplace `protobuf:"bytes,3,rep,name=readKeyReplaces,proto3" json:"readKeyReplaces,omitempty"` +} + +func (m *ACLChangeUserRemove) Reset() { *m = ACLChangeUserRemove{} } +func (m *ACLChangeUserRemove) String() string { return proto.CompactTextString(m) } +func (*ACLChangeUserRemove) ProtoMessage() {} +func (*ACLChangeUserRemove) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 10} +} +func (m *ACLChangeUserRemove) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeUserRemove) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeUserRemove.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeUserRemove) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeUserRemove.Merge(m, src) +} +func (m *ACLChangeUserRemove) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeUserRemove) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeUserRemove.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeUserRemove proto.InternalMessageInfo + +func (m *ACLChangeUserRemove) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChangeUserRemove) GetReadKeyReplaces() []*ACLChangeReadKeyReplace { + if m != nil { + return m.ReadKeyReplaces + } + return nil +} + +type ACLChangeReadKeyReplace struct { + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + EncryptionKey []byte `protobuf:"bytes,2,opt,name=encryptionKey,proto3" json:"encryptionKey,omitempty"` + EncryptedReadKey []byte `protobuf:"bytes,3,opt,name=encryptedReadKey,proto3" json:"encryptedReadKey,omitempty"` +} + +func (m *ACLChangeReadKeyReplace) Reset() { *m = ACLChangeReadKeyReplace{} } +func (m *ACLChangeReadKeyReplace) String() string { return proto.CompactTextString(m) } +func (*ACLChangeReadKeyReplace) ProtoMessage() {} +func (*ACLChangeReadKeyReplace) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 11} +} +func (m *ACLChangeReadKeyReplace) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeReadKeyReplace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeReadKeyReplace.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeReadKeyReplace) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeReadKeyReplace.Merge(m, src) +} +func (m *ACLChangeReadKeyReplace) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeReadKeyReplace) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeReadKeyReplace.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeReadKeyReplace proto.InternalMessageInfo + +func (m *ACLChangeReadKeyReplace) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChangeReadKeyReplace) GetEncryptionKey() []byte { + if m != nil { + return m.EncryptionKey + } + return nil +} + +func (m *ACLChangeReadKeyReplace) GetEncryptedReadKey() []byte { + if m != nil { + return m.EncryptedReadKey + } + return nil +} + +type ACLChangeUserPermissionChange struct { + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + Permissions ACLChangeUserPermissions `protobuf:"varint,2,opt,name=permissions,proto3,enum=anytype.ACLChangeUserPermissions" json:"permissions,omitempty"` +} + +func (m *ACLChangeUserPermissionChange) Reset() { *m = ACLChangeUserPermissionChange{} } +func (m *ACLChangeUserPermissionChange) String() string { return proto.CompactTextString(m) } +func (*ACLChangeUserPermissionChange) ProtoMessage() {} +func (*ACLChangeUserPermissionChange) Descriptor() ([]byte, []int) { + return fileDescriptor_5a15109171a9dc49, []int{0, 12} +} +func (m *ACLChangeUserPermissionChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ACLChangeUserPermissionChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ACLChangeUserPermissionChange.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ACLChangeUserPermissionChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_ACLChangeUserPermissionChange.Merge(m, src) +} +func (m *ACLChangeUserPermissionChange) XXX_Size() int { + return m.Size() +} +func (m *ACLChangeUserPermissionChange) XXX_DiscardUnknown() { + xxx_messageInfo_ACLChangeUserPermissionChange.DiscardUnknown(m) +} + +var xxx_messageInfo_ACLChangeUserPermissionChange proto.InternalMessageInfo + +func (m *ACLChangeUserPermissionChange) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *ACLChangeUserPermissionChange) GetPermissions() ACLChangeUserPermissions { + if m != nil { + return m.Permissions + } + return ACLChange_Admin +} + +func init() { + proto.RegisterEnum("anytype.ACLChangeUserPermissions", ACLChangeUserPermissions_name, ACLChangeUserPermissions_value) + proto.RegisterType((*ACLChange)(nil), "anytype.ACLChange") + proto.RegisterMapType((map[string]string)(nil), "anytype.ACLChange.LogHeadsEntry") + proto.RegisterType((*ACLChangeACLContentValue)(nil), "anytype.ACLChange.ACLContentValue") + proto.RegisterType((*ACLChangeACLData)(nil), "anytype.ACLChange.ACLData") + proto.RegisterType((*ACLChangeACLSnapshot)(nil), "anytype.ACLChange.ACLSnapshot") + proto.RegisterType((*ACLChangeACLState)(nil), "anytype.ACLChange.ACLState") + proto.RegisterMapType((map[string]*ACLChangeUserInvite)(nil), "anytype.ACLChange.ACLState.InvitesEntry") + proto.RegisterType((*ACLChangeUserState)(nil), "anytype.ACLChange.UserState") + proto.RegisterType((*ACLChangeUserAdd)(nil), "anytype.ACLChange.UserAdd") + proto.RegisterType((*ACLChangeUserConfirm)(nil), "anytype.ACLChange.UserConfirm") + proto.RegisterType((*ACLChangeUserInvite)(nil), "anytype.ACLChange.UserInvite") + proto.RegisterType((*ACLChangeUserJoin)(nil), "anytype.ACLChange.UserJoin") + proto.RegisterType((*ACLChangeUserRemove)(nil), "anytype.ACLChange.UserRemove") + proto.RegisterType((*ACLChangeReadKeyReplace)(nil), "anytype.ACLChange.ReadKeyReplace") + proto.RegisterType((*ACLChangeUserPermissionChange)(nil), "anytype.ACLChange.UserPermissionChange") +} + +func init() { proto.RegisterFile("data/pb/protos/aclchanges.proto", fileDescriptor_5a15109171a9dc49) } + +var fileDescriptor_5a15109171a9dc49 = []byte{ + // 938 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0xdd, 0x6e, 0xe3, 0x44, + 0x14, 0xce, 0x24, 0x6d, 0x1d, 0x1f, 0xa7, 0x6d, 0x18, 0x15, 0xc9, 0xf2, 0x16, 0x63, 0xaa, 0x15, + 0x58, 0x08, 0xa5, 0x52, 0x16, 0xf1, 0xbb, 0x12, 0x34, 0x5d, 0x20, 0xa1, 0x7b, 0xb1, 0x9a, 0x15, + 0x20, 0x90, 0xb8, 0x98, 0xda, 0x43, 0x6b, 0x91, 0xd8, 0x96, 0x67, 0x52, 0x91, 0x1b, 0xde, 0x00, + 0xc1, 0x53, 0xf0, 0x00, 0xbc, 0x00, 0xb7, 0x5c, 0xee, 0x05, 0x42, 0x5c, 0xa2, 0xf6, 0x45, 0xd0, + 0xfc, 0xd8, 0x71, 0x52, 0xa7, 0x48, 0x68, 0x25, 0xc4, 0xdd, 0xcc, 0x37, 0xdf, 0x99, 0x39, 0x3f, + 0xdf, 0x39, 0x36, 0xbc, 0x1c, 0x53, 0x41, 0x8f, 0xf3, 0xf3, 0xe3, 0xbc, 0xc8, 0x44, 0xc6, 0x8f, + 0x69, 0x34, 0x8d, 0x2e, 0x69, 0x7a, 0xc1, 0xf8, 0x40, 0x21, 0xd8, 0xa2, 0xe9, 0x42, 0x2c, 0x72, + 0x76, 0xf4, 0xf3, 0x8b, 0x60, 0x9f, 0x9c, 0x3e, 0x3e, 0x55, 0xa7, 0x38, 0x00, 0x47, 0x14, 0x8c, + 0x8d, 0x19, 0x8d, 0x27, 0x31, 0x77, 0x51, 0xd0, 0x09, 0x6d, 0x52, 0x87, 0xb0, 0x0f, 0x40, 0xa3, + 0x69, 0x49, 0x68, 0x2b, 0x42, 0x0d, 0xc1, 0xaf, 0xc2, 0x1e, 0x4f, 0x69, 0xce, 0x2f, 0x33, 0x31, + 0xa2, 0x9c, 0x4d, 0x62, 0xb7, 0x13, 0xa0, 0xd0, 0x26, 0x6b, 0x28, 0x7e, 0x13, 0x2c, 0x1a, 0x4d, + 0x1f, 0x51, 0x41, 0xdd, 0xad, 0x00, 0x85, 0xce, 0xd0, 0x1b, 0x18, 0x97, 0x06, 0x95, 0x3b, 0x72, + 0x25, 0x19, 0xa4, 0xa4, 0x4a, 0xff, 0x4c, 0x1c, 0xca, 0x72, 0x3b, 0x40, 0x61, 0x8f, 0xd4, 0x21, + 0x3c, 0x00, 0x1c, 0xcd, 0x8b, 0x82, 0xa5, 0x82, 0x30, 0x1a, 0x9f, 0xb1, 0xc5, 0x98, 0xf2, 0x4b, + 0x77, 0x27, 0x40, 0xe1, 0x16, 0x69, 0x38, 0xc1, 0x87, 0x60, 0x8b, 0x64, 0xc6, 0xb8, 0xa0, 0xb3, + 0xdc, 0xb5, 0x02, 0x14, 0x76, 0xc8, 0x12, 0xc0, 0x1e, 0x74, 0x93, 0x98, 0xa5, 0x22, 0x11, 0x0b, + 0xb7, 0xab, 0xe2, 0xa8, 0xf6, 0xf8, 0x21, 0x74, 0xa7, 0xd9, 0x85, 0x8c, 0x9b, 0xbb, 0x76, 0xd0, + 0x09, 0x9d, 0x61, 0xd0, 0x10, 0xc2, 0x63, 0x43, 0xf9, 0x28, 0x15, 0xc5, 0x82, 0x54, 0x16, 0xde, + 0xfb, 0xb0, 0xbb, 0x72, 0x84, 0xfb, 0xd0, 0xf9, 0x96, 0x2d, 0x5c, 0xa4, 0x5e, 0x91, 0x4b, 0x7c, + 0x00, 0xdb, 0x57, 0x74, 0x3a, 0x67, 0x6e, 0x5b, 0x61, 0x7a, 0xf3, 0x5e, 0xfb, 0x1d, 0xe4, 0xfd, + 0xd2, 0x81, 0x7d, 0xf9, 0x44, 0x96, 0x0a, 0x96, 0x8a, 0xcf, 0x25, 0x8e, 0xdf, 0x02, 0x6b, 0xce, + 0x59, 0x71, 0x12, 0xc7, 0xea, 0x8e, 0xe6, 0x84, 0x7e, 0xa6, 0x19, 0xe3, 0x16, 0x29, 0xc9, 0xf8, + 0x03, 0x00, 0xb9, 0x24, 0x6c, 0x96, 0x5d, 0xe9, 0xa7, 0x9c, 0xe1, 0x4b, 0x1b, 0x4c, 0x35, 0x69, + 0xdc, 0x22, 0x35, 0x13, 0xfc, 0x35, 0x1c, 0xc8, 0xdd, 0x13, 0x56, 0xcc, 0x12, 0xce, 0x93, 0x2c, + 0xd5, 0x06, 0xaa, 0xee, 0xce, 0xf0, 0xb5, 0x0d, 0x57, 0xad, 0xd3, 0xc7, 0x2d, 0xd2, 0x78, 0x4d, + 0xe9, 0xdf, 0x24, 0xbd, 0x4a, 0x04, 0x33, 0x5a, 0xd9, 0xe4, 0x9f, 0x26, 0x95, 0xfe, 0xe9, 0x1d, + 0x7e, 0x17, 0xba, 0x72, 0xf7, 0x69, 0x96, 0xa4, 0x4a, 0x30, 0xce, 0xf0, 0xde, 0x06, 0x73, 0x49, + 0x19, 0xb7, 0x48, 0x45, 0xc7, 0x23, 0x70, 0xe4, 0xfa, 0x34, 0x4b, 0xbf, 0x49, 0x8a, 0x99, 0x52, + 0x91, 0x33, 0xf4, 0x37, 0x58, 0x1b, 0xd6, 0xb8, 0x45, 0xea, 0x46, 0x23, 0xcb, 0x54, 0xd1, 0xfb, + 0x11, 0x81, 0x65, 0x04, 0x8d, 0x3f, 0x04, 0x87, 0x46, 0xd3, 0xa7, 0xa6, 0x25, 0x4c, 0xc1, 0xfc, + 0xe6, 0x0e, 0x28, 0x59, 0xa4, 0x6e, 0x82, 0x47, 0xaa, 0x0f, 0x8d, 0x02, 0x54, 0x1f, 0x3a, 0xc3, + 0xa3, 0xe6, 0x0b, 0xea, 0x32, 0x21, 0x35, 0x2b, 0xef, 0x63, 0x70, 0x6a, 0xf7, 0xe3, 0xb7, 0xa1, + 0x2b, 0x5f, 0x10, 0x54, 0x30, 0xe3, 0xd1, 0xbd, 0x0d, 0x1e, 0x49, 0x0a, 0xa9, 0xc8, 0xde, 0x0f, + 0x6d, 0xe8, 0x96, 0x30, 0xbe, 0x0f, 0xbb, 0xc5, 0xb2, 0xbf, 0x98, 0x1e, 0x22, 0x5b, 0x64, 0x15, + 0xc4, 0x0f, 0x75, 0x55, 0x95, 0x09, 0x37, 0xee, 0x1f, 0x6e, 0x48, 0xac, 0x7e, 0xae, 0xc6, 0xc7, + 0x23, 0xb0, 0x12, 0x55, 0x5c, 0xee, 0x76, 0x94, 0x69, 0x78, 0x87, 0xa3, 0x03, 0xad, 0x03, 0xd3, + 0x81, 0xa5, 0xa1, 0xf7, 0x25, 0xf4, 0xea, 0x07, 0x0d, 0xfd, 0xf7, 0xa0, 0xde, 0x7f, 0xff, 0x24, + 0xba, 0x7a, 0x7b, 0xde, 0x20, 0xb0, 0x2b, 0xc7, 0x57, 0x66, 0x08, 0x5a, 0x9b, 0x21, 0xf7, 0x61, + 0x97, 0xa5, 0x51, 0xb1, 0xc8, 0x45, 0x92, 0xa5, 0x67, 0x6c, 0xa1, 0x9e, 0xea, 0x91, 0x55, 0x10, + 0xbf, 0x01, 0x2f, 0x18, 0x80, 0xc5, 0x66, 0x76, 0xe9, 0xc0, 0x7b, 0xe4, 0xf6, 0x01, 0x7e, 0x04, + 0x4e, 0x5e, 0x35, 0x11, 0x57, 0x1d, 0xb3, 0xd7, 0x28, 0x8d, 0xd5, 0x36, 0xe4, 0xa4, 0x6e, 0x26, + 0x27, 0xed, 0x84, 0x1b, 0x0d, 0xb3, 0x58, 0x35, 0x4e, 0x97, 0xd4, 0x21, 0xef, 0x57, 0x04, 0x96, + 0x99, 0x27, 0xff, 0xcf, 0x18, 0xbd, 0x4f, 0xc0, 0xa9, 0x35, 0xee, 0x9d, 0x41, 0x1c, 0x82, 0x6d, + 0x06, 0xe6, 0x24, 0x36, 0xf3, 0x78, 0x09, 0x78, 0x7f, 0x20, 0x80, 0xa5, 0x14, 0x70, 0x08, 0xfb, + 0x34, 0x8a, 0x58, 0x2e, 0x9e, 0xcc, 0xcf, 0xa7, 0x49, 0x74, 0x66, 0x64, 0xd5, 0x23, 0xeb, 0x30, + 0x7e, 0x1d, 0xfa, 0x26, 0xb8, 0x25, 0x55, 0xa7, 0xe7, 0x16, 0xfe, 0x9f, 0x64, 0xe8, 0x77, 0x04, + 0xdd, 0x72, 0x32, 0x3e, 0x87, 0x22, 0x57, 0x89, 0x79, 0x9a, 0x5c, 0xa4, 0x54, 0xcc, 0x0b, 0xfd, + 0x95, 0xa8, 0x12, 0x53, 0xc1, 0xf2, 0x33, 0xbe, 0x1c, 0xe1, 0xda, 0xd3, 0x49, 0xac, 0xa2, 0xb0, + 0x49, 0xc3, 0x49, 0x73, 0x72, 0xb6, 0x37, 0x24, 0xc7, 0x9b, 0xeb, 0x72, 0x99, 0x0f, 0xd8, 0x5d, + 0x71, 0x9d, 0xc1, 0xbe, 0x19, 0x5c, 0x84, 0xe5, 0x53, 0x1a, 0x55, 0x13, 0xe7, 0x95, 0x86, 0x54, + 0x92, 0x15, 0x26, 0x59, 0xb7, 0xf4, 0xbe, 0x87, 0xbd, 0x55, 0xca, 0x73, 0x48, 0xe9, 0x52, 0x41, + 0x55, 0x7c, 0x26, 0xa7, 0xb7, 0x70, 0xef, 0x3b, 0x38, 0x68, 0xfa, 0xf4, 0xde, 0xe9, 0xc5, 0x9a, + 0x8e, 0xda, 0xff, 0x4a, 0x47, 0x47, 0x27, 0xb0, 0xbf, 0x76, 0x8e, 0x6d, 0xd8, 0x3e, 0x89, 0x67, + 0x49, 0xda, 0x6f, 0x61, 0x80, 0x9d, 0x2f, 0x8a, 0x44, 0xb0, 0xa2, 0x8f, 0xe4, 0x5a, 0xba, 0xcb, + 0x8a, 0x7e, 0x1b, 0x3b, 0x60, 0xe9, 0x12, 0xc5, 0xfd, 0xce, 0xe8, 0xf0, 0xb7, 0x6b, 0x1f, 0x3d, + 0xbb, 0xf6, 0xd1, 0x5f, 0xd7, 0x3e, 0xfa, 0xe9, 0xc6, 0x6f, 0x3d, 0xbb, 0xf1, 0x5b, 0x7f, 0xde, + 0xf8, 0xad, 0xaf, 0xda, 0xf9, 0xf9, 0xf9, 0x8e, 0xfa, 0xad, 0x7d, 0xf0, 0x77, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x14, 0x6d, 0x36, 0xcd, 0xf9, 0x0a, 0x00, 0x00, +} + +func (m *ACLChange) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.LogHeads) > 0 { + for k := range m.LogHeads { + v := m.LogHeads[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintAclchanges(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintAclchanges(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintAclchanges(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x4a + } + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0x42 + } + if m.Timestamp != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x38 + } + if m.CurrentReadKeyHash != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.CurrentReadKeyHash)) + i-- + dAtA[i] = 0x30 + } + if len(m.ChangesData) > 0 { + i -= len(m.ChangesData) + copy(dAtA[i:], m.ChangesData) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.ChangesData))) + i-- + dAtA[i] = 0x2a + } + if m.AclData != nil { + { + size, err := m.AclData.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.SnapshotBaseId) > 0 { + i -= len(m.SnapshotBaseId) + copy(dAtA[i:], m.SnapshotBaseId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.SnapshotBaseId))) + i-- + dAtA[i] = 0x1a + } + if len(m.AclHeadIds) > 0 { + for iNdEx := len(m.AclHeadIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AclHeadIds[iNdEx]) + copy(dAtA[i:], m.AclHeadIds[iNdEx]) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.AclHeadIds[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.TreeHeadIds) > 0 { + for iNdEx := len(m.TreeHeadIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TreeHeadIds[iNdEx]) + copy(dAtA[i:], m.TreeHeadIds[iNdEx]) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.TreeHeadIds[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeACLContentValue) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeACLContentValue) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLContentValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Value != nil { + { + size := m.Value.Size() + i -= size + if _, err := m.Value.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeACLContentValueValueOfUserAdd) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLContentValueValueOfUserAdd) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UserAdd != nil { + { + size, err := m.UserAdd.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *ACLChangeACLContentValueValueOfUserRemove) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLContentValueValueOfUserRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UserRemove != nil { + { + size, err := m.UserRemove.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *ACLChangeACLContentValueValueOfUserPermissionChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLContentValueValueOfUserPermissionChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UserPermissionChange != nil { + { + size, err := m.UserPermissionChange.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} +func (m *ACLChangeACLContentValueValueOfUserInvite) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLContentValueValueOfUserInvite) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UserInvite != nil { + { + size, err := m.UserInvite.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + return len(dAtA) - i, nil +} +func (m *ACLChangeACLContentValueValueOfUserJoin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLContentValueValueOfUserJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UserJoin != nil { + { + size, err := m.UserJoin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + return len(dAtA) - i, nil +} +func (m *ACLChangeACLContentValueValueOfUserConfirm) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLContentValueValueOfUserConfirm) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.UserConfirm != nil { + { + size, err := m.UserConfirm.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + return len(dAtA) - i, nil +} +func (m *ACLChangeACLData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeACLData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AclContent) > 0 { + for iNdEx := len(m.AclContent) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AclContent[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.AclSnapshot != nil { + { + size, err := m.AclSnapshot.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeACLSnapshot) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeACLSnapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AclState != nil { + { + size, err := m.AclState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeACLState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeACLState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeACLState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Invites) > 0 { + for k := range m.Invites { + v := m.Invites[k] + baseI := i + if v != nil { + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintAclchanges(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintAclchanges(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.UserStates) > 0 { + for iNdEx := len(m.UserStates) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.UserStates[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ReadKeyHashes) > 0 { + dAtA12 := make([]byte, len(m.ReadKeyHashes)*10) + var j11 int + for _, num := range m.ReadKeyHashes { + for num >= 1<<7 { + dAtA12[j11] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j11++ + } + dAtA12[j11] = uint8(num) + j11++ + } + i -= j11 + copy(dAtA[i:], dAtA12[:j11]) + i = encodeVarintAclchanges(dAtA, i, uint64(j11)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeUserState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeUserState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeUserState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsConfirmed { + i-- + if m.IsConfirmed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Permissions != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.Permissions)) + i-- + dAtA[i] = 0x20 + } + if len(m.EncryptedReadKeys) > 0 { + for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EncryptedReadKeys[iNdEx]) + copy(dAtA[i:], m.EncryptedReadKeys[iNdEx]) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptedReadKeys[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.EncryptionKey) > 0 { + i -= len(m.EncryptionKey) + copy(dAtA[i:], m.EncryptionKey) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptionKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeUserAdd) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeUserAdd) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeUserAdd) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Permissions != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.Permissions)) + i-- + dAtA[i] = 0x20 + } + if len(m.EncryptedReadKeys) > 0 { + for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EncryptedReadKeys[iNdEx]) + copy(dAtA[i:], m.EncryptedReadKeys[iNdEx]) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptedReadKeys[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.EncryptionKey) > 0 { + i -= len(m.EncryptionKey) + copy(dAtA[i:], m.EncryptionKey) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptionKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeUserConfirm) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeUserConfirm) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeUserConfirm) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UserAddId) > 0 { + i -= len(m.UserAddId) + copy(dAtA[i:], m.UserAddId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.UserAddId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeUserInvite) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeUserInvite) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeUserInvite) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Permissions != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.Permissions)) + i-- + dAtA[i] = 0x20 + } + if len(m.EncryptedReadKeys) > 0 { + for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EncryptedReadKeys[iNdEx]) + copy(dAtA[i:], m.EncryptedReadKeys[iNdEx]) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptedReadKeys[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.EncryptPublicKey) > 0 { + i -= len(m.EncryptPublicKey) + copy(dAtA[i:], m.EncryptPublicKey) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptPublicKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.AcceptPublicKey) > 0 { + i -= len(m.AcceptPublicKey) + copy(dAtA[i:], m.AcceptPublicKey) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.AcceptPublicKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeUserJoin) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeUserJoin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeUserJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.EncryptedReadKeys) > 0 { + for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EncryptedReadKeys[iNdEx]) + copy(dAtA[i:], m.EncryptedReadKeys[iNdEx]) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptedReadKeys[iNdEx]))) + i-- + dAtA[i] = 0x2a + } + } + if len(m.UserInviteChangeId) > 0 { + i -= len(m.UserInviteChangeId) + copy(dAtA[i:], m.UserInviteChangeId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.UserInviteChangeId))) + i-- + dAtA[i] = 0x22 + } + if len(m.AcceptSignature) > 0 { + i -= len(m.AcceptSignature) + copy(dAtA[i:], m.AcceptSignature) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.AcceptSignature))) + i-- + dAtA[i] = 0x1a + } + if len(m.EncryptionKey) > 0 { + i -= len(m.EncryptionKey) + copy(dAtA[i:], m.EncryptionKey) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptionKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeUserRemove) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeUserRemove) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeUserRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ReadKeyReplaces) > 0 { + for iNdEx := len(m.ReadKeyReplaces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ReadKeyReplaces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeReadKeyReplace) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeReadKeyReplace) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeReadKeyReplace) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.EncryptedReadKey) > 0 { + i -= len(m.EncryptedReadKey) + copy(dAtA[i:], m.EncryptedReadKey) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptedReadKey))) + i-- + dAtA[i] = 0x1a + } + if len(m.EncryptionKey) > 0 { + i -= len(m.EncryptionKey) + copy(dAtA[i:], m.EncryptionKey) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.EncryptionKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ACLChangeUserPermissionChange) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ACLChangeUserPermissionChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ACLChangeUserPermissionChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Permissions != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.Permissions)) + i-- + dAtA[i] = 0x10 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAclchanges(dAtA []byte, offset int, v uint64) int { + offset -= sovAclchanges(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ACLChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.TreeHeadIds) > 0 { + for _, s := range m.TreeHeadIds { + l = len(s) + n += 1 + l + sovAclchanges(uint64(l)) + } + } + if len(m.AclHeadIds) > 0 { + for _, s := range m.AclHeadIds { + l = len(s) + n += 1 + l + sovAclchanges(uint64(l)) + } + } + l = len(m.SnapshotBaseId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if m.AclData != nil { + l = m.AclData.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.ChangesData) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if m.CurrentReadKeyHash != 0 { + n += 1 + sovAclchanges(uint64(m.CurrentReadKeyHash)) + } + if m.Timestamp != 0 { + n += 1 + sovAclchanges(uint64(m.Timestamp)) + } + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if len(m.LogHeads) > 0 { + for k, v := range m.LogHeads { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovAclchanges(uint64(len(k))) + 1 + len(v) + sovAclchanges(uint64(len(v))) + n += mapEntrySize + 1 + sovAclchanges(uint64(mapEntrySize)) + } + } + return n +} + +func (m *ACLChangeACLContentValue) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Value != nil { + n += m.Value.Size() + } + return n +} + +func (m *ACLChangeACLContentValueValueOfUserAdd) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UserAdd != nil { + l = m.UserAdd.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} +func (m *ACLChangeACLContentValueValueOfUserRemove) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UserRemove != nil { + l = m.UserRemove.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} +func (m *ACLChangeACLContentValueValueOfUserPermissionChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UserPermissionChange != nil { + l = m.UserPermissionChange.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} +func (m *ACLChangeACLContentValueValueOfUserInvite) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UserInvite != nil { + l = m.UserInvite.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} +func (m *ACLChangeACLContentValueValueOfUserJoin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UserJoin != nil { + l = m.UserJoin.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} +func (m *ACLChangeACLContentValueValueOfUserConfirm) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UserConfirm != nil { + l = m.UserConfirm.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} +func (m *ACLChangeACLData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AclSnapshot != nil { + l = m.AclSnapshot.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + if len(m.AclContent) > 0 { + for _, e := range m.AclContent { + l = e.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + } + return n +} + +func (m *ACLChangeACLSnapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AclState != nil { + l = m.AclState.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} + +func (m *ACLChangeACLState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ReadKeyHashes) > 0 { + l = 0 + for _, e := range m.ReadKeyHashes { + l += sovAclchanges(uint64(e)) + } + n += 1 + sovAclchanges(uint64(l)) + l + } + if len(m.UserStates) > 0 { + for _, e := range m.UserStates { + l = e.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + } + if len(m.Invites) > 0 { + for k, v := range m.Invites { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovAclchanges(uint64(l)) + } + mapEntrySize := 1 + len(k) + sovAclchanges(uint64(len(k))) + l + n += mapEntrySize + 1 + sovAclchanges(uint64(mapEntrySize)) + } + } + return n +} + +func (m *ACLChangeUserState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.EncryptionKey) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if len(m.EncryptedReadKeys) > 0 { + for _, b := range m.EncryptedReadKeys { + l = len(b) + n += 1 + l + sovAclchanges(uint64(l)) + } + } + if m.Permissions != 0 { + n += 1 + sovAclchanges(uint64(m.Permissions)) + } + if m.IsConfirmed { + n += 2 + } + return n +} + +func (m *ACLChangeUserAdd) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.EncryptionKey) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if len(m.EncryptedReadKeys) > 0 { + for _, b := range m.EncryptedReadKeys { + l = len(b) + n += 1 + l + sovAclchanges(uint64(l)) + } + } + if m.Permissions != 0 { + n += 1 + sovAclchanges(uint64(m.Permissions)) + } + return n +} + +func (m *ACLChangeUserConfirm) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.UserAddId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} + +func (m *ACLChangeUserInvite) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AcceptPublicKey) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.EncryptPublicKey) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if len(m.EncryptedReadKeys) > 0 { + for _, b := range m.EncryptedReadKeys { + l = len(b) + n += 1 + l + sovAclchanges(uint64(l)) + } + } + if m.Permissions != 0 { + n += 1 + sovAclchanges(uint64(m.Permissions)) + } + return n +} + +func (m *ACLChangeUserJoin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.EncryptionKey) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.AcceptSignature) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.UserInviteChangeId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if len(m.EncryptedReadKeys) > 0 { + for _, b := range m.EncryptedReadKeys { + l = len(b) + n += 1 + l + sovAclchanges(uint64(l)) + } + } + return n +} + +func (m *ACLChangeUserRemove) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if len(m.ReadKeyReplaces) > 0 { + for _, e := range m.ReadKeyReplaces { + l = e.Size() + n += 1 + l + sovAclchanges(uint64(l)) + } + } + return n +} + +func (m *ACLChangeReadKeyReplace) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.EncryptionKey) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.EncryptedReadKey) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} + +func (m *ACLChangeUserPermissionChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if m.Permissions != 0 { + n += 1 + sovAclchanges(uint64(m.Permissions)) + } + return n +} + +func sovAclchanges(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAclchanges(x uint64) (n int) { + return sovAclchanges(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ACLChange) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACLChange: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACLChange: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TreeHeadIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TreeHeadIds = append(m.TreeHeadIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclHeadIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AclHeadIds = append(m.AclHeadIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SnapshotBaseId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SnapshotBaseId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclData", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AclData == nil { + m.AclData = &ACLChangeACLData{} + } + if err := m.AclData.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChangesData", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChangesData = append(m.ChangesData[:0], dAtA[iNdEx:postIndex]...) + if m.ChangesData == nil { + m.ChangesData = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentReadKeyHash", wireType) + } + m.CurrentReadKeyHash = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CurrentReadKeyHash |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LogHeads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.LogHeads == nil { + m.LogHeads = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthAclchanges + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthAclchanges + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthAclchanges + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthAclchanges + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.LogHeads[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeACLContentValue) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACLContentValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACLContentValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserAdd", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ACLChangeUserAdd{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &ACLChangeACLContentValueValueOfUserAdd{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserRemove", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ACLChangeUserRemove{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &ACLChangeACLContentValueValueOfUserRemove{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserPermissionChange", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ACLChangeUserPermissionChange{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &ACLChangeACLContentValueValueOfUserPermissionChange{v} + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserInvite", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ACLChangeUserInvite{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &ACLChangeACLContentValueValueOfUserInvite{v} + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserJoin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ACLChangeUserJoin{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &ACLChangeACLContentValueValueOfUserJoin{v} + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserConfirm", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ACLChangeUserConfirm{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &ACLChangeACLContentValueValueOfUserConfirm{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeACLData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACLData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACLData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclSnapshot", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AclSnapshot == nil { + m.AclSnapshot = &ACLChangeACLSnapshot{} + } + if err := m.AclSnapshot.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclContent", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AclContent = append(m.AclContent, &ACLChangeACLContentValue{}) + if err := m.AclContent[len(m.AclContent)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeACLSnapshot) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACLSnapshot: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACLSnapshot: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AclState == nil { + m.AclState = &ACLChangeACLState{} + } + if err := m.AclState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeACLState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ACLState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACLState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ReadKeyHashes = append(m.ReadKeyHashes, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.ReadKeyHashes) == 0 { + m.ReadKeyHashes = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ReadKeyHashes = append(m.ReadKeyHashes, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field ReadKeyHashes", wireType) + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserStates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserStates = append(m.UserStates, &ACLChangeUserState{}) + if err := m.UserStates[len(m.UserStates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Invites", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Invites == nil { + m.Invites = make(map[string]*ACLChangeUserInvite) + } + var mapkey string + var mapvalue *ACLChangeUserInvite + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthAclchanges + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthAclchanges + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthAclchanges + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &ACLChangeUserInvite{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Invites[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeUserState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptionKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptionKey = append(m.EncryptionKey[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptionKey == nil { + m.EncryptionKey = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedReadKeys = append(m.EncryptedReadKeys, make([]byte, postIndex-iNdEx)) + copy(m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) + } + m.Permissions = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Permissions |= ACLChangeUserPermissions(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsConfirmed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsConfirmed = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeUserAdd) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserAdd: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserAdd: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptionKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptionKey = append(m.EncryptionKey[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptionKey == nil { + m.EncryptionKey = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedReadKeys = append(m.EncryptedReadKeys, make([]byte, postIndex-iNdEx)) + copy(m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) + } + m.Permissions = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Permissions |= ACLChangeUserPermissions(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeUserConfirm) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserConfirm: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserConfirm: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserAddId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserAddId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeUserInvite) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserInvite: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserInvite: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AcceptPublicKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AcceptPublicKey = append(m.AcceptPublicKey[:0], dAtA[iNdEx:postIndex]...) + if m.AcceptPublicKey == nil { + m.AcceptPublicKey = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptPublicKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptPublicKey = append(m.EncryptPublicKey[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptPublicKey == nil { + m.EncryptPublicKey = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedReadKeys = append(m.EncryptedReadKeys, make([]byte, postIndex-iNdEx)) + copy(m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) + } + m.Permissions = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Permissions |= ACLChangeUserPermissions(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeUserJoin) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserJoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserJoin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptionKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptionKey = append(m.EncryptionKey[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptionKey == nil { + m.EncryptionKey = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AcceptSignature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AcceptSignature = append(m.AcceptSignature[:0], dAtA[iNdEx:postIndex]...) + if m.AcceptSignature == nil { + m.AcceptSignature = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserInviteChangeId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserInviteChangeId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedReadKeys = append(m.EncryptedReadKeys, make([]byte, postIndex-iNdEx)) + copy(m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeUserRemove) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserRemove: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserRemove: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReadKeyReplaces", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ReadKeyReplaces = append(m.ReadKeyReplaces, &ACLChangeReadKeyReplace{}) + if err := m.ReadKeyReplaces[len(m.ReadKeyReplaces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeReadKeyReplace) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReadKeyReplace: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReadKeyReplace: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptionKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptionKey = append(m.EncryptionKey[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptionKey == nil { + m.EncryptionKey = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedReadKey = append(m.EncryptedReadKey[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptedReadKey == nil { + m.EncryptedReadKey = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ACLChangeUserPermissionChange) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserPermissionChange: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserPermissionChange: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) + } + m.Permissions = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Permissions |= ACLChangeUserPermissions(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAclchanges(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAclchanges + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAclchanges + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAclchanges + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAclchanges + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAclchanges + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAclchanges + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAclchanges = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAclchanges = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAclchanges = fmt.Errorf("proto: unexpected end of group") +) diff --git a/data/pb/protos/aclchanges.proto b/data/pb/protos/aclchanges.proto index d0c6d955..35883b88 100644 --- a/data/pb/protos/aclchanges.proto +++ b/data/pb/protos/aclchanges.proto @@ -2,8 +2,6 @@ syntax = "proto3"; package anytype; option go_package = "pb"; -import "pb/protos/changes.proto"; - // the element of change tree used to store and internal apply smartBlock history message ACLChange { repeated string treeHeadIds = 1; @@ -28,12 +26,6 @@ message ACLChange { } } - message ChangeData { - anytype.Change.Snapshot snapshot = 1; - repeated anytype.Change.Content content = 2; - repeated anytype.Change.FileKeys fileKeys = 3; - } - message ACLData { ACLSnapshot aclSnapshot = 1; repeated ACLContentValue aclContent = 2; From 3c2cd5d9369a6aee018aa639562783ffbfc3f862 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 30 Jun 2022 18:52:20 +0200 Subject: [PATCH 03/53] Rework threadbuilder for new change format --- Makefile | 5 +- data/aclstate.go | 7 +- data/aclstatebuilder.go | 3 +- data/aclstatebuilder_test.go | 2 +- data/acltreebuilder.go | 4 +- data/change.go | 2 +- data/pb/aclchanges.pb.go | 313 ++--- data/pb/plaintextchanges.pb.go | 1088 +++++++++++++++++ data/pb/protos/aclchanges.proto | 1 - data/pb/protos/plaintextchanges.proto | 24 + data/threadbuilder/invalidsnapshotexample.yml | 162 +-- data/threadbuilder/keychain.go | 5 +- data/threadbuilder/threadbuilder.go | 231 ++-- data/threadbuilder/threadbuildergraph_nix.go | 4 +- data/threadbuilder/ymlentities.go | 33 +- data/threadmodels/models.go | 24 +- data/threadmodels/threadid.go | 12 +- data/treebuilder.go | 4 +- go.mod | 2 + 19 files changed, 1407 insertions(+), 519 deletions(-) create mode 100644 data/pb/plaintextchanges.pb.go create mode 100644 data/pb/protos/plaintextchanges.proto diff --git a/Makefile b/Makefile index f0ac4769..38a94af5 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,8 @@ protos-go: @echo 'Generating protobuf packages (Go)...' $(eval P_TIMESTAMP := Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types) $(eval P_STRUCT := Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types) - @$(eval P_PROTOS := Mdata/pb/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb) + @$(eval P_ACL_CHANGES := Mdata/pb/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb) + @$(eval P_PLAINTEXT_CHANGES := Mdata/pb/protos/plaintextchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb) - $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT),$$(P_PROTOS)) + $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT),$$(P_ACL_CHANGES),$$(P_PLAINTEXT_CHANGES)) GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1 protoc --gogofaster_out=$(PKGMAP):./data/pb data/pb/protos/*.*; mv data/pb/data/pb/protos/*.go data/pb; rm -rf data/pb/data diff --git a/data/aclstate.go b/data/aclstate.go index 934e29c5..4bf0b200 100644 --- a/data/aclstate.go +++ b/data/aclstate.go @@ -4,10 +4,11 @@ import ( "bytes" "errors" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" - "github.com/textileio/go-threads/crypto/symmetric" "hash/fnv" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/textileio/go-threads/crypto/symmetric" ) var ErrNoSuchUser = errors.New("no such user") diff --git a/data/aclstatebuilder.go b/data/aclstatebuilder.go index ee1e1d9f..d80d07b8 100644 --- a/data/aclstatebuilder.go +++ b/data/aclstatebuilder.go @@ -2,8 +2,9 @@ package data import ( "fmt" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" ) type ACLStateBuilder struct { diff --git a/data/aclstatebuilder_test.go b/data/aclstatebuilder_test.go index c8f68756..9f81cc3f 100644 --- a/data/aclstatebuilder_test.go +++ b/data/aclstatebuilder_test.go @@ -1,9 +1,9 @@ package data import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadbuilder" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" "github.com/stretchr/testify/assert" "testing" ) diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index 1583eed7..7ce2f681 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -3,8 +3,8 @@ package data import ( "context" "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/core/thread" @@ -41,7 +41,7 @@ func (tb *ACLTreeBuilder) loadChange(id string) (ch *Change, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - record, err := tb.thread.GetRecord(ctx, id) + record, err := tb.thread.GetChange(ctx, id) if err != nil { return nil, err } diff --git a/data/change.go b/data/change.go index 1d33e57b..ede8f712 100644 --- a/data/change.go +++ b/data/change.go @@ -3,7 +3,7 @@ package data import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/crypto/symmetric" ) diff --git a/data/pb/aclchanges.pb.go b/data/pb/aclchanges.pb.go index ced412d1..925d68d4 100644 --- a/data/pb/aclchanges.pb.go +++ b/data/pb/aclchanges.pb.go @@ -60,11 +60,10 @@ type ACLChange struct { SnapshotBaseId string `protobuf:"bytes,3,opt,name=snapshotBaseId,proto3" json:"snapshotBaseId,omitempty"` AclData *ACLChangeACLData `protobuf:"bytes,4,opt,name=aclData,proto3" json:"aclData,omitempty"` // the data is encoded with read key and should be read in ChangesData format - ChangesData []byte `protobuf:"bytes,5,opt,name=changesData,proto3" json:"changesData,omitempty"` - CurrentReadKeyHash uint64 `protobuf:"varint,6,opt,name=currentReadKeyHash,proto3" json:"currentReadKeyHash,omitempty"` - Timestamp int64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Identity string `protobuf:"bytes,8,opt,name=identity,proto3" json:"identity,omitempty"` - LogHeads map[string]string `protobuf:"bytes,9,rep,name=logHeads,proto3" json:"logHeads,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ChangesData []byte `protobuf:"bytes,5,opt,name=changesData,proto3" json:"changesData,omitempty"` + CurrentReadKeyHash uint64 `protobuf:"varint,6,opt,name=currentReadKeyHash,proto3" json:"currentReadKeyHash,omitempty"` + Timestamp int64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Identity string `protobuf:"bytes,8,opt,name=identity,proto3" json:"identity,omitempty"` } func (m *ACLChange) Reset() { *m = ACLChange{} } @@ -156,13 +155,6 @@ func (m *ACLChange) GetIdentity() string { return "" } -func (m *ACLChange) GetLogHeads() map[string]string { - if m != nil { - return m.LogHeads - } - return nil -} - type ACLChangeACLContentValue struct { // Types that are valid to be assigned to Value: // *ACLChangeACLContentValueValueOfUserAdd @@ -178,7 +170,7 @@ func (m *ACLChangeACLContentValue) Reset() { *m = ACLChangeACLContentVal func (m *ACLChangeACLContentValue) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLContentValue) ProtoMessage() {} func (*ACLChangeACLContentValue) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 1} + return fileDescriptor_5a15109171a9dc49, []int{0, 0} } func (m *ACLChangeACLContentValue) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -309,7 +301,7 @@ func (m *ACLChangeACLData) Reset() { *m = ACLChangeACLData{} } func (m *ACLChangeACLData) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLData) ProtoMessage() {} func (*ACLChangeACLData) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 2} + return fileDescriptor_5a15109171a9dc49, []int{0, 1} } func (m *ACLChangeACLData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -361,7 +353,7 @@ func (m *ACLChangeACLSnapshot) Reset() { *m = ACLChangeACLSnapshot{} } func (m *ACLChangeACLSnapshot) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLSnapshot) ProtoMessage() {} func (*ACLChangeACLSnapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 3} + return fileDescriptor_5a15109171a9dc49, []int{0, 2} } func (m *ACLChangeACLSnapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -407,7 +399,7 @@ func (m *ACLChangeACLState) Reset() { *m = ACLChangeACLState{} } func (m *ACLChangeACLState) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLState) ProtoMessage() {} func (*ACLChangeACLState) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 4} + return fileDescriptor_5a15109171a9dc49, []int{0, 3} } func (m *ACLChangeACLState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -469,7 +461,7 @@ func (m *ACLChangeUserState) Reset() { *m = ACLChangeUserState{} } func (m *ACLChangeUserState) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserState) ProtoMessage() {} func (*ACLChangeUserState) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 5} + return fileDescriptor_5a15109171a9dc49, []int{0, 4} } func (m *ACLChangeUserState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -545,7 +537,7 @@ func (m *ACLChangeUserAdd) Reset() { *m = ACLChangeUserAdd{} } func (m *ACLChangeUserAdd) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserAdd) ProtoMessage() {} func (*ACLChangeUserAdd) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 6} + return fileDescriptor_5a15109171a9dc49, []int{0, 5} } func (m *ACLChangeUserAdd) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -612,7 +604,7 @@ func (m *ACLChangeUserConfirm) Reset() { *m = ACLChangeUserConfirm{} } func (m *ACLChangeUserConfirm) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserConfirm) ProtoMessage() {} func (*ACLChangeUserConfirm) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 7} + return fileDescriptor_5a15109171a9dc49, []int{0, 6} } func (m *ACLChangeUserConfirm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -666,7 +658,7 @@ func (m *ACLChangeUserInvite) Reset() { *m = ACLChangeUserInvite{} } func (m *ACLChangeUserInvite) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserInvite) ProtoMessage() {} func (*ACLChangeUserInvite) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 8} + return fileDescriptor_5a15109171a9dc49, []int{0, 7} } func (m *ACLChangeUserInvite) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -735,7 +727,7 @@ func (m *ACLChangeUserJoin) Reset() { *m = ACLChangeUserJoin{} } func (m *ACLChangeUserJoin) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserJoin) ProtoMessage() {} func (*ACLChangeUserJoin) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 9} + return fileDescriptor_5a15109171a9dc49, []int{0, 8} } func (m *ACLChangeUserJoin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -808,7 +800,7 @@ func (m *ACLChangeUserRemove) Reset() { *m = ACLChangeUserRemove{} } func (m *ACLChangeUserRemove) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserRemove) ProtoMessage() {} func (*ACLChangeUserRemove) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 10} + return fileDescriptor_5a15109171a9dc49, []int{0, 9} } func (m *ACLChangeUserRemove) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -861,7 +853,7 @@ func (m *ACLChangeReadKeyReplace) Reset() { *m = ACLChangeReadKeyReplace func (m *ACLChangeReadKeyReplace) String() string { return proto.CompactTextString(m) } func (*ACLChangeReadKeyReplace) ProtoMessage() {} func (*ACLChangeReadKeyReplace) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 11} + return fileDescriptor_5a15109171a9dc49, []int{0, 10} } func (m *ACLChangeReadKeyReplace) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -920,7 +912,7 @@ func (m *ACLChangeUserPermissionChange) Reset() { *m = ACLChangeUserPerm func (m *ACLChangeUserPermissionChange) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserPermissionChange) ProtoMessage() {} func (*ACLChangeUserPermissionChange) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 12} + return fileDescriptor_5a15109171a9dc49, []int{0, 11} } func (m *ACLChangeUserPermissionChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -966,7 +958,6 @@ func (m *ACLChangeUserPermissionChange) GetPermissions() ACLChangeUserPermission func init() { proto.RegisterEnum("anytype.ACLChangeUserPermissions", ACLChangeUserPermissions_name, ACLChangeUserPermissions_value) proto.RegisterType((*ACLChange)(nil), "anytype.ACLChange") - proto.RegisterMapType((map[string]string)(nil), "anytype.ACLChange.LogHeadsEntry") proto.RegisterType((*ACLChangeACLContentValue)(nil), "anytype.ACLChange.ACLContentValue") proto.RegisterType((*ACLChangeACLData)(nil), "anytype.ACLChange.ACLData") proto.RegisterType((*ACLChangeACLSnapshot)(nil), "anytype.ACLChange.ACLSnapshot") @@ -985,66 +976,64 @@ func init() { func init() { proto.RegisterFile("data/pb/protos/aclchanges.proto", fileDescriptor_5a15109171a9dc49) } var fileDescriptor_5a15109171a9dc49 = []byte{ - // 938 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0xdd, 0x6e, 0xe3, 0x44, - 0x14, 0xce, 0x24, 0x6d, 0x1d, 0x1f, 0xa7, 0x6d, 0x18, 0x15, 0xc9, 0xf2, 0x16, 0x63, 0xaa, 0x15, - 0x58, 0x08, 0xa5, 0x52, 0x16, 0xf1, 0xbb, 0x12, 0x34, 0x5d, 0x20, 0xa1, 0x7b, 0xb1, 0x9a, 0x15, - 0x20, 0x90, 0xb8, 0x98, 0xda, 0x43, 0x6b, 0x91, 0xd8, 0x96, 0x67, 0x52, 0x91, 0x1b, 0xde, 0x00, - 0xc1, 0x53, 0xf0, 0x00, 0xbc, 0x00, 0xb7, 0x5c, 0xee, 0x05, 0x42, 0x5c, 0xa2, 0xf6, 0x45, 0xd0, - 0xfc, 0xd8, 0x71, 0x52, 0xa7, 0x48, 0x68, 0x25, 0xc4, 0xdd, 0xcc, 0x37, 0xdf, 0x99, 0x39, 0x3f, - 0xdf, 0x39, 0x36, 0xbc, 0x1c, 0x53, 0x41, 0x8f, 0xf3, 0xf3, 0xe3, 0xbc, 0xc8, 0x44, 0xc6, 0x8f, - 0x69, 0x34, 0x8d, 0x2e, 0x69, 0x7a, 0xc1, 0xf8, 0x40, 0x21, 0xd8, 0xa2, 0xe9, 0x42, 0x2c, 0x72, - 0x76, 0xf4, 0xf3, 0x8b, 0x60, 0x9f, 0x9c, 0x3e, 0x3e, 0x55, 0xa7, 0x38, 0x00, 0x47, 0x14, 0x8c, - 0x8d, 0x19, 0x8d, 0x27, 0x31, 0x77, 0x51, 0xd0, 0x09, 0x6d, 0x52, 0x87, 0xb0, 0x0f, 0x40, 0xa3, - 0x69, 0x49, 0x68, 0x2b, 0x42, 0x0d, 0xc1, 0xaf, 0xc2, 0x1e, 0x4f, 0x69, 0xce, 0x2f, 0x33, 0x31, - 0xa2, 0x9c, 0x4d, 0x62, 0xb7, 0x13, 0xa0, 0xd0, 0x26, 0x6b, 0x28, 0x7e, 0x13, 0x2c, 0x1a, 0x4d, - 0x1f, 0x51, 0x41, 0xdd, 0xad, 0x00, 0x85, 0xce, 0xd0, 0x1b, 0x18, 0x97, 0x06, 0x95, 0x3b, 0x72, - 0x25, 0x19, 0xa4, 0xa4, 0x4a, 0xff, 0x4c, 0x1c, 0xca, 0x72, 0x3b, 0x40, 0x61, 0x8f, 0xd4, 0x21, - 0x3c, 0x00, 0x1c, 0xcd, 0x8b, 0x82, 0xa5, 0x82, 0x30, 0x1a, 0x9f, 0xb1, 0xc5, 0x98, 0xf2, 0x4b, - 0x77, 0x27, 0x40, 0xe1, 0x16, 0x69, 0x38, 0xc1, 0x87, 0x60, 0x8b, 0x64, 0xc6, 0xb8, 0xa0, 0xb3, - 0xdc, 0xb5, 0x02, 0x14, 0x76, 0xc8, 0x12, 0xc0, 0x1e, 0x74, 0x93, 0x98, 0xa5, 0x22, 0x11, 0x0b, - 0xb7, 0xab, 0xe2, 0xa8, 0xf6, 0xf8, 0x21, 0x74, 0xa7, 0xd9, 0x85, 0x8c, 0x9b, 0xbb, 0x76, 0xd0, - 0x09, 0x9d, 0x61, 0xd0, 0x10, 0xc2, 0x63, 0x43, 0xf9, 0x28, 0x15, 0xc5, 0x82, 0x54, 0x16, 0xde, - 0xfb, 0xb0, 0xbb, 0x72, 0x84, 0xfb, 0xd0, 0xf9, 0x96, 0x2d, 0x5c, 0xa4, 0x5e, 0x91, 0x4b, 0x7c, - 0x00, 0xdb, 0x57, 0x74, 0x3a, 0x67, 0x6e, 0x5b, 0x61, 0x7a, 0xf3, 0x5e, 0xfb, 0x1d, 0xe4, 0xfd, - 0xd2, 0x81, 0x7d, 0xf9, 0x44, 0x96, 0x0a, 0x96, 0x8a, 0xcf, 0x25, 0x8e, 0xdf, 0x02, 0x6b, 0xce, - 0x59, 0x71, 0x12, 0xc7, 0xea, 0x8e, 0xe6, 0x84, 0x7e, 0xa6, 0x19, 0xe3, 0x16, 0x29, 0xc9, 0xf8, - 0x03, 0x00, 0xb9, 0x24, 0x6c, 0x96, 0x5d, 0xe9, 0xa7, 0x9c, 0xe1, 0x4b, 0x1b, 0x4c, 0x35, 0x69, - 0xdc, 0x22, 0x35, 0x13, 0xfc, 0x35, 0x1c, 0xc8, 0xdd, 0x13, 0x56, 0xcc, 0x12, 0xce, 0x93, 0x2c, - 0xd5, 0x06, 0xaa, 0xee, 0xce, 0xf0, 0xb5, 0x0d, 0x57, 0xad, 0xd3, 0xc7, 0x2d, 0xd2, 0x78, 0x4d, - 0xe9, 0xdf, 0x24, 0xbd, 0x4a, 0x04, 0x33, 0x5a, 0xd9, 0xe4, 0x9f, 0x26, 0x95, 0xfe, 0xe9, 0x1d, - 0x7e, 0x17, 0xba, 0x72, 0xf7, 0x69, 0x96, 0xa4, 0x4a, 0x30, 0xce, 0xf0, 0xde, 0x06, 0x73, 0x49, - 0x19, 0xb7, 0x48, 0x45, 0xc7, 0x23, 0x70, 0xe4, 0xfa, 0x34, 0x4b, 0xbf, 0x49, 0x8a, 0x99, 0x52, - 0x91, 0x33, 0xf4, 0x37, 0x58, 0x1b, 0xd6, 0xb8, 0x45, 0xea, 0x46, 0x23, 0xcb, 0x54, 0xd1, 0xfb, - 0x11, 0x81, 0x65, 0x04, 0x8d, 0x3f, 0x04, 0x87, 0x46, 0xd3, 0xa7, 0xa6, 0x25, 0x4c, 0xc1, 0xfc, - 0xe6, 0x0e, 0x28, 0x59, 0xa4, 0x6e, 0x82, 0x47, 0xaa, 0x0f, 0x8d, 0x02, 0x54, 0x1f, 0x3a, 0xc3, - 0xa3, 0xe6, 0x0b, 0xea, 0x32, 0x21, 0x35, 0x2b, 0xef, 0x63, 0x70, 0x6a, 0xf7, 0xe3, 0xb7, 0xa1, - 0x2b, 0x5f, 0x10, 0x54, 0x30, 0xe3, 0xd1, 0xbd, 0x0d, 0x1e, 0x49, 0x0a, 0xa9, 0xc8, 0xde, 0x0f, - 0x6d, 0xe8, 0x96, 0x30, 0xbe, 0x0f, 0xbb, 0xc5, 0xb2, 0xbf, 0x98, 0x1e, 0x22, 0x5b, 0x64, 0x15, - 0xc4, 0x0f, 0x75, 0x55, 0x95, 0x09, 0x37, 0xee, 0x1f, 0x6e, 0x48, 0xac, 0x7e, 0xae, 0xc6, 0xc7, - 0x23, 0xb0, 0x12, 0x55, 0x5c, 0xee, 0x76, 0x94, 0x69, 0x78, 0x87, 0xa3, 0x03, 0xad, 0x03, 0xd3, - 0x81, 0xa5, 0xa1, 0xf7, 0x25, 0xf4, 0xea, 0x07, 0x0d, 0xfd, 0xf7, 0xa0, 0xde, 0x7f, 0xff, 0x24, - 0xba, 0x7a, 0x7b, 0xde, 0x20, 0xb0, 0x2b, 0xc7, 0x57, 0x66, 0x08, 0x5a, 0x9b, 0x21, 0xf7, 0x61, - 0x97, 0xa5, 0x51, 0xb1, 0xc8, 0x45, 0x92, 0xa5, 0x67, 0x6c, 0xa1, 0x9e, 0xea, 0x91, 0x55, 0x10, - 0xbf, 0x01, 0x2f, 0x18, 0x80, 0xc5, 0x66, 0x76, 0xe9, 0xc0, 0x7b, 0xe4, 0xf6, 0x01, 0x7e, 0x04, - 0x4e, 0x5e, 0x35, 0x11, 0x57, 0x1d, 0xb3, 0xd7, 0x28, 0x8d, 0xd5, 0x36, 0xe4, 0xa4, 0x6e, 0x26, - 0x27, 0xed, 0x84, 0x1b, 0x0d, 0xb3, 0x58, 0x35, 0x4e, 0x97, 0xd4, 0x21, 0xef, 0x57, 0x04, 0x96, - 0x99, 0x27, 0xff, 0xcf, 0x18, 0xbd, 0x4f, 0xc0, 0xa9, 0x35, 0xee, 0x9d, 0x41, 0x1c, 0x82, 0x6d, - 0x06, 0xe6, 0x24, 0x36, 0xf3, 0x78, 0x09, 0x78, 0x7f, 0x20, 0x80, 0xa5, 0x14, 0x70, 0x08, 0xfb, - 0x34, 0x8a, 0x58, 0x2e, 0x9e, 0xcc, 0xcf, 0xa7, 0x49, 0x74, 0x66, 0x64, 0xd5, 0x23, 0xeb, 0x30, - 0x7e, 0x1d, 0xfa, 0x26, 0xb8, 0x25, 0x55, 0xa7, 0xe7, 0x16, 0xfe, 0x9f, 0x64, 0xe8, 0x77, 0x04, - 0xdd, 0x72, 0x32, 0x3e, 0x87, 0x22, 0x57, 0x89, 0x79, 0x9a, 0x5c, 0xa4, 0x54, 0xcc, 0x0b, 0xfd, - 0x95, 0xa8, 0x12, 0x53, 0xc1, 0xf2, 0x33, 0xbe, 0x1c, 0xe1, 0xda, 0xd3, 0x49, 0xac, 0xa2, 0xb0, - 0x49, 0xc3, 0x49, 0x73, 0x72, 0xb6, 0x37, 0x24, 0xc7, 0x9b, 0xeb, 0x72, 0x99, 0x0f, 0xd8, 0x5d, - 0x71, 0x9d, 0xc1, 0xbe, 0x19, 0x5c, 0x84, 0xe5, 0x53, 0x1a, 0x55, 0x13, 0xe7, 0x95, 0x86, 0x54, - 0x92, 0x15, 0x26, 0x59, 0xb7, 0xf4, 0xbe, 0x87, 0xbd, 0x55, 0xca, 0x73, 0x48, 0xe9, 0x52, 0x41, - 0x55, 0x7c, 0x26, 0xa7, 0xb7, 0x70, 0xef, 0x3b, 0x38, 0x68, 0xfa, 0xf4, 0xde, 0xe9, 0xc5, 0x9a, - 0x8e, 0xda, 0xff, 0x4a, 0x47, 0x47, 0x27, 0xb0, 0xbf, 0x76, 0x8e, 0x6d, 0xd8, 0x3e, 0x89, 0x67, - 0x49, 0xda, 0x6f, 0x61, 0x80, 0x9d, 0x2f, 0x8a, 0x44, 0xb0, 0xa2, 0x8f, 0xe4, 0x5a, 0xba, 0xcb, - 0x8a, 0x7e, 0x1b, 0x3b, 0x60, 0xe9, 0x12, 0xc5, 0xfd, 0xce, 0xe8, 0xf0, 0xb7, 0x6b, 0x1f, 0x3d, - 0xbb, 0xf6, 0xd1, 0x5f, 0xd7, 0x3e, 0xfa, 0xe9, 0xc6, 0x6f, 0x3d, 0xbb, 0xf1, 0x5b, 0x7f, 0xde, - 0xf8, 0xad, 0xaf, 0xda, 0xf9, 0xf9, 0xf9, 0x8e, 0xfa, 0xad, 0x7d, 0xf0, 0x77, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x14, 0x6d, 0x36, 0xcd, 0xf9, 0x0a, 0x00, 0x00, + // 898 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4d, 0x6f, 0xe3, 0x44, + 0x18, 0xf6, 0x24, 0x6d, 0x1d, 0xbf, 0x0e, 0x6d, 0x18, 0xf5, 0x60, 0x79, 0x8b, 0x09, 0x15, 0x82, + 0x08, 0xa1, 0x54, 0xca, 0x22, 0xbe, 0x84, 0x04, 0x4d, 0x17, 0x48, 0x28, 0x87, 0xd5, 0xac, 0x00, + 0x81, 0xc4, 0x61, 0x6a, 0x0f, 0x5b, 0x8b, 0xc4, 0xb6, 0x3c, 0x93, 0x0a, 0x5f, 0xf8, 0x07, 0x08, + 0xfe, 0x07, 0x37, 0xfe, 0x00, 0x57, 0x8e, 0x7b, 0x40, 0x88, 0x23, 0x6a, 0xff, 0x08, 0x9a, 0x0f, + 0x3b, 0x4e, 0x6a, 0x17, 0x09, 0x55, 0x42, 0xdc, 0x66, 0x9e, 0x79, 0xde, 0xf1, 0xf3, 0x7e, 0x8e, + 0xe1, 0xc5, 0x88, 0x0a, 0x7a, 0x92, 0x5d, 0x9c, 0x64, 0x79, 0x2a, 0x52, 0x7e, 0x42, 0xc3, 0x45, + 0x78, 0x49, 0x93, 0xa7, 0x8c, 0x8f, 0x15, 0x82, 0x6d, 0x9a, 0x14, 0xa2, 0xc8, 0xd8, 0xf1, 0xcf, + 0x87, 0xe0, 0x9c, 0x9e, 0x7d, 0x7a, 0xa6, 0x4e, 0xf1, 0x10, 0x5c, 0x91, 0x33, 0x36, 0x63, 0x34, + 0x9a, 0x47, 0xdc, 0x43, 0xc3, 0xee, 0xc8, 0x21, 0x75, 0x08, 0x07, 0x00, 0x34, 0x5c, 0x94, 0x84, + 0x8e, 0x22, 0xd4, 0x10, 0xfc, 0x0a, 0xec, 0xf3, 0x84, 0x66, 0xfc, 0x32, 0x15, 0x53, 0xca, 0xd9, + 0x3c, 0xf2, 0xba, 0x43, 0x34, 0x72, 0xc8, 0x16, 0x8a, 0xdf, 0x00, 0x9b, 0x86, 0x8b, 0x47, 0x54, + 0x50, 0x6f, 0x67, 0x88, 0x46, 0xee, 0xc4, 0x1f, 0x1b, 0x49, 0xe3, 0x4a, 0x8e, 0x5c, 0x49, 0x06, + 0x29, 0xa9, 0x52, 0x9f, 0xf1, 0x43, 0x59, 0xee, 0x0e, 0xd1, 0xa8, 0x4f, 0xea, 0x10, 0x1e, 0x03, + 0x0e, 0x57, 0x79, 0xce, 0x12, 0x41, 0x18, 0x8d, 0xce, 0x59, 0x31, 0xa3, 0xfc, 0xd2, 0xdb, 0x1b, + 0xa2, 0xd1, 0x0e, 0x69, 0x38, 0xc1, 0x47, 0xe0, 0x88, 0x78, 0xc9, 0xb8, 0xa0, 0xcb, 0xcc, 0xb3, + 0x87, 0x68, 0xd4, 0x25, 0x6b, 0x00, 0xfb, 0xd0, 0x8b, 0x23, 0x96, 0x88, 0x58, 0x14, 0x5e, 0x4f, + 0xf9, 0x51, 0xed, 0xfd, 0x5f, 0xba, 0x70, 0x20, 0xa5, 0xa6, 0x89, 0x60, 0x89, 0xf8, 0x9c, 0x2e, + 0x56, 0x0c, 0xbf, 0x09, 0xf6, 0x8a, 0xb3, 0xfc, 0x34, 0x8a, 0x3c, 0xd4, 0xea, 0xd5, 0x67, 0x9a, + 0x31, 0xb3, 0x48, 0x49, 0xc6, 0xef, 0x03, 0xc8, 0x25, 0x61, 0xcb, 0xf4, 0x8a, 0x79, 0x1d, 0x65, + 0xfa, 0x42, 0x8b, 0xa9, 0x26, 0xcd, 0x2c, 0x52, 0x33, 0xc1, 0x5f, 0xc3, 0xa1, 0xdc, 0x3d, 0x66, + 0xf9, 0x32, 0xe6, 0x3c, 0x4e, 0x13, 0x6d, 0xa0, 0x82, 0xef, 0x4e, 0x5e, 0x6d, 0xb9, 0x6a, 0x9b, + 0x3e, 0xb3, 0x48, 0xe3, 0x35, 0xa5, 0xbe, 0x79, 0x72, 0x15, 0x0b, 0x66, 0x12, 0xd6, 0xa6, 0x4f, + 0x93, 0x4a, 0x7d, 0x7a, 0x87, 0xdf, 0x81, 0x9e, 0xdc, 0x7d, 0x92, 0xc6, 0x89, 0xca, 0x9a, 0x3b, + 0x79, 0xd0, 0x62, 0x2e, 0x29, 0x33, 0x8b, 0x54, 0x74, 0x3c, 0x05, 0x57, 0xae, 0xcf, 0xd2, 0xe4, + 0x9b, 0x38, 0x5f, 0xaa, 0x54, 0xba, 0x93, 0xa0, 0xc5, 0xda, 0xb0, 0x66, 0x16, 0xa9, 0x1b, 0x4d, + 0x6d, 0xd8, 0xbd, 0x92, 0x09, 0xf2, 0x7f, 0x44, 0x60, 0x9b, 0xaa, 0xc2, 0x1f, 0x80, 0x4b, 0xc3, + 0xc5, 0x13, 0x53, 0x97, 0x26, 0x61, 0x41, 0x73, 0x19, 0x96, 0x2c, 0x52, 0x37, 0xc1, 0x53, 0xd5, + 0x0c, 0xa6, 0x02, 0x54, 0x33, 0xb8, 0x93, 0xe3, 0xe6, 0x0b, 0xea, 0x65, 0x42, 0x6a, 0x56, 0xfe, + 0x47, 0xe0, 0xd6, 0xee, 0xc7, 0x6f, 0x41, 0x4f, 0x7e, 0x41, 0x50, 0xc1, 0x8c, 0xa2, 0x07, 0x2d, + 0x8a, 0x24, 0x85, 0x54, 0x64, 0xff, 0x87, 0x0e, 0xf4, 0x4a, 0x18, 0xbf, 0x0c, 0xcf, 0xe5, 0xeb, + 0x22, 0x67, 0xba, 0x93, 0x77, 0xc8, 0x26, 0x88, 0xdf, 0xd3, 0x59, 0x55, 0x26, 0xdc, 0xc8, 0x3f, + 0x6a, 0x09, 0xac, 0xfe, 0x5c, 0x8d, 0x8f, 0xa7, 0x60, 0xc7, 0x2a, 0xb9, 0xdc, 0xeb, 0x2a, 0xd3, + 0xd1, 0x1d, 0x42, 0xc7, 0xba, 0x0e, 0xf8, 0x87, 0x89, 0xc8, 0x0b, 0x52, 0x1a, 0xfa, 0x5f, 0x42, + 0xbf, 0x7e, 0x80, 0x07, 0xd0, 0xfd, 0x96, 0x15, 0xca, 0x71, 0x87, 0xc8, 0x25, 0x7e, 0x68, 0x32, + 0xf7, 0x0f, 0x4d, 0xa1, 0x6f, 0x21, 0x9a, 0xfb, 0x6e, 0xe7, 0x6d, 0xe4, 0xdf, 0x20, 0x70, 0x2a, + 0xe1, 0x1b, 0x8d, 0x8c, 0x36, 0x1b, 0x59, 0x06, 0x8b, 0x25, 0x61, 0x5e, 0x64, 0x22, 0x4e, 0x93, + 0x73, 0x56, 0xa8, 0x4f, 0xf5, 0xc9, 0x26, 0x88, 0x5f, 0x87, 0xe7, 0x0d, 0xc0, 0x22, 0x33, 0x40, + 0xb4, 0xe3, 0x7d, 0x72, 0xfb, 0x00, 0x3f, 0x02, 0x37, 0xab, 0x9a, 0x88, 0xab, 0x8e, 0xd9, 0x6f, + 0x2c, 0x8d, 0xcd, 0x36, 0xe4, 0xa4, 0x6e, 0x26, 0xc7, 0xdd, 0x9c, 0x9b, 0x1a, 0x66, 0x91, 0x6a, + 0x9c, 0x1e, 0xa9, 0x43, 0xfe, 0xaf, 0x08, 0x6c, 0x33, 0x4f, 0xfe, 0x9f, 0x3e, 0xfa, 0x1f, 0x83, + 0x5b, 0x6b, 0xdc, 0x3b, 0x9d, 0x38, 0x02, 0xc7, 0x0c, 0xcc, 0x79, 0xa4, 0x1c, 0x70, 0xc8, 0x1a, + 0xf0, 0xff, 0x40, 0x00, 0xeb, 0x52, 0xc0, 0x23, 0x38, 0xa0, 0x61, 0xc8, 0x32, 0xf1, 0x78, 0x75, + 0xb1, 0x88, 0xc3, 0x73, 0x53, 0x56, 0x7d, 0xb2, 0x0d, 0xe3, 0xd7, 0x60, 0x60, 0x9c, 0x5b, 0x53, + 0x75, 0x78, 0x6e, 0xe1, 0xff, 0x49, 0x84, 0x7e, 0x47, 0xd0, 0x2b, 0x27, 0xe3, 0x3d, 0x24, 0xb9, + 0x0a, 0xcc, 0x93, 0xf8, 0x69, 0x42, 0xc5, 0x2a, 0xd7, 0xaf, 0x44, 0x15, 0x98, 0x0a, 0x96, 0x6f, + 0xe9, 0x7a, 0x84, 0x6b, 0xa5, 0xf3, 0x48, 0x79, 0xe1, 0x90, 0x86, 0x93, 0xe6, 0xe0, 0xec, 0xb6, + 0x04, 0xc7, 0x5f, 0xe9, 0x74, 0x99, 0x07, 0xec, 0x2e, 0xbf, 0xce, 0xe1, 0xc0, 0x0c, 0x2e, 0xc2, + 0xb2, 0x05, 0x0d, 0xab, 0x89, 0xf3, 0x52, 0x43, 0x28, 0xc9, 0x06, 0x93, 0x6c, 0x5b, 0xfa, 0xdf, + 0xc3, 0xfe, 0x26, 0xe5, 0x1e, 0x42, 0xba, 0xae, 0xa0, 0xca, 0x3f, 0x13, 0xd3, 0x5b, 0xb8, 0xff, + 0x1d, 0x1c, 0x36, 0x3d, 0xbd, 0x77, 0xaa, 0xd8, 0xaa, 0xa3, 0xce, 0xbf, 0xaa, 0xa3, 0xe3, 0x53, + 0x38, 0xd8, 0x3a, 0xc7, 0x0e, 0xec, 0x9e, 0x46, 0xcb, 0x38, 0x19, 0x58, 0x18, 0x60, 0xef, 0x8b, + 0x3c, 0x16, 0x2c, 0x1f, 0x20, 0xb9, 0x96, 0x72, 0x59, 0x3e, 0xe8, 0x60, 0x17, 0x6c, 0x9d, 0xa2, + 0x68, 0xd0, 0x9d, 0x1e, 0xfd, 0x76, 0x1d, 0xa0, 0x67, 0xd7, 0x01, 0xfa, 0xeb, 0x3a, 0x40, 0x3f, + 0xdd, 0x04, 0xd6, 0xb3, 0x9b, 0xc0, 0xfa, 0xf3, 0x26, 0xb0, 0xbe, 0xea, 0x64, 0x17, 0x17, 0x7b, + 0xea, 0xdf, 0xf2, 0xe1, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x2f, 0x29, 0xef, 0x7e, 0x0a, + 0x00, 0x00, } func (m *ACLChange) Marshal() (dAtA []byte, err error) { @@ -1067,25 +1056,6 @@ func (m *ACLChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.LogHeads) > 0 { - for k := range m.LogHeads { - v := m.LogHeads[k] - baseI := i - i -= len(v) - copy(dAtA[i:], v) - i = encodeVarintAclchanges(dAtA, i, uint64(len(v))) - i-- - dAtA[i] = 0x12 - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintAclchanges(dAtA, i, uint64(len(k))) - i-- - dAtA[i] = 0xa - i = encodeVarintAclchanges(dAtA, i, uint64(baseI-i)) - i-- - dAtA[i] = 0x4a - } - } if len(m.Identity) > 0 { i -= len(m.Identity) copy(dAtA[i:], m.Identity) @@ -1907,14 +1877,6 @@ func (m *ACLChange) Size() (n int) { if l > 0 { n += 1 + l + sovAclchanges(uint64(l)) } - if len(m.LogHeads) > 0 { - for k, v := range m.LogHeads { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovAclchanges(uint64(len(k))) + 1 + len(v) + sovAclchanges(uint64(len(v))) - n += mapEntrySize + 1 + sovAclchanges(uint64(mapEntrySize)) - } - } return n } @@ -2525,133 +2487,6 @@ func (m *ACLChange) Unmarshal(dAtA []byte) error { } m.Identity = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LogHeads", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclchanges - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclchanges - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.LogHeads == nil { - m.LogHeads = make(map[string]string) - } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthAclchanges - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthAclchanges - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthAclchanges - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue < 0 { - return ErrInvalidLengthAclchanges - } - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipAclchanges(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclchanges - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.LogHeads[mapkey] = mapvalue - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAclchanges(dAtA[iNdEx:]) diff --git a/data/pb/plaintextchanges.pb.go b/data/pb/plaintextchanges.pb.go new file mode 100644 index 00000000..ac95a1b7 --- /dev/null +++ b/data/pb/plaintextchanges.pb.go @@ -0,0 +1,1088 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: data/pb/protos/plaintextchanges.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type PlainTextChange struct { +} + +func (m *PlainTextChange) Reset() { *m = PlainTextChange{} } +func (m *PlainTextChange) String() string { return proto.CompactTextString(m) } +func (*PlainTextChange) ProtoMessage() {} +func (*PlainTextChange) Descriptor() ([]byte, []int) { + return fileDescriptor_970a1e91eeb094c9, []int{0} +} +func (m *PlainTextChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PlainTextChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PlainTextChange.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PlainTextChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_PlainTextChange.Merge(m, src) +} +func (m *PlainTextChange) XXX_Size() int { + return m.Size() +} +func (m *PlainTextChange) XXX_DiscardUnknown() { + xxx_messageInfo_PlainTextChange.DiscardUnknown(m) +} + +var xxx_messageInfo_PlainTextChange proto.InternalMessageInfo + +type PlainTextChangeContent struct { + // Types that are valid to be assigned to Value: + // *PlainTextChangeContentValueOfTextAppend + Value IsPlainTextChangeContentValue `protobuf_oneof:"value"` +} + +func (m *PlainTextChangeContent) Reset() { *m = PlainTextChangeContent{} } +func (m *PlainTextChangeContent) String() string { return proto.CompactTextString(m) } +func (*PlainTextChangeContent) ProtoMessage() {} +func (*PlainTextChangeContent) Descriptor() ([]byte, []int) { + return fileDescriptor_970a1e91eeb094c9, []int{0, 0} +} +func (m *PlainTextChangeContent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PlainTextChangeContent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PlainTextChangeContent.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PlainTextChangeContent) XXX_Merge(src proto.Message) { + xxx_messageInfo_PlainTextChangeContent.Merge(m, src) +} +func (m *PlainTextChangeContent) XXX_Size() int { + return m.Size() +} +func (m *PlainTextChangeContent) XXX_DiscardUnknown() { + xxx_messageInfo_PlainTextChangeContent.DiscardUnknown(m) +} + +var xxx_messageInfo_PlainTextChangeContent proto.InternalMessageInfo + +type IsPlainTextChangeContentValue interface { + IsPlainTextChangeContentValue() + MarshalTo([]byte) (int, error) + Size() int +} + +type PlainTextChangeContentValueOfTextAppend struct { + TextAppend *PlainTextChangeTextAppend `protobuf:"bytes,1,opt,name=textAppend,proto3,oneof" json:"textAppend,omitempty"` +} + +func (*PlainTextChangeContentValueOfTextAppend) IsPlainTextChangeContentValue() {} + +func (m *PlainTextChangeContent) GetValue() IsPlainTextChangeContentValue { + if m != nil { + return m.Value + } + return nil +} + +func (m *PlainTextChangeContent) GetTextAppend() *PlainTextChangeTextAppend { + if x, ok := m.GetValue().(*PlainTextChangeContentValueOfTextAppend); ok { + return x.TextAppend + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*PlainTextChangeContent) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*PlainTextChangeContentValueOfTextAppend)(nil), + } +} + +type PlainTextChangeTextAppend struct { + Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` +} + +func (m *PlainTextChangeTextAppend) Reset() { *m = PlainTextChangeTextAppend{} } +func (m *PlainTextChangeTextAppend) String() string { return proto.CompactTextString(m) } +func (*PlainTextChangeTextAppend) ProtoMessage() {} +func (*PlainTextChangeTextAppend) Descriptor() ([]byte, []int) { + return fileDescriptor_970a1e91eeb094c9, []int{0, 1} +} +func (m *PlainTextChangeTextAppend) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PlainTextChangeTextAppend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PlainTextChangeTextAppend.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PlainTextChangeTextAppend) XXX_Merge(src proto.Message) { + xxx_messageInfo_PlainTextChangeTextAppend.Merge(m, src) +} +func (m *PlainTextChangeTextAppend) XXX_Size() int { + return m.Size() +} +func (m *PlainTextChangeTextAppend) XXX_DiscardUnknown() { + xxx_messageInfo_PlainTextChangeTextAppend.DiscardUnknown(m) +} + +var xxx_messageInfo_PlainTextChangeTextAppend proto.InternalMessageInfo + +func (m *PlainTextChangeTextAppend) GetText() string { + if m != nil { + return m.Text + } + return "" +} + +type PlainTextChangeSnapshot struct { + Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` +} + +func (m *PlainTextChangeSnapshot) Reset() { *m = PlainTextChangeSnapshot{} } +func (m *PlainTextChangeSnapshot) String() string { return proto.CompactTextString(m) } +func (*PlainTextChangeSnapshot) ProtoMessage() {} +func (*PlainTextChangeSnapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_970a1e91eeb094c9, []int{0, 2} +} +func (m *PlainTextChangeSnapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PlainTextChangeSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PlainTextChangeSnapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PlainTextChangeSnapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_PlainTextChangeSnapshot.Merge(m, src) +} +func (m *PlainTextChangeSnapshot) XXX_Size() int { + return m.Size() +} +func (m *PlainTextChangeSnapshot) XXX_DiscardUnknown() { + xxx_messageInfo_PlainTextChangeSnapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_PlainTextChangeSnapshot proto.InternalMessageInfo + +func (m *PlainTextChangeSnapshot) GetText() string { + if m != nil { + return m.Text + } + return "" +} + +type PlainTextChangeData struct { + Content []*PlainTextChangeContent `protobuf:"bytes,1,rep,name=content,proto3" json:"content,omitempty"` + Snapshot *PlainTextChangeSnapshot `protobuf:"bytes,2,opt,name=snapshot,proto3" json:"snapshot,omitempty"` +} + +func (m *PlainTextChangeData) Reset() { *m = PlainTextChangeData{} } +func (m *PlainTextChangeData) String() string { return proto.CompactTextString(m) } +func (*PlainTextChangeData) ProtoMessage() {} +func (*PlainTextChangeData) Descriptor() ([]byte, []int) { + return fileDescriptor_970a1e91eeb094c9, []int{0, 3} +} +func (m *PlainTextChangeData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PlainTextChangeData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PlainTextChangeData.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PlainTextChangeData) XXX_Merge(src proto.Message) { + xxx_messageInfo_PlainTextChangeData.Merge(m, src) +} +func (m *PlainTextChangeData) XXX_Size() int { + return m.Size() +} +func (m *PlainTextChangeData) XXX_DiscardUnknown() { + xxx_messageInfo_PlainTextChangeData.DiscardUnknown(m) +} + +var xxx_messageInfo_PlainTextChangeData proto.InternalMessageInfo + +func (m *PlainTextChangeData) GetContent() []*PlainTextChangeContent { + if m != nil { + return m.Content + } + return nil +} + +func (m *PlainTextChangeData) GetSnapshot() *PlainTextChangeSnapshot { + if m != nil { + return m.Snapshot + } + return nil +} + +func init() { + proto.RegisterType((*PlainTextChange)(nil), "anytype.PlainTextChange") + proto.RegisterType((*PlainTextChangeContent)(nil), "anytype.PlainTextChange.Content") + proto.RegisterType((*PlainTextChangeTextAppend)(nil), "anytype.PlainTextChange.TextAppend") + proto.RegisterType((*PlainTextChangeSnapshot)(nil), "anytype.PlainTextChange.Snapshot") + proto.RegisterType((*PlainTextChangeData)(nil), "anytype.PlainTextChange.Data") +} + +func init() { + proto.RegisterFile("data/pb/protos/plaintextchanges.proto", fileDescriptor_970a1e91eeb094c9) +} + +var fileDescriptor_970a1e91eeb094c9 = []byte{ + // 259 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0x49, 0x2c, 0x49, + 0xd4, 0x2f, 0x48, 0xd2, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x2f, 0xd6, 0x2f, 0xc8, 0x49, 0xcc, 0xcc, + 0x2b, 0x49, 0xad, 0x28, 0x49, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0x2d, 0xd6, 0x03, 0x8b, 0x0b, 0xb1, + 0x27, 0xe6, 0x55, 0x96, 0x54, 0x16, 0xa4, 0x2a, 0x6d, 0x62, 0xe2, 0xe2, 0x0f, 0x00, 0xa9, 0x09, + 0x49, 0xad, 0x28, 0x71, 0x06, 0xab, 0x91, 0x8a, 0xe4, 0x62, 0x77, 0xce, 0xcf, 0x2b, 0x49, 0xcd, + 0x2b, 0x11, 0x72, 0xe5, 0xe2, 0x02, 0x69, 0x76, 0x2c, 0x28, 0x48, 0xcd, 0x4b, 0x91, 0x60, 0x54, + 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd6, 0x83, 0x6a, 0xd6, 0x43, 0xd3, 0xa8, 0x17, 0x02, 0x57, 0xea, + 0xc1, 0x10, 0x84, 0xa4, 0xd1, 0x89, 0x9d, 0x8b, 0xb5, 0x2c, 0x31, 0xa7, 0x34, 0x55, 0x4a, 0x81, + 0x8b, 0x0b, 0xa1, 0x48, 0x48, 0x88, 0x8b, 0x05, 0xa4, 0x08, 0x6c, 0x2e, 0x67, 0x10, 0x98, 0x2d, + 0x25, 0xc7, 0xc5, 0x11, 0x9c, 0x97, 0x58, 0x50, 0x9c, 0x91, 0x5f, 0x82, 0x55, 0xbe, 0x91, 0x91, + 0x8b, 0xc5, 0x25, 0xb1, 0x24, 0x51, 0xc8, 0x8a, 0x8b, 0x3d, 0x19, 0xe2, 0x4a, 0x09, 0x46, 0x05, + 0x66, 0x0d, 0x6e, 0x23, 0x05, 0x9c, 0xee, 0x82, 0xfa, 0x26, 0x08, 0xa6, 0x41, 0xc8, 0x96, 0x8b, + 0xa3, 0x18, 0x6a, 0x89, 0x04, 0x13, 0xd8, 0x53, 0x8a, 0x38, 0x35, 0xc3, 0x5c, 0x13, 0x04, 0xd7, + 0xe2, 0x24, 0x73, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, + 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x4c, 0x05, 0x49, + 0x49, 0x6c, 0xe0, 0x20, 0x36, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x1e, 0xb0, 0x6a, 0x8b, + 0x01, 0x00, 0x00, +} + +func (m *PlainTextChange) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PlainTextChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlainTextChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *PlainTextChangeContent) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PlainTextChangeContent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlainTextChangeContent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Value != nil { + { + size := m.Value.Size() + i -= size + if _, err := m.Value.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *PlainTextChangeContentValueOfTextAppend) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlainTextChangeContentValueOfTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.TextAppend != nil { + { + size, err := m.TextAppend.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlaintextchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *PlainTextChangeTextAppend) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PlainTextChangeTextAppend) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlainTextChangeTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Text) > 0 { + i -= len(m.Text) + copy(dAtA[i:], m.Text) + i = encodeVarintPlaintextchanges(dAtA, i, uint64(len(m.Text))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PlainTextChangeSnapshot) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PlainTextChangeSnapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlainTextChangeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Text) > 0 { + i -= len(m.Text) + copy(dAtA[i:], m.Text) + i = encodeVarintPlaintextchanges(dAtA, i, uint64(len(m.Text))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PlainTextChangeData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PlainTextChangeData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlainTextChangeData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Snapshot != nil { + { + size, err := m.Snapshot.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlaintextchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Content) > 0 { + for iNdEx := len(m.Content) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Content[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlaintextchanges(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintPlaintextchanges(dAtA []byte, offset int, v uint64) int { + offset -= sovPlaintextchanges(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PlainTextChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *PlainTextChangeContent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Value != nil { + n += m.Value.Size() + } + return n +} + +func (m *PlainTextChangeContentValueOfTextAppend) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TextAppend != nil { + l = m.TextAppend.Size() + n += 1 + l + sovPlaintextchanges(uint64(l)) + } + return n +} +func (m *PlainTextChangeTextAppend) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Text) + if l > 0 { + n += 1 + l + sovPlaintextchanges(uint64(l)) + } + return n +} + +func (m *PlainTextChangeSnapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Text) + if l > 0 { + n += 1 + l + sovPlaintextchanges(uint64(l)) + } + return n +} + +func (m *PlainTextChangeData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Content) > 0 { + for _, e := range m.Content { + l = e.Size() + n += 1 + l + sovPlaintextchanges(uint64(l)) + } + } + if m.Snapshot != nil { + l = m.Snapshot.Size() + n += 1 + l + sovPlaintextchanges(uint64(l)) + } + return n +} + +func sovPlaintextchanges(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozPlaintextchanges(x uint64) (n int) { + return sovPlaintextchanges(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PlainTextChange) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PlainTextChange: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PlainTextChange: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipPlaintextchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlaintextchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Content: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TextAppend", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlaintextchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlaintextchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &PlainTextChangeTextAppend{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &PlainTextChangeContentValueOfTextAppend{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlaintextchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlaintextchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TextAppend: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TextAppend: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Text", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlaintextchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlaintextchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Text = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlaintextchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlaintextchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Snapshot: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Snapshot: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Text", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlaintextchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlaintextchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Text = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlaintextchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlaintextchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Data: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Data: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlaintextchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlaintextchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = append(m.Content, &PlainTextChangeContent{}) + if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Snapshot", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPlaintextchanges + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlaintextchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Snapshot == nil { + m.Snapshot = &PlainTextChangeSnapshot{} + } + if err := m.Snapshot.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlaintextchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPlaintextchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPlaintextchanges(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlaintextchanges + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthPlaintextchanges + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPlaintextchanges + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthPlaintextchanges + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthPlaintextchanges = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPlaintextchanges = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPlaintextchanges = fmt.Errorf("proto: unexpected end of group") +) diff --git a/data/pb/protos/aclchanges.proto b/data/pb/protos/aclchanges.proto index 35883b88..5b101b04 100644 --- a/data/pb/protos/aclchanges.proto +++ b/data/pb/protos/aclchanges.proto @@ -13,7 +13,6 @@ message ACLChange { uint64 currentReadKeyHash = 6; int64 timestamp = 7; string identity = 8; - map logHeads = 9; message ACLContentValue { oneof value { diff --git a/data/pb/protos/plaintextchanges.proto b/data/pb/protos/plaintextchanges.proto new file mode 100644 index 00000000..4b5e9d37 --- /dev/null +++ b/data/pb/protos/plaintextchanges.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package anytype; +option go_package = "pb"; + +message PlainTextChange { + message Content { + oneof value { + TextAppend textAppend = 1; + } + } + + message TextAppend { + string text = 1; + } + + message Snapshot { + string text = 1; + } + + message Data { + repeated Content content = 1; + Snapshot snapshot = 2; + } +} diff --git a/data/threadbuilder/invalidsnapshotexample.yml b/data/threadbuilder/invalidsnapshotexample.yml index 366a9ced..3a317ac0 100644 --- a/data/threadbuilder/invalidsnapshotexample.yml +++ b/data/threadbuilder/invalidsnapshotexample.yml @@ -1,87 +1,86 @@ thread: author: A -logs: - - id: A.1 +changes: + - id: A.1.1 identity: A - records: - - id: A.1.1 - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: B - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - permission: admin - snapshot: - blocks: - - id: root - aclChanges: - - userAdd: - identity: A - permission: admin - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - - userAdd: - identity: B - permission: admin - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: A.1.2 - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: B - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: D - encryptionKey: key.Enc.D - encryptedReadKeys: [ key.Read.1 ] - permission: admin - snapshot: - blocks: - - id: root - aclChanges: - - userAdd: - identity: D - permission: admin - encryptionKey: key.Enc.D - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: A.1.3 - aclChanges: - - userAdd: - identity: E - permission: admin - encryptionKey: key.Enc.E - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: B.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + - userAdd: + identity: B + permission: admin + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: A.1.2 + identity: A + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: D + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + permission: admin + snapshot: + blocks: + - id: root + aclChanges: + - userAdd: + identity: D + permission: admin + encryptionKey: key.Enc.D + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: A.1.3 + identity: A + aclChanges: + - userAdd: + identity: E + permission: admin + encryptionKey: key.Enc.E + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1.1 identity: B - records: - - id: B.1.1 - aclChanges: - - userAdd: - identity: C - permission: admin - encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 - - id: B.1.2 - aclChanges: - - userAdd: - identity: F - permission: admin - encryptionKey: key.Enc.F - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 + aclChanges: + - userAdd: + identity: C + permission: admin + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: B.1.2 + identity: B + aclChanges: + - userAdd: + identity: F + permission: admin + encryptionKey: key.Enc.F + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 keys: Enc: - A @@ -120,3 +119,6 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] +heads: + - A.1.3 + - B.1.2 diff --git a/data/threadbuilder/keychain.go b/data/threadbuilder/keychain.go index f048d1d6..69051c79 100644 --- a/data/threadbuilder/keychain.go +++ b/data/threadbuilder/keychain.go @@ -1,10 +1,11 @@ package threadbuilder import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/textileio/go-threads/crypto/symmetric" "hash/fnv" "strings" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/textileio/go-threads/crypto/symmetric" ) type SymKey struct { diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go index f391e8ad..45437383 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/data/threadbuilder/threadbuilder.go @@ -3,43 +3,36 @@ package threadbuilder import ( "context" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/core/smartblock" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/pb/model" - "github.com/gogo/protobuf/proto" "io/ioutil" - "sort" + + "github.com/gogo/protobuf/proto" + "gopkg.in/yaml.v3" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" ) -type threadRecord struct { +const plainTextDocType uint16 = 1 + +type threadChange struct { *pb.ACLChange id string - logId string readKey *SymKey signKey threadmodels.SigningPrivKey - prevRecord *threadRecord - changesData *pb.ACLChangeChangeData -} - -type threadLog struct { - id string - owner string - records []*threadRecord + changesData *pb.PlainTextChangeData } type ThreadBuilder struct { threadId string - logs map[string]*threadLog - allRecords map[string]*threadRecord + allChanges map[string]*threadChange + heads []string keychain *Keychain } func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { return &ThreadBuilder{ - logs: make(map[string]*threadLog), - allRecords: make(map[string]*threadRecord), + allChanges: make(map[string]*threadChange), keychain: keychain, } } @@ -70,27 +63,13 @@ func (t *ThreadBuilder) GetKeychain() *Keychain { return t.keychain } -func (t *ThreadBuilder) GetLogs() ([]threadmodels.ThreadLog, error) { - var logs []threadmodels.ThreadLog - for _, l := range t.logs { - logs = append(logs, threadmodels.ThreadLog{ - ID: l.id, - Head: l.records[len(l.records)-1].id, - Counter: int64(len(l.records)), - }) - } - sort.Slice(logs, func(i, j int) bool { - return logs[i].ID < logs[j].ID - }) - return logs, nil -} +// writer can create docs -> id can create writer permissions +// by id we can check who created +// at the same time this guy can add some random folks which are not in space +// but we should compare this against space in the future -func (t *ThreadBuilder) GetRecord(ctx context.Context, recordID string) (*threadmodels.ThreadRecord, error) { - rec := t.allRecords[recordID] - prevId := "" - if rec.prevRecord != nil { - prevId = rec.prevRecord.id - } +func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*threadmodels.RawChange, error) { + rec := t.allChanges[recordID] var encrypted []byte if rec.changesData != nil { @@ -117,19 +96,15 @@ func (t *ThreadBuilder) GetRecord(ctx context.Context, recordID string) (*thread panic("should be able to sign final acl message!") } - transformedRec := &threadmodels.ThreadRecord{ - PrevId: prevId, - Id: rec.id, - LogId: rec.logId, - Signed: &threadmodels.SignedPayload{ - Payload: aclMarshaled, - Signature: signature, - }, + transformedRec := &threadmodels.RawChange{ + Payload: aclMarshaled, + Signature: signature, + Id: recordID, } return transformedRec, nil } -func (t *ThreadBuilder) PushRecord(payload proto.Marshaler) (id string, err error) { +func (t *ThreadBuilder) PushChange(payload proto.Marshaler) (id string, err error) { panic("implement me") } @@ -139,63 +114,49 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { // the same thing is happening for the encryption keys t.keychain.ParseKeys(&thread.Keys) t.threadId = t.parseThreadId(thread.Description) - for _, l := range thread.Logs { - newLog := &threadLog{ - id: l.Id, - owner: t.keychain.GetIdentity(l.Identity), + for _, ch := range thread.Changes { + newChange := &threadChange{ + id: ch.Id, } - var records []*threadRecord - for _, r := range l.Records { - newRecord := &threadRecord{ - id: r.Id, - logId: newLog.id, + k := t.keychain.GetKey(ch.ReadKey).(*SymKey) + newChange.readKey = k + newChange.signKey = t.keychain.SigningKeys[ch.Identity] + aclChange := &pb.ACLChange{} + aclChange.Identity = newChange.Identity + if len(ch.AclChanges) > 0 || ch.AclSnapshot != nil { + aclChange.AclData = &pb.ACLChangeACLData{} + if ch.AclSnapshot != nil { + aclChange.AclData.AclSnapshot = t.parseACLSnapshot(ch.AclSnapshot) } - if len(records) > 0 { - newRecord.prevRecord = records[len(records)-1] + if ch.AclChanges != nil { + var aclChangeContents []*pb.ACLChangeACLContentValue + for _, ch := range ch.AclChanges { + aclChangeContent := t.parseACLChange(ch) + aclChangeContents = append(aclChangeContents, aclChangeContent) + } + aclChange.AclData.AclContent = aclChangeContents } - k := t.keychain.GetKey(r.ReadKey).(*SymKey) - newRecord.readKey = k - newRecord.signKey = t.keychain.SigningKeys[l.Identity] - - aclChange := &pb.ACLChange{} - aclChange.Identity = newLog.owner - if len(r.AclChanges) > 0 || r.AclSnapshot != nil { - aclChange.AclData = &pb.ACLChangeACLData{} - if r.AclSnapshot != nil { - aclChange.AclData.AclSnapshot = t.parseACLSnapshot(r.AclSnapshot) - } - if r.AclChanges != nil { - var aclChangeContents []*pb.ACLChangeACLContentValue - for _, ch := range r.AclChanges { - aclChangeContent := t.parseACLChange(ch) - aclChangeContents = append(aclChangeContents, aclChangeContent) - } - aclChange.AclData.AclContent = aclChangeContents - } - } - if len(r.Changes) > 0 || r.Snapshot != nil { - newRecord.changesData = &pb.ACLChangeChangeData{} - if r.Snapshot != nil { - newRecord.changesData.Snapshot = t.parseChangeSnapshot(r.Snapshot) - } - if len(r.Changes) > 0 { - var changeContents []*pb.ChangeContent - for _, ch := range r.Changes { - aclChangeContent := t.parseDocumentChange(ch) - changeContents = append(changeContents, aclChangeContent) - } - newRecord.changesData.Content = changeContents - } - } - aclChange.CurrentReadKeyHash = k.Hash - newRecord.ACLChange = aclChange - t.allRecords[newRecord.id] = newRecord - records = append(records, newRecord) } - newLog.records = records - t.logs[newLog.id] = newLog + if len(ch.Changes) > 0 || ch.Snapshot != nil { + newChange.changesData = &pb.PlainTextChangeData{} + if ch.Snapshot != nil { + newChange.changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) + } + if len(ch.Changes) > 0 { + var changeContents []*pb.PlainTextChangeContent + for _, ch := range ch.Changes { + aclChangeContent := t.parseDocumentChange(ch) + changeContents = append(changeContents, aclChangeContent) + } + newChange.changesData.Content = changeContents + } + } + aclChange.CurrentReadKeyHash = k.Hash + newChange.ACLChange = aclChange + t.allChanges[newChange.id] = newChange } t.parseGraph(thread) + t.parseHeads(thread) } func (t *ThreadBuilder) parseThreadId(description *ThreadDescription) string { @@ -203,7 +164,7 @@ func (t *ThreadBuilder) parseThreadId(description *ThreadDescription) string { panic("no author in thread") } key := t.keychain.SigningKeys[description.Author] - id, err := threadmodels.CreateACLThreadID(key.GetPublic(), smartblock.SmartBlockTypeWorkspace) + id, err := threadmodels.CreateACLThreadID(key.GetPublic(), plainTextDocType) if err != nil { panic(err) } @@ -211,16 +172,9 @@ func (t *ThreadBuilder) parseThreadId(description *ThreadDescription) string { return id.String() } -func (t *ThreadBuilder) parseChangeSnapshot(s *ChangeSnapshot) *pb.ChangeSnapshot { - data := &model.SmartBlockSnapshotBase{} - var blocks []*model.Block - for _, b := range s.Blocks { - modelBlock := &model.Block{Id: b.Id, ChildrenIds: b.ChildrenIds} - blocks = append(blocks, modelBlock) - } - data.Blocks = blocks - return &pb.ChangeSnapshot{ - Data: data, +func (t *ThreadBuilder) parseChangeSnapshot(s *PlainTextSnapshot) *pb.PlainTextChangeSnapshot { + return &pb.PlainTextChangeSnapshot{ + Text: s.Text, } } @@ -244,18 +198,16 @@ func (t *ThreadBuilder) parseACLSnapshot(s *ACLSnapshot) *pb.ACLChangeACLSnapsho } } -func (t *ThreadBuilder) parseDocumentChange(ch *DocumentChange) (convCh *pb.ChangeContent) { +func (t *ThreadBuilder) parseDocumentChange(ch *PlainTextChange) (convCh *pb.PlainTextChangeContent) { switch { - case ch.BlockAdd != nil: - blockAdd := ch.BlockAdd - - convCh = &pb.ChangeContent{ - Value: &pb.ChangeContentValueOfBlockCreate{ - BlockCreate: &pb.ChangeBlockCreate{ - TargetId: blockAdd.TargetId, - Position: model.Block_Inner, - Blocks: []*model.Block{&model.Block{Id: blockAdd.Id}}, - }}} + case ch.TextAppend != nil: + convCh = &pb.PlainTextChangeContent{ + Value: &pb.PlainTextChangeContentValueOfTextAppend{ + TextAppend: &pb.PlainTextChangeTextAppend{ + Text: ch.TextAppend.Text, + }, + }, + } } if convCh == nil { panic("cannot have empty document change") @@ -410,25 +362,24 @@ func (t *ThreadBuilder) convertPermission(perm string) pb.ACLChangeUserPermissio } } -func (t *ThreadBuilder) traverseFromHeads(f func(t *threadRecord) error) error { - allLogs, err := t.GetLogs() - if err != nil { - return err - } +func (t *ThreadBuilder) traverseFromHeads(f func(t *threadChange) error) error { + uniqMap := map[string]struct{}{} + stack := t.heads + for len(stack) > 0 { + id := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if _, exists := uniqMap[id]; exists { + continue + } - for _, log := range allLogs { - head := t.allRecords[log.Head] - err = f(head) - if err != nil { + ch := t.allChanges[id] + uniqMap[id] = struct{}{} + if err := f(ch); err != nil { return err } - for head.prevRecord != nil { - head = head.prevRecord - err = f(head) - if err != nil { - return err - } + for _, prev := range ch.ACLChange.TreeHeadIds { + stack = append(stack, prev) } } return nil @@ -436,9 +387,13 @@ func (t *ThreadBuilder) traverseFromHeads(f func(t *threadRecord) error) error { func (t *ThreadBuilder) parseGraph(thread *YMLThread) { for _, node := range thread.Graph { - rec := t.allRecords[node.Id] + rec := t.allChanges[node.Id] rec.AclHeadIds = node.ACLHeads rec.TreeHeadIds = node.TreeHeads rec.SnapshotBaseId = node.BaseSnapshot } } + +func (t *ThreadBuilder) parseHeads(thread *YMLThread) { + t.heads = thread.Heads +} diff --git a/data/threadbuilder/threadbuildergraph_nix.go b/data/threadbuilder/threadbuildergraph_nix.go index 75c7e06d..1ea60a5a 100644 --- a/data/threadbuilder/threadbuildergraph_nix.go +++ b/data/threadbuilder/threadbuildergraph_nix.go @@ -30,7 +30,7 @@ func (t *ThreadBuilder) Graph() (string, error) { graph.SetDir(true) var nodes = make(map[string]struct{}) - var addNodes = func(r *threadRecord) error { + var addNodes = func(r *threadChange) error { // TODO: revisit function after checking style := "solid" @@ -105,7 +105,7 @@ func (t *ThreadBuilder) Graph() (string, error) { return nil } - var addLinks = func(t *threadRecord) error { + var addLinks = func(t *threadChange) error { for _, prevId := range t.AclHeadIds { err := createEdge(t.id, prevId, EdgeParameters{ style: "dashed", diff --git a/data/threadbuilder/ymlentities.go b/data/threadbuilder/ymlentities.go index 771bbf27..3ef03f05 100644 --- a/data/threadbuilder/ymlentities.go +++ b/data/threadbuilder/ymlentities.go @@ -20,11 +20,8 @@ type ACLSnapshot struct { } `yaml:"userStates"` } -type ChangeSnapshot struct { - Blocks []struct { - Id string `yaml:"id"` - ChildrenIds []string `yaml:"childrenIds"` - } `yaml:"blocks"` +type PlainTextSnapshot struct { + Text string `yaml:"text"` } type ACLChange struct { @@ -67,29 +64,25 @@ type ACLChange struct { } } -type DocumentChange struct { - BlockAdd *struct { - Id string `yaml:"id"` - TargetId string `yaml:"targetId"` +type PlainTextChange struct { + TextAppend *struct { + Text string `yaml:"text"` } `yaml:"blockAdd"` } type YMLThread struct { Description *ThreadDescription `yaml:"thread"` - Logs []struct { + Changes []struct { Id string `yaml:"id"` Identity string `yaml:"identity"` - Records []struct { - Id string `yaml:"id"` - AclSnapshot *ACLSnapshot `yaml:"aclSnapshot"` - Snapshot *ChangeSnapshot `yaml:"snapshot"` - AclChanges []*ACLChange `yaml:"aclChanges"` - Changes []*DocumentChange `yaml:"changes"` + AclSnapshot *ACLSnapshot `yaml:"aclSnapshot"` + Snapshot *PlainTextSnapshot `yaml:"snapshot"` + AclChanges []*ACLChange `yaml:"aclChanges"` + Changes []*PlainTextChange `yaml:"changes"` - ReadKey string `yaml:"readKey"` - } `yaml:"records"` - } `yaml:"logs"` + ReadKey string `yaml:"readKey"` + } `yaml:"changes"` Keys Keys `yaml:"keys"` @@ -100,4 +93,6 @@ type YMLThread struct { ACLHeads []string `yaml:"aclHeads"` TreeHeads []string `yaml:"treeHeads"` } `yaml:"graph"` + + Heads []string `yaml:"heads"` } diff --git a/data/threadmodels/models.go b/data/threadmodels/models.go index a9ef39cc..a0d3f8d7 100644 --- a/data/threadmodels/models.go +++ b/data/threadmodels/models.go @@ -2,33 +2,17 @@ package threadmodels import ( "context" - "github.com/gogo/protobuf/proto" ) type Thread interface { ID() string - GetLogs() ([]ThreadLog, error) - GetRecord(ctx context.Context, recordID string) (*ThreadRecord, error) - PushRecord(payload proto.Marshaler) (id string, err error) - - // SubscribeForRecords() + GetChange(ctx context.Context, recordID string) (*RawChange, error) + PushChange(payload proto.Marshaler) (id string, err error) } -type SignedPayload struct { +type RawChange struct { Payload []byte Signature []byte -} - -type ThreadRecord struct { - PrevId string - Id string - LogId string - Signed *SignedPayload -} - -type ThreadLog struct { - ID string - Head string - Counter int64 + Id string } diff --git a/data/threadmodels/threadid.go b/data/threadmodels/threadid.go index 4d2f0392..c2bfe178 100644 --- a/data/threadmodels/threadid.go +++ b/data/threadmodels/threadid.go @@ -4,12 +4,12 @@ import ( "crypto/rand" "encoding/binary" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/core/smartblock" - "github.com/textileio/go-threads/core/thread" "hash/fnv" + + "github.com/textileio/go-threads/core/thread" ) -func CreateACLThreadID(k SigningPubKey, blockType smartblock.SmartBlockType) (thread.ID, error) { +func CreateACLThreadID(k SigningPubKey, docType uint16) (thread.ID, error) { rndlen := 32 buf := make([]byte, 8+rndlen) @@ -31,7 +31,7 @@ func CreateACLThreadID(k SigningPubKey, blockType smartblock.SmartBlockType) (th // putting hash of the pubkey in the beginning binary.LittleEndian.PutUint64(buf[:8], res) - return threadIDFromBytes(blockType, buf) + return threadIDFromBytes(docType, buf) } func VerifyACLThreadID(k SigningPubKey, threadId thread.ID) (bool, error) { @@ -52,7 +52,7 @@ func VerifyACLThreadID(k SigningPubKey, threadId thread.ID) (bool, error) { } func threadIDFromBytes( - blockType smartblock.SmartBlockType, + docType uint16, b []byte) (thread.ID, error) { blen := len(b) @@ -60,7 +60,7 @@ func threadIDFromBytes( buf := make([]byte, 2*binary.MaxVarintLen64+blen) n := binary.PutUvarint(buf, thread.V1) n += binary.PutUvarint(buf[n:], uint64(thread.AccessControlled)) - n += binary.PutUvarint(buf[n:], uint64(blockType)) + n += binary.PutUvarint(buf[n:], uint64(docType)) cn := copy(buf[n:], b) if cn != blen { diff --git a/data/treebuilder.go b/data/treebuilder.go index 0a226d70..b7d17577 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -4,8 +4,8 @@ import ( "context" "errors" "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" @@ -48,7 +48,7 @@ func (tb *TreeBuilder) loadChange(id string) (ch *Change, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - record, err := tb.thread.GetRecord(ctx, id) + record, err := tb.thread.GetChange(ctx, id) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index dd9e3e9e..45eaba98 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.18 require ( github.com/textileio/go-threads v1.0.2-0.20210304072541-d0f91da84404 + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) replace github.com/textileio/go-threads => github.com/anytypeio/go-threads v1.1.0-rc1.0.20220223104843-a67245cee80e From dc9f6f3917a2ab70540f843d2523f31bdb210a9d Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 1 Jul 2022 10:54:47 +0200 Subject: [PATCH 04/53] Add some deps for threadbuilder --- data/threadmodels/keys.go | 5 +- util/crc16/crc16.go | 122 ++++++++++++++++++++++++++++++++++++++ util/strkey/strkey.go | 118 ++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 util/crc16/crc16.go create mode 100644 util/strkey/strkey.go diff --git a/data/threadmodels/keys.go b/data/threadmodels/keys.go index 95a7254b..deb7967b 100644 --- a/data/threadmodels/keys.go +++ b/data/threadmodels/keys.go @@ -7,10 +7,11 @@ import ( "crypto/subtle" "crypto/x509" "errors" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/strkey" + "io" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/strkey" "github.com/libp2p/go-libp2p-core/crypto" crypto_pb "github.com/libp2p/go-libp2p-core/crypto/pb" - "io" ) type SigningPubKey crypto.PubKey diff --git a/util/crc16/crc16.go b/util/crc16/crc16.go new file mode 100644 index 00000000..810f4bed --- /dev/null +++ b/util/crc16/crc16.go @@ -0,0 +1,122 @@ +// Package crc16 is implementation according to CCITT standards. +// +// Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the +// following parameters: +// +// Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" +// Width : 16 bit +// Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) +// Initialization : 0000 +// Reflect Input byte : False +// Reflect Output CRC : False +// Xor constant to output CRC : 0000 +// Output for "123456789" : 31C3 +// +// ported from the c++ code in the stellar-core codebase +// (https://github.com/stellar/stellar-core). The code is licensed +// as: +/* + * Copyright 2001-2010 Georges Menie (www.menie.org) + * Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style) + * Copyright 2015 Stellar Development Foundation (ported to go) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package crc16 + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +// ErrInvalidChecksum is returned when Validate determines either the checksum +// or the payload has been corrupted +var ErrInvalidChecksum = fmt.Errorf("invalid checksum") + +var crc16tab = [256]uint16{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +} + +// Checksum returns the 2-byte checksum for the provided data +func Checksum(data []byte) []byte { + var crc uint16 + var out bytes.Buffer + for _, b := range data { + crc = ((crc << 8) & 0xffff) ^ crc16tab[((crc>>8)^uint16(b))&0x00FF] + } + + err := binary.Write(&out, binary.LittleEndian, crc) + if err != nil { + panic(err) + } + + return out.Bytes() +} + +// Validate returns an error if the provided checksum does not match +// the calculated checksum of the provided data +func Validate(data []byte, expected []byte) error { + + actual := Checksum(data) + + // validate the provided checksum against the calculated + if !bytes.Equal(actual, expected) { + return ErrInvalidChecksum + } + + return nil +} diff --git a/util/strkey/strkey.go b/util/strkey/strkey.go new file mode 100644 index 00000000..0e766f12 --- /dev/null +++ b/util/strkey/strkey.go @@ -0,0 +1,118 @@ +package strkey + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/mr-tron/base58/base58" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/crc16" +) + +// ErrInvalidVersionByte is returned when the version byte from a provided +// strkey-encoded string is not one of the valid values. +var ErrInvalidVersionByte = fmt.Errorf("invalid version byte") + +// VersionByte represents one of the possible prefix values for a StrKey base +// string--the string the when encoded using base58 yields a final StrKey. +type VersionByte byte + +// Decode decodes the provided StrKey into a raw value, checking the checksum +// and ensuring the expected VersionByte (the version parameter) is the value +// actually encoded into the provided src string. +func Decode(expected VersionByte, src string) ([]byte, error) { + raw, err := decodeString(src) + if err != nil { + return nil, err + } + + // decode into components + version := VersionByte(raw[0]) + vp := raw[0 : len(raw)-2] + payload := raw[1 : len(raw)-2] + checksum := raw[len(raw)-2:] + + // ensure version byte is expected + if version != expected { + return nil, ErrInvalidVersionByte + } + + // ensure checksum is valid + if err := crc16.Validate(vp, checksum); err != nil { + return nil, err + } + + // if we made it through the gaunlet, return the decoded value + return payload, nil +} + +// MustDecode is like Decode, but panics on error +func MustDecode(expected VersionByte, src string) []byte { + d, err := Decode(expected, src) + if err != nil { + panic(err) + } + return d +} + +// Encode encodes the provided data to a StrKey, using the provided version +// byte. +func Encode(version VersionByte, src []byte) (string, error) { + var raw bytes.Buffer + + // write version byte + if err := binary.Write(&raw, binary.LittleEndian, version); err != nil { + return "", err + } + + // write payload + if _, err := raw.Write(src); err != nil { + return "", err + } + + // calculate and write checksum + checksum := crc16.Checksum(raw.Bytes()) + if _, err := raw.Write(checksum); err != nil { + return "", err + } + + result := base58.FastBase58Encoding(raw.Bytes()) + return result, nil +} + +// MustEncode is like Encode, but panics on error +func MustEncode(version VersionByte, src []byte) string { + e, err := Encode(version, src) + if err != nil { + panic(err) + } + return e +} + +// Version extracts and returns the version byte from the provided source +// string. +func Version(src string) (VersionByte, error) { + raw, err := decodeString(src) + if err != nil { + return VersionByte(0), err + } + + return VersionByte(raw[0]), nil +} + +// decodeString decodes a base58 string into the raw bytes, and ensures it could +// potentially be strkey encoded (i.e. it has both a version byte and a +// checksum, neither of which are explicitly checked by this func) +func decodeString(src string) ([]byte, error) { + raw, err := base58.FastBase58Decoding(src) + if err != nil { + return nil, fmt.Errorf("base58 decode failed: %s", err) + } + + if len(raw) < 3 { + return nil, fmt.Errorf("encoded value is %d bytes; minimum valid length is 3", len(raw)) + } + + return raw, nil +} From 64eb6581e3d1e3d968e52ad5f192ad0c63ba1dc5 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 1 Jul 2022 11:39:42 +0200 Subject: [PATCH 05/53] Further refactoring of ACL logic --- data/aclstate.go | 2 +- data/aclstatebuilder.go | 2 +- data/acltreebuilder.go | 88 ++------------------ data/change.go | 13 +-- data/threadbuilder/threadbuilder.go | 4 + data/threadmodels/models.go | 2 + data/threadmodels/threadid_test.go | 3 +- data/treebuilder.go | 45 ++--------- util/slice/slice.go | 120 ++++++++++++++++++++++++++++ 9 files changed, 143 insertions(+), 136 deletions(-) create mode 100644 util/slice/slice.go diff --git a/data/aclstate.go b/data/aclstate.go index 4bf0b200..5a7a5cf8 100644 --- a/data/aclstate.go +++ b/data/aclstate.go @@ -89,7 +89,7 @@ func (st *ACLState) ApplyChange(changeId string, change *pb.ACLChange) error { for _, ch := range change.GetAclData().GetAclContent() { if err := st.applyChange(changeId, ch); err != nil { - log.Infof("error while applying changes: %v; ignore", err) + //log.Infof("error while applying changes: %v; ignore", err) return err } } diff --git a/data/aclstatebuilder.go b/data/aclstatebuilder.go index d80d07b8..5aefc766 100644 --- a/data/aclstatebuilder.go +++ b/data/aclstatebuilder.go @@ -82,7 +82,7 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*ACLState, bool, error) if err == nil { startChange = c } else if err != ErrDocumentForbidden { - log.Errorf("marking change %s as invalid: %v", c.Id, err) + //log.Errorf("marking change %s as invalid: %v", c.Id, err) sb.tree.RemoveInvalidChange(c.Id) } }() diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index 7ce2f681..0fe3e347 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -5,10 +5,8 @@ import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/core/thread" - "sort" "time" ) @@ -41,7 +39,7 @@ func (tb *ACLTreeBuilder) loadChange(id string) (ch *Change, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - record, err := tb.thread.GetChange(ctx, id) + change, err := tb.thread.GetChange(ctx, id) if err != nil { return nil, err } @@ -49,11 +47,11 @@ func (tb *ACLTreeBuilder) loadChange(id string) (ch *Change, 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 - if err = proto.Unmarshal(record.Signed.Payload, aclChange); err != nil { + if err = proto.Unmarshal(change.Payload, aclChange); err != nil { return } var verified bool - verified, err = tb.verify(aclChange.Identity, record.Signed.Payload, record.Signed.Signature) + verified, err = tb.verify(aclChange.Identity, change.Payload, change.Signature) if err != nil { return } @@ -80,44 +78,10 @@ func (tb *ACLTreeBuilder) verify(identity string, payload, signature []byte) (is return identityKey.Verify(payload, signature) } -func (tb *ACLTreeBuilder) getLogs() (logs []threadmodels.ThreadLog, err error) { - // TODO: Add beforeId building logic - logs, err = tb.thread.GetLogs() - if err != nil { - return nil, fmt.Errorf("GetLogs error: %w", err) - } - - log.Debugf("build tree: logs: %v", logs) - if len(logs) == 0 || len(logs) == 1 && len(logs[0].Head) <= 1 { - return nil, ErrEmpty - } - var nonEmptyLogs = logs[:0] - for _, l := range logs { - if len(l.Head) == 0 { - continue - } - if ch, err := tb.loadChange(l.Head); err != nil { - log.Errorf("loading head %s of the log %s failed: %v", l.Head, l.ID, err) - } else { - tb.logHeads[l.ID] = ch - } - nonEmptyLogs = append(nonEmptyLogs, l) - } - return nonEmptyLogs, nil -} - func (tb *ACLTreeBuilder) Build() (*Tree, error) { - logs, err := tb.getLogs() - if err != nil { - return nil, err - } + heads := tb.thread.Heads() - heads, err := tb.getACLHeads(logs) - if err != nil { - return nil, fmt.Errorf("get acl heads error: %v", err) - } - - if err = tb.buildTreeFromStart(heads); err != nil { + if err := tb.buildTreeFromStart(heads); err != nil { return nil, fmt.Errorf("buildTree error: %v", err) } tb.cache = nil @@ -169,48 +133,6 @@ func (tb *ACLTreeBuilder) dfsFromStart(stack []string) (buf []*Change, possibleR return buf, possibleRoots, nil } -func (tb *ACLTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) { - headChange, err := tb.loadChange(head) - if err != nil { - return nil, err - } - - if headChange.Content.GetAclData() != nil { - return []string{head}, nil - } else { - return headChange.PreviousIds, nil - } -} - -func (tb *ACLTreeBuilder) getACLHeads(logs []threadmodels.ThreadLog) (aclTreeHeads []string, err error) { - sort.Slice(logs, func(i, j int) bool { - return logs[i].ID < logs[j].ID - }) - - // get acl tree heads from log heads - for _, l := range logs { - if slice.FindPos(aclTreeHeads, l.Head) != -1 { // do not scan known heads - continue - } - precedingHeads, err := tb.getPrecedingACLHeads(l.Head) - if err != nil { - return nil, err - } - - for _, head := range precedingHeads { - if slice.FindPos(aclTreeHeads, l.Head) != -1 { - continue - } - aclTreeHeads = append(aclTreeHeads, head) - } - } - - if len(aclTreeHeads) == 0 { - return nil, fmt.Errorf("no usable ACL heads in thread") - } - return aclTreeHeads, nil -} - func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { threadId, err := thread.Decode(tb.thread.ID()) if err != nil { diff --git a/data/change.go b/data/change.go index ede8f712..eabc4128 100644 --- a/data/change.go +++ b/data/change.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/crypto/symmetric" ) @@ -15,9 +14,8 @@ type Change struct { PreviousIds []string Id string SnapshotId string - LogHeads map[string]string IsSnapshot bool - DecryptedDocumentChange *pb.ACLChangeChangeData + DecryptedDocumentChange []byte Content *pb.ACLChange } @@ -27,17 +25,12 @@ func (ch *Change) DecryptContents(key *symmetric.Key) error { return nil } - var changesData pb.ACLChangeChangeData decrypted, err := key.Decrypt(ch.Content.ChangesData) if err != nil { return fmt.Errorf("failed to decrypt changes data: %w", err) } - err = proto.Unmarshal(decrypted, &changesData) - if err != nil { - return fmt.Errorf("failed to umarshall into ChangesData: %w", err) - } - ch.DecryptedDocumentChange = &changesData + ch.DecryptedDocumentChange = decrypted return nil } @@ -53,7 +46,6 @@ func NewChange(id string, ch *pb.ACLChange) (*Change, error) { Content: ch, SnapshotId: ch.SnapshotBaseId, IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - LogHeads: ch.GetLogHeads(), }, nil } @@ -65,6 +57,5 @@ func NewACLChange(id string, ch *pb.ACLChange) (*Change, error) { Content: ch, SnapshotId: ch.SnapshotBaseId, IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - LogHeads: ch.GetLogHeads(), }, nil } diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go index 45437383..db3e35d1 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/data/threadbuilder/threadbuilder.go @@ -68,6 +68,10 @@ func (t *ThreadBuilder) GetKeychain() *Keychain { // at the same time this guy can add some random folks which are not in space // but we should compare this against space in the future +func (t *ThreadBuilder) Heads() []string { + return t.heads +} + func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*threadmodels.RawChange, error) { rec := t.allChanges[recordID] diff --git a/data/threadmodels/models.go b/data/threadmodels/models.go index a0d3f8d7..aa388596 100644 --- a/data/threadmodels/models.go +++ b/data/threadmodels/models.go @@ -7,6 +7,8 @@ import ( type Thread interface { ID() string + Heads() []string + // TODO: add ACL heads GetChange(ctx context.Context, recordID string) (*RawChange, error) PushChange(payload proto.Marshaler) (id string, err error) } diff --git a/data/threadmodels/threadid_test.go b/data/threadmodels/threadid_test.go index c6516aae..df562a77 100644 --- a/data/threadmodels/threadid_test.go +++ b/data/threadmodels/threadid_test.go @@ -1,7 +1,6 @@ package threadmodels import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/core/smartblock" "testing" ) @@ -11,7 +10,7 @@ func TestCreateACLThreadIDVerify(t *testing.T) { t.Fatalf("should not return error after generating key pair: %v", err) } - thread, err := CreateACLThreadID(pubKey, smartblock.SmartBlockTypeWorkspace) + thread, err := CreateACLThreadID(pubKey, 1) if err != nil { t.Fatalf("should not return error after generating thread: %v", err) } diff --git a/data/treebuilder.go b/data/treebuilder.go index b7d17577..4e76f27c 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -6,16 +6,17 @@ import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" + //"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" + "github.com/prometheus/common/log" "github.com/textileio/go-threads/core/thread" "sort" "time" ) var ( - log = logging.Logger("anytype-data") + //log = logging.Logger("anytype-data") ErrEmpty = errors.New("logs empty") ) @@ -48,7 +49,7 @@ func (tb *TreeBuilder) loadChange(id string) (ch *Change, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - record, err := tb.thread.GetChange(ctx, id) + change, err := tb.thread.GetChange(ctx, id) if err != nil { return nil, err } @@ -56,11 +57,11 @@ func (tb *TreeBuilder) loadChange(id string) (ch *Change, 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 - if err = proto.Unmarshal(record.Signed.Payload, aclChange); err != nil { + if err = proto.Unmarshal(change.Payload, aclChange); err != nil { return } var verified bool - verified, err = tb.verify(aclChange.Identity, record.Signed.Payload, record.Signed.Signature) + verified, err = tb.verify(aclChange.Identity, change.Payload, change.Signature) if err != nil { return } @@ -87,40 +88,8 @@ func (tb *TreeBuilder) verify(identity string, payload, signature []byte) (isVer return identityKey.Verify(payload, signature) } -func (tb *TreeBuilder) getLogs() (logs []threadmodels.ThreadLog, err error) { - // TODO: Add beforeId building logic - logs, err = tb.thread.GetLogs() - if err != nil { - return nil, fmt.Errorf("GetLogs error: %w", err) - } - - log.Debugf("build tree: logs: %v", logs) - if len(logs) == 0 || len(logs) == 1 && len(logs[0].Head) <= 1 { - return nil, ErrEmpty - } - var nonEmptyLogs = logs[:0] - for _, l := range logs { - if len(l.Head) == 0 { - continue - } - if ch, err := tb.loadChange(l.Head); err != nil { - log.Errorf("loading head %s of the log %s failed: %v", l.Head, l.ID, err) - } else { - tb.logHeads[l.ID] = ch - } - nonEmptyLogs = append(nonEmptyLogs, l) - } - return nonEmptyLogs, nil -} - func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) { - logs, err := tb.getLogs() - if err != nil { - return nil, err - } - - // TODO: check if this should be changed if we are building from start - heads, err := tb.getActualHeads(logs) + heads, err := tb.getActualHeads() if err != nil { return nil, fmt.Errorf("get acl heads error: %v", err) } diff --git a/util/slice/slice.go b/util/slice/slice.go new file mode 100644 index 00000000..cc7e9067 --- /dev/null +++ b/util/slice/slice.go @@ -0,0 +1,120 @@ +package slice + +import ( + "hash/fnv" + "math/rand" + "sort" +) + +func DifferenceRemovedAdded(a, b []string) (removed []string, added []string) { + var amap = map[string]struct{}{} + var bmap = map[string]struct{}{} + + for _, item := range a { + amap[item] = struct{}{} + } + + for _, item := range b { + if _, exists := amap[item]; !exists { + added = append(added, item) + } + bmap[item] = struct{}{} + } + + for _, item := range a { + if _, exists := bmap[item]; !exists { + removed = append(removed, item) + } + } + return +} + +func FindPos(s []string, v string) int { + for i, sv := range s { + if sv == v { + return i + } + } + return -1 +} + +// Difference returns the elements in `a` that aren't in `b`. +func Difference(a, b []string) []string { + var diff = make([]string, 0, len(a)) + for _, a1 := range a { + if FindPos(b, a1) == -1 { + diff = append(diff, a1) + } + } + return diff +} + +func Insert(s []string, pos int, v ...string) []string { + if len(s) <= pos { + return append(s, v...) + } + if pos == 0 { + return append(v, s[pos:]...) + } + return append(s[:pos], append(v, s[pos:]...)...) +} + +// Remove reuses provided slice capacity. Provided s slice should not be used after without reassigning to the func return! +func Remove(s []string, v string) []string { + var n int + for _, x := range s { + if x != v { + s[n] = x + n++ + } + } + return s[:n] +} + +func Filter(vals []string, cond func(string) bool) []string { + var result = make([]string, 0, len(vals)) + for i := range vals { + if cond(vals[i]) { + result = append(result, vals[i]) + } + } + return result +} + +func GetRandomString(s []string, seed string) string { + rand.Seed(int64(hash(seed))) + return s[rand.Intn(len(s))] +} + +func hash(s string) uint64 { + h := fnv.New64a() + h.Write([]byte(s)) + return h.Sum64() +} + +func SortedEquals(s1, s2 []string) bool { + if len(s1) != len(s2) { + return false + } + for i := range s1 { + if s1[i] != s2[i] { + return false + } + } + return true +} + +func UnsortedEquals(s1, s2 []string) bool { + if len(s1) != len(s2) { + return false + } + + s1Sorted := make([]string, len(s1)) + s2Sorted := make([]string, len(s2)) + copy(s1Sorted, s1) + copy(s2Sorted, s2) + sort.Strings(s1Sorted) + sort.Strings(s2Sorted) + + return SortedEquals(s1Sorted, s2Sorted) +} From e5b3b7a3c006fb9238417791b8430af7b34bcd0e Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sat, 2 Jul 2022 22:34:50 +0200 Subject: [PATCH 06/53] Some more fixes for trees and stuff --- data/acltreebuilder.go | 44 +++++++++++++++++++++++++++++- data/treebuilder.go | 61 ++---------------------------------------- data/treegraph_nix.go | 28 ++++++++++--------- go.mod | 1 + 4 files changed, 61 insertions(+), 73 deletions(-) diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index 0fe3e347..f97aa805 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/core/thread" "time" @@ -80,8 +81,12 @@ func (tb *ACLTreeBuilder) verify(identity string, payload, signature []byte) (is func (tb *ACLTreeBuilder) Build() (*Tree, error) { heads := tb.thread.Heads() + aclHeads, err := tb.getACLHeads(heads) + if err != nil { + return nil, err + } - if err := tb.buildTreeFromStart(heads); err != nil { + if err = tb.buildTreeFromStart(aclHeads); err != nil { return nil, fmt.Errorf("buildTree error: %v", err) } tb.cache = nil @@ -157,3 +162,40 @@ 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) { + for _, head := range heads { + if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads + continue + } + precedingHeads, err := tb.getPrecedingACLHeads(head) + if err != nil { + return nil, err + } + + for _, aclHead := range precedingHeads { + if slice.FindPos(aclTreeHeads, aclHead) != -1 { + continue + } + aclTreeHeads = append(aclTreeHeads, aclHead) + } + } + + if len(aclTreeHeads) == 0 { + return nil, fmt.Errorf("no usable ACL heads in thread") + } + return aclTreeHeads, nil +} + +func (tb *ACLTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) { + headChange, err := tb.loadChange(head) + if err != nil { + return nil, err + } + + if headChange.Content.GetAclData() != nil { + return []string{head}, nil + } else { + return headChange.PreviousIds, nil + } +} diff --git a/data/treebuilder.go b/data/treebuilder.go index 4e76f27c..1d04804b 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -11,7 +11,6 @@ import ( "github.com/gogo/protobuf/proto" "github.com/prometheus/common/log" "github.com/textileio/go-threads/core/thread" - "sort" "time" ) @@ -89,13 +88,10 @@ func (tb *TreeBuilder) verify(identity string, payload, signature []byte) (isVer } func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) { - heads, err := tb.getActualHeads() - if err != nil { - return nil, fmt.Errorf("get acl heads error: %v", err) - } + heads := tb.thread.Heads() if fromStart { - if err = tb.buildTreeFromStart(heads); err != nil { + if err := tb.buildTreeFromStart(heads); err != nil { return nil, fmt.Errorf("buildTree error: %v", err) } } else { @@ -195,59 +191,6 @@ func (tb *TreeBuilder) dfs(stack []string, breakpoint string) (buf []*Change, er return buf, nil } -func (tb *TreeBuilder) getActualHeads(logs []threadmodels.ThreadLog) (heads []string, err error) { - sort.Slice(logs, func(i, j int) bool { - return logs[i].ID < logs[j].ID - }) - var knownHeads []string - var validLogs = logs[:0] - for _, l := range logs { - if slice.FindPos(knownHeads, l.Head) != -1 { // do not scan known heads - continue - } - sh, err := tb.getNearSnapshot(l.Head) - if err != nil { - log.Warnf("can't get near snapshot: %v; ignore", err) - continue - } - if sh.LogHeads != nil { - for _, headId := range sh.LogHeads { - knownHeads = append(knownHeads, headId) - } - } - validLogs = append(validLogs, l) - } - for _, l := range validLogs { - if slice.FindPos(knownHeads, l.Head) != -1 { // do not scan known heads - continue - } else { - heads = append(heads, l.Head) - } - } - if len(heads) == 0 { - return nil, fmt.Errorf("no usable logs in head") - } - return -} - -func (tb *TreeBuilder) getNearSnapshot(id string) (sh *Change, err error) { - ch, err := tb.loadChange(id) - if err != nil { - return - } - - if ch.IsSnapshot { - sh = ch - } else { - sh, err = tb.loadChange(ch.SnapshotId) - if err != nil { - return nil, err - } - } - - return sh, nil -} - func (tb *TreeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) { var ( ch *Change diff --git a/data/treegraph_nix.go b/data/treegraph_nix.go index b06f7435..0adbb58f 100644 --- a/data/treegraph_nix.go +++ b/data/treegraph_nix.go @@ -10,11 +10,12 @@ package data import ( "bytes" "fmt" - "github.com/goccy/go-graphviz" - "github.com/goccy/go-graphviz/cgraph" "strings" "time" "unicode" + + "github.com/goccy/go-graphviz" + "github.com/goccy/go-graphviz/cgraph" ) func (t *Tree) Graph() (data string, err error) { @@ -70,17 +71,18 @@ func (t *Tree) Graph() (data string, err error) { } } if c.DecryptedDocumentChange != nil { - for _, chc := range c.DecryptedDocumentChange.Content { - tp := fmt.Sprintf("%T", chc.Value) - tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) - res := "" - for _, ts := range tp { - if unicode.IsUpper(ts) { - res += string(ts) - } - } - chSymbs = append(chSymbs, res) - } + // TODO: add some parser to provide custom unmarshalling for the document change + //for _, chc := range c.DecryptedDocumentChange.Content { + // tp := fmt.Sprintf("%T", chc.Value) + // tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) + // res := "" + // for _, ts := range tp { + // if unicode.IsUpper(ts) { + // res += string(ts) + // } + // } + // chSymbs = append(chSymbs, res) + //} } shortId := c.Id diff --git a/go.mod b/go.mod index 45eaba98..bcbed4af 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/textileio/go-threads v1.0.2-0.20210304072541-d0f91da84404 gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c + github.com/goccy/go-graphviz v0.0.9 ) replace github.com/textileio/go-threads => github.com/anytypeio/go-threads v1.1.0-rc1.0.20220223104843-a67245cee80e From e970fa1d4dbf8eeb0568ecd725c750d9cc39bdac Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 3 Jul 2022 16:38:48 +0200 Subject: [PATCH 07/53] Make doc state building more abstract --- data/docstate.go | 9 + data/documentstatebuilder.go | 61 +- data/threadhelpers.go | 11 +- go.mod | 49 +- go.sum | 1351 ++++++++++++++++++++++++++++++++++ 5 files changed, 1436 insertions(+), 45 deletions(-) create mode 100644 data/docstate.go create mode 100644 go.sum diff --git a/data/docstate.go b/data/docstate.go new file mode 100644 index 00000000..693319d8 --- /dev/null +++ b/data/docstate.go @@ -0,0 +1,9 @@ +package data + +type DocumentState interface { + ApplyChange(change []byte, id string) (DocumentState, error) +} + +type InitialStateProvider interface { + ProvideFromInitialChange(change []byte, id string) (DocumentState, error) +} diff --git a/data/documentstatebuilder.go b/data/documentstatebuilder.go index ed6d1852..3ab6e677 100644 --- a/data/documentstatebuilder.go +++ b/data/documentstatebuilder.go @@ -1,59 +1,51 @@ package data import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/core/block/editor/state" - "time" + "fmt" ) type documentStateBuilder struct { - tree *Tree - aclState *ACLState // TODO: decide if this is needed or not + tree *Tree + aclState *ACLState // TODO: decide if this is needed or not + stateProvider InitialStateProvider } -func newDocumentStateBuilder(tree *Tree, state *ACLState) *documentStateBuilder { +func newDocumentStateBuilder(tree *Tree, state *ACLState, stateProvider InitialStateProvider) *documentStateBuilder { return &documentStateBuilder{ - tree: tree, - aclState: state, + tree: tree, + aclState: state, + stateProvider: stateProvider, } } // TODO: we should probably merge the two builders into one -func (d *documentStateBuilder) build() (s *state.State, err error) { +func (d *documentStateBuilder) build() (s DocumentState, err error) { var ( - startId string - applyRoot bool - st = time.Now() - lastChange *Change - count int + startId string + count int ) rootChange := d.tree.Root() - root := state.NewDocFromSnapshot("root", rootChange.DecryptedDocumentChange.Snapshot).(*state.State) - root.SetChangeId(rootChange.Id) + + if rootChange.DecryptedDocumentChange == nil { + err = fmt.Errorf("root doesn't have decrypted change") + return + } + + s, err = d.stateProvider.ProvideFromInitialChange(rootChange.DecryptedDocumentChange, rootChange.Id) + if err != nil { + return + } t := d.tree - if startId = root.ChangeId(); startId == "" { - startId = t.RootId() - applyRoot = true - } + startId = rootChange.Id t.Iterate(startId, func(c *Change) (isContinue bool) { count++ - lastChange = c if startId == c.Id { - s = root.NewState() - if applyRoot && c.DecryptedDocumentChange != nil { - s.ApplyChangeIgnoreErr(c.DecryptedDocumentChange.Content...) - s.SetChangeId(c.Id) - s.AddFileKeys(c.DecryptedDocumentChange.FileKeys...) - } return true } if c.DecryptedDocumentChange != nil { - ns := s.NewState() - ns.ApplyChangeIgnoreErr(c.DecryptedDocumentChange.Content...) - ns.SetChangeId(c.Id) - ns.AddFileKeys(c.DecryptedDocumentChange.FileKeys...) - _, _, err = state.ApplyStateFastOne(ns) + _, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id) if err != nil { return false } @@ -61,12 +53,7 @@ func (d *documentStateBuilder) build() (s *state.State, err error) { return true }) if err != nil { - return nil, err + return } - if lastChange != nil { - s.SetLastModified(lastChange.Content.Timestamp, lastChange.Content.Identity) - } - - log.Infof("build state (crdt): changes: %d; dur: %v;", count, time.Since(st)) return s, err } diff --git a/data/threadhelpers.go b/data/threadhelpers.go index 78b8df3d..ab179df9 100644 --- a/data/threadhelpers.go +++ b/data/threadhelpers.go @@ -1,14 +1,13 @@ package data import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/core/block/editor/state" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" ) type ACLContext struct { Tree *Tree ACLState *ACLState - DocState *state.State + DocState DocumentState } func createTreeFromThread(t threadmodels.Thread, fromStart bool) (*Tree, error) { @@ -21,6 +20,7 @@ func createACLStateFromThread( identity string, key threadmodels.EncryptionPrivKey, decoder threadmodels.SigningPubKeyDecoder, + provider InitialStateProvider, fromStart bool) (*ACLContext, error) { tree, err := createTreeFromThread(t, fromStart) if err != nil { @@ -41,7 +41,7 @@ func createACLStateFromThread( } if !valid { // 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) + return createACLStateFromThread(t, identity, key, decoder, provider, true) } } @@ -64,13 +64,14 @@ func createDocumentStateFromThread( t threadmodels.Thread, identity string, key threadmodels.EncryptionPrivKey, + provider InitialStateProvider, decoder threadmodels.SigningPubKeyDecoder) (*ACLContext, error) { - context, err := createACLStateFromThread(t, identity, key, decoder, false) + context, err := createACLStateFromThread(t, identity, key, decoder, provider, false) if err != nil { return nil, err } - docStateBuilder := newDocumentStateBuilder(context.Tree, context.ACLState) + docStateBuilder := newDocumentStateBuilder(context.Tree, context.ACLState, provider) docState, err := docStateBuilder.build() if err != nil { return nil, err diff --git a/go.mod b/go.mod index bcbed4af..0c846236 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,53 @@ module github.com/anytypeio/go-anytype-infrastructure-experiments go 1.18 require ( + github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab + github.com/goccy/go-graphviz v0.0.9 + github.com/gogo/protobuf v1.3.2 + github.com/libp2p/go-libp2p-core v0.8.5 + github.com/mr-tron/base58 v1.2.0 + github.com/prometheus/common v0.18.0 + github.com/stretchr/testify v1.7.0 github.com/textileio/go-threads v1.0.2-0.20210304072541-d0f91da84404 - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c - github.com/goccy/go-graphviz v0.0.9 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c +) + +require ( + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect + github.com/btcsuite/btcd v0.21.0-beta // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/fogleman/gg v1.3.0 // indirect + github.com/gogo/googleapis v1.3.1 // indirect + github.com/gogo/status v1.1.0 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/protobuf v1.4.3 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.2.1 // indirect + github.com/ipfs/go-cid v0.0.7 // indirect + github.com/klauspost/cpuid/v2 v2.0.4 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/libp2p/go-buffer-pool v0.0.2 // indirect + github.com/libp2p/go-openssl v0.0.7 // indirect + github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/multiformats/go-base32 v0.0.3 // indirect + github.com/multiformats/go-base36 v0.1.0 // indirect + github.com/multiformats/go-multiaddr v0.3.3 // indirect + github.com/multiformats/go-multibase v0.0.3 // indirect + github.com/multiformats/go-multihash v0.0.15 // indirect + github.com/multiformats/go-varint v0.0.6 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sirupsen/logrus v1.6.0 // indirect + github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect + golang.org/x/image v0.0.0-20200119044424-58c23975cae1 // indirect + golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + google.golang.org/grpc v1.33.2 // indirect + google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect ) replace github.com/textileio/go-threads => github.com/anytypeio/go-threads v1.1.0-rc1.0.20220223104843-a67245cee80e diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..31e899cb --- /dev/null +++ b/go.sum @@ -0,0 +1,1351 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= +github.com/alecthomas/jsonschema v0.0.0-20191017121752-4bb6e3fae4f2/go.mod h1:Juc2PrI3wtNfUwptSvAIeNx+HrETwHQs6nf+TkOJlOA= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/anytypeio/go-threads v1.1.0-rc1.0.20220223104843-a67245cee80e h1:u6UT0oqGybURAWvKw0p7BPnqoVqC+wEvOOz5K+ESBZE= +github.com/anytypeio/go-threads v1.1.0-rc1.0.20220223104843-a67245cee80e/go.mod h1:O7G/oTjIZfQmB6ZoeU42IdRxWEY9D+HZuLzF6wMk4jI= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab h1:+cdNqtOJWjvepyhxy23G7z7vmpYCoC65AP0nqi1f53s= +github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.29.15/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.0.2/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= +github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.2.1-0.20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/corona10/goimagehash v1.0.2 h1:pUfB0LnsJASMPGEZLj7tGY251vF+qLGqOgEP4rUs6kA= +github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= +github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= +github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgtony/collections v0.1.6/go.mod h1:olD2FRoNisWmjMhK6LDRKv+lMnDoryOZIT+owtd/o6U= +github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/goccy/go-graphviz v0.0.9 h1:s/FMMJ1Joj6La3S5ApO3Jk2cwM4LpXECC2muFx3IPQQ= +github.com/goccy/go-graphviz v0.0.9/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.3.1 h1:CzMaKrvF6Qa7XtRii064vKBQiyvmY8H8vG1xa1/W1JA= +github.com/gogo/googleapis v1.3.1/go.mod h1:d+q1s/xVJxZGKWwC/6UfPIF33J+G1Tq4GYv9Y+Tg/EU= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +github.com/google/gopacket v1.1.18/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.1 h1:V59tBiPuMkySHwJkuq/OYkK0WnOLwCwD3UkTbEMr12U= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hsanjuan/ipfs-lite v1.1.17/go.mod h1:ZetJanzQEAqWj+OwzIppE/S7x+Azu4WFF6PNMLnQGoY= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= +github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= +github.com/ipfs/go-bitswap v0.0.9/go.mod h1:kAPf5qgn2W2DrgAcscZ3HrM9qh4pH+X8Fkk3UPrwvis= +github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= +github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= +github.com/ipfs/go-bitswap v0.1.3/go.mod h1:YEQlFy0kkxops5Vy+OxWdRSEZIoS7I7KDIwoa5Chkps= +github.com/ipfs/go-bitswap v0.1.8/go.mod h1:TOWoxllhccevbWFUR2N7B1MTSVVge1s6XSMiCSA4MzM= +github.com/ipfs/go-bitswap v0.2.20/go.mod h1:C7TwBgHnu89Q8sHsTJP7IhUqF9XYLe71P4tT5adgmYo= +github.com/ipfs/go-bitswap v0.3.3/go.mod h1:AyWWfN3moBzQX0banEtfKOfbXb3ZeoOeXnZGNPV9S6w= +github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= +github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= +github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So= +github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= +github.com/ipfs/go-blockservice v0.1.2/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I= +github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU= +github.com/ipfs/go-blockservice v0.1.4/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU= +github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= +github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= +github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-cidutil v0.0.2/go.mod h1:ewllrvrxG6AMYStla3GD7Cqn+XYSLqjK0vc+086tB6s= +github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= +github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= +github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-datastore v0.4.2/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= +github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= +github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= +github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= +github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= +github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= +github.com/ipfs/go-ds-badger v0.2.6/go.mod h1:02rnztVKA4aZwDuaRPTf8mpqcKmXP7mLl6JPxd14JHA= +github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= +github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= +github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= +github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= +github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= +github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= +github.com/ipfs/go-ipfs-blockstore v0.1.4/go.mod h1:Jxm3XMVjh6R17WvxFEiyKBLUGr86HgIYJW/D/MwqeYQ= +github.com/ipfs/go-ipfs-blockstore v1.0.1/go.mod h1:MGNZlHNEnR4KGgPHM3/k8lBySIOK2Ve+0KjZubKlaOE= +github.com/ipfs/go-ipfs-blockstore v1.0.3/go.mod h1:MGNZlHNEnR4KGgPHM3/k8lBySIOK2Ve+0KjZubKlaOE= +github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= +github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= +github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= +github.com/ipfs/go-ipfs-config v0.10.0/go.mod h1:Ei/FLgHGTdPyqCPK0oPCwGTe8VSnsjJjx7HZqUb6Ry0= +github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-ds-help v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo= +github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= +github.com/ipfs/go-ipfs-ds-help v1.0.0/go.mod h1:ujAbkeIgkKAWtxxNkoZHWLCyk5JpPoKnGyCcsoF6ueE= +github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM= +github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0= +github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= +github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs= +github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= +github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= +github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= +github.com/ipfs/go-ipfs-provider v0.4.3/go.mod h1:rcQBVqfblDQRk5LaCtf2uxuKxMJxvKmF5pLS0pO4au4= +github.com/ipfs/go-ipfs-routing v0.0.1/go.mod h1:k76lf20iKFxQTjcJokbPM9iBXVXVZhcOwc360N4nuKs= +github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= +github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= +github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= +github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= +github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= +github.com/ipfs/go-ipld-cbor v0.0.4/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= +github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= +github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= +github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= +github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= +github.com/ipfs/go-ipns v0.0.2/go.mod h1:WChil4e0/m9cIINWLxZe1Jtf77oz5L05rO2ei/uKJ5U= +github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= +github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= +github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= +github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= +github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= +github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= +github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= +github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= +github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= +github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= +github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= +github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= +github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= +github.com/ipfs/go-path v0.0.7/go.mod h1:6KTKmeRnBXgqrTvzFrPV3CamxcgvXX/4z79tfAd2Sno= +github.com/ipfs/go-peertaskqueue v0.0.4/go.mod h1:03H8fhyeMfKNFWqzYEVyMbcPUeYrqP1MX6Kd+aN+rMQ= +github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= +github.com/ipfs/go-peertaskqueue v0.1.1/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= +github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUnr+P7jivavs9lY= +github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= +github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= +github.com/ipfs/interface-go-ipfs-core v0.4.0/go.mod h1:UJBcU6iNennuI05amq3FQ7g0JHUkibHFAfhfUIy927o= +github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= +github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= +github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-conn-security v0.0.1/go.mod h1:bGmu51N0KU9IEjX7kl2PQjgZa40JQWnayTvNMgD/vyk= +github.com/libp2p/go-conn-security-multistream v0.0.2/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE= +github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= +github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= +github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= +github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= +github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= +github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= +github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= +github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= +github.com/libp2p/go-libp2p v0.0.30/go.mod h1:XWT8FGHlhptAv1+3V/+J5mEpzyui/5bvFsNuWYs611A= +github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= +github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= +github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= +github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k= +github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= +github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= +github.com/libp2p/go-libp2p v0.8.3/go.mod h1:EsH1A+8yoWK+L4iKcbPYu6MPluZ+CHWI9El8cTaefiM= +github.com/libp2p/go-libp2p v0.11.0/go.mod h1:3/ogJDXsbbepEfqtZKBR/DedzxJXCeK17t2Z9RE9bEE= +github.com/libp2p/go-libp2p v0.12.0/go.mod h1:FpHZrfC1q7nA8jitvdjKBDF31hguaC676g/nT9PgQM0= +github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= +github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= +github.com/libp2p/go-libp2p-autonat v0.0.6/go.mod h1:uZneLdOkZHro35xIhpbtTzLlgYturpu4J5+0cZK3MqE= +github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= +github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= +github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= +github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= +github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= +github.com/libp2p/go-libp2p-autonat v0.3.2/go.mod h1:0OzOi1/cVc7UcxfOddemYD5vzEqi4fwRbnZcJGLi68U= +github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= +github.com/libp2p/go-libp2p-autonat v0.4.2/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= +github.com/libp2p/go-libp2p-blankhost v0.0.1/go.mod h1:Ibpbw/7cPPYwFb7PACIWdvxxv0t0XCCI10t7czjAjTc= +github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= +github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= +github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= +github.com/libp2p/go-libp2p-circuit v0.0.9/go.mod h1:uU+IBvEQzCu953/ps7bYzC/D/R0Ho2A9LfKVVCatlqU= +github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= +github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= +github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= +github.com/libp2p/go-libp2p-circuit v0.2.2/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4= +github.com/libp2p/go-libp2p-circuit v0.3.1/go.mod h1:8RMIlivu1+RxhebipJwFDA45DasLx+kkrp4IlJj53F4= +github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= +github.com/libp2p/go-libp2p-connmgr v0.2.4/go.mod h1:YV0b/RIm8NGPnnNWM7hG9Q38OeQiQfKhHCCs1++ufn0= +github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= +github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= +github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= +github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= +github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= +github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= +github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= +github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= +github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= +github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= +github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= +github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= +github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= +github.com/libp2p/go-libp2p-core v0.5.2/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= +github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= +github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= +github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= +github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= +github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= +github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= +github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= +github.com/libp2p/go-libp2p-discovery v0.0.5/go.mod h1:YtF20GUxjgoKZ4zmXj8j3Nb2TUSBHFlOCetzYdbZL5I= +github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= +github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= +github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= +github.com/libp2p/go-libp2p-discovery v0.4.0/go.mod h1:bZ0aJSrFc/eX2llP0ryhb1kpgkPyTo23SJ5b7UQCMh4= +github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= +github.com/libp2p/go-libp2p-gostream v0.3.0/go.mod h1:pLBQu8db7vBMNINGsAwLL/ZCE8wng5V1FThoaE5rNjc= +github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go= +github.com/libp2p/go-libp2p-host v0.0.3/go.mod h1:Y/qPyA6C8j2coYyos1dfRm0I8+nvd4TGrDGt4tA7JR8= +github.com/libp2p/go-libp2p-interface-connmgr v0.0.1/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= +github.com/libp2p/go-libp2p-interface-connmgr v0.0.4/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= +github.com/libp2p/go-libp2p-interface-connmgr v0.0.5/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= +github.com/libp2p/go-libp2p-interface-pnet v0.0.1/go.mod h1:el9jHpQAXK5dnTpKA4yfCNBZXvrzdOU75zz+C6ryp3k= +github.com/libp2p/go-libp2p-kad-dht v0.10.0/go.mod h1:LEKcCFHxnvypOPaqZ0m6h0fLQ9Y8t1iZMOg7a0aQDD4= +github.com/libp2p/go-libp2p-kad-dht v0.11.0/go.mod h1:5ojtR2acDPqh/jXf5orWy8YGb8bHQDS+qeDcoscL/PI= +github.com/libp2p/go-libp2p-kbucket v0.4.7/go.mod h1:XyVo99AfQH0foSf176k4jY1xUJ2+jUJIZCSDm7r2YKk= +github.com/libp2p/go-libp2p-loggables v0.0.1/go.mod h1:lDipDlBNYbpyqyPX/KcoO+eq0sJYEVR2JgOexcivchg= +github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= +github.com/libp2p/go-libp2p-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08= +github.com/libp2p/go-libp2p-mplex v0.1.1/go.mod h1:KUQWpGkCzfV7UIpi8SKsAVxyBgz1c9R5EvxgnwLsb/I= +github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= +github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= +github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= +github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= +github.com/libp2p/go-libp2p-mplex v0.2.4/go.mod h1:mI7iOezdWFOisvUwaYd3IDrJ4oVmgoXK8H331ui39CE= +github.com/libp2p/go-libp2p-mplex v0.3.0/go.mod h1:l9QWxRbbb5/hQMECEb908GbS9Sm2UAR2KFZKUJEynEs= +github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= +github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= +github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= +github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= +github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= +github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= +github.com/libp2p/go-libp2p-net v0.0.2/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= +github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFxecf9Gt03cKxm2f/Q= +github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= +github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM= +github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= +github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo= +github.com/libp2p/go-libp2p-peer v0.1.1/go.mod h1:jkF12jGB4Gk/IOo+yomm+7oLWxF278F7UnrYUQ1Q8es= +github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= +github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= +github.com/libp2p/go-libp2p-peerstore v0.0.6/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= +github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= +github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= +github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= +github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= +github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= +github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= +github.com/libp2p/go-libp2p-peerstore v0.2.3/go.mod h1:K8ljLdFn590GMttg/luh4caB/3g0vKuY01psze0upRw= +github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= +github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= +github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= +github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s= +github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk= +github.com/libp2p/go-libp2p-pubsub v0.4.0/go.mod h1:izkeMLvz6Ht8yAISXjx60XUQZMq9ZMe5h2ih4dLIBIQ= +github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= +github.com/libp2p/go-libp2p-record v0.0.1/go.mod h1:grzqg263Rug/sRex85QrDOLntdFAymLDLm7lxMgU79Q= +github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= +github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= +github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4= +github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys= +github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw= +github.com/libp2p/go-libp2p-secio v0.0.3/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0= +github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= +github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= +github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= +github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= +github.com/libp2p/go-libp2p-swarm v0.0.6/go.mod h1:s5GZvzg9xXe8sbeESuFpjt8CJPTCa8mhEusweJqyFy8= +github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= +github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= +github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= +github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= +github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= +github.com/libp2p/go-libp2p-swarm v0.3.1/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= +github.com/libp2p/go-libp2p-swarm v0.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= +github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= +github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= +github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= +github.com/libp2p/go-libp2p-testing v0.2.0/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= +github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= +github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= +github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= +github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk= +github.com/libp2p/go-libp2p-transport v0.0.5/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A= +github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2kn/m1w6YXxcIAYJYeI90h6BGgUc= +github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= +github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= +github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= +github.com/libp2p/go-libp2p-yamux v0.1.2/go.mod h1:xUoV/RmYkg6BW/qGxA9XJyg+HzXFYkeXbnhjmnYzKp8= +github.com/libp2p/go-libp2p-yamux v0.1.3/go.mod h1:VGSQVrqkh6y4nm0189qqxMtvyBft44MOYYPpYKXiVt4= +github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= +github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= +github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= +github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= +github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= +github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= +github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= +github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= +github.com/libp2p/go-libp2p-yamux v0.5.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE= +github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= +github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= +github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= +github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= +github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= +github.com/libp2p/go-mplex v0.0.4/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= +github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= +github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= +github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= +github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= +github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= +github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= +github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= +github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= +github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= +github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= +github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= +github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= +github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= +github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= +github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= +github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= +github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= +github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= +github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= +github.com/libp2p/go-stream-muxer v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ= +github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw= +github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= +github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= +github.com/libp2p/go-tcp-transport v0.0.4/go.mod h1:+E8HvC8ezEVOxIo3V5vCK9l1y/19K427vCzQ+xHKH/o= +github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= +github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= +github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= +github.com/libp2p/go-tcp-transport v0.2.1/go.mod h1:zskiJ70MEfWz2MKxvFB/Pv+tPIB1PpPUrHIWQ8aFw7M= +github.com/libp2p/go-tcp-transport v0.2.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= +github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I= +github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= +github.com/libp2p/go-ws-transport v0.0.5/go.mod h1:Qbl4BxPfXXhhd/o0wcrgoaItHqA9tnZjoFZnxykuaXU= +github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= +github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= +github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= +github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= +github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= +github.com/libp2p/go-yamux v1.2.1/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= +github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= +github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= +github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= +github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= +github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= +github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= +github.com/multiformats/go-multiaddr v0.3.3 h1:vo2OTSAqnENB2rLk79pLtr+uhj+VAzSe3uef5q0lRSs= +github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= +github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= +github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= +github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= +github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= +github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= +github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= +github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= +github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.15 h1:hWOPdrNqDjwHDx82vsYGSDZNyktOJJ2dzZJzFkOV1jM= +github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= +github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/multiformats/go-multistream v0.0.4/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= +github.com/multiformats/go-multistream v0.1.2/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-multistream v0.2.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= +github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= +github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= +github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= +github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y= +github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/textileio/go-datastore-extensions v1.0.1/go.mod h1:Pzj9FDRkb55910dr/FX8M7WywvnS26gBgEDez1ZBuLE= +github.com/textileio/go-ds-badger v0.2.7-0.20201204225019-4ee78c4a40e2/go.mod h1:qEZ/z1KyoRhGS5MYEbIcWUCCPd/0HxCkFDVeJgP1RcI= +github.com/textileio/go-ds-mongo v0.1.4/go.mod h1:Zf6JlMPiIQUUmGlFFn5Z65C9p9LAvPg7XvX+qdGmTsU= +github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= +github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= +github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= +github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= +github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= +github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= +github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= +github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= +github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= +github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= +github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.4.0/go.mod h1:llVBH2pkj9HywK0Dtdt6lDikOjFLbceHVu/Rc0iMKLs= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 h1:0PC75Fz/kyMGhL0e1QnypqK2kQMqKt9csD1GnMJR+Zk= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190524152521-dbbf3f1254d4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 h1:kHSDPqCtsHZOg0nVylfTo20DDhE9gG4Y0jn7hKQ0QAM= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= +gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= From 3404626dd6a4fd1733649ec2b4f05e9262537d18 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 3 Jul 2022 17:35:21 +0200 Subject: [PATCH 08/53] Fix threadbuilder and change examples --- data/threadbuilder/invalidsnapshotexample.yml | 6 +- data/threadbuilder/threadbuilder.go | 5 +- data/threadbuilder/userjoinexample.yml | 130 +++++++------ .../threadbuilder/userremovebeforeexample.yml | 131 ++++++------- data/threadbuilder/userremoveexample.yml | 122 ++++++------- data/threadbuilder/validsnapshotexample.yml | 172 +++++++++--------- data/threadbuilder/ymlentities.go | 2 +- data/threadbuilder/ymlentities_test.go | 2 +- 8 files changed, 284 insertions(+), 286 deletions(-) diff --git a/data/threadbuilder/invalidsnapshotexample.yml b/data/threadbuilder/invalidsnapshotexample.yml index 3a317ac0..eb49fa95 100644 --- a/data/threadbuilder/invalidsnapshotexample.yml +++ b/data/threadbuilder/invalidsnapshotexample.yml @@ -14,8 +14,7 @@ changes: encryptedReadKeys: [key.Read.1] permission: admin snapshot: - blocks: - - id: root + text: "some text" aclChanges: - userAdd: identity: A @@ -45,8 +44,7 @@ changes: encryptedReadKeys: [ key.Read.1 ] permission: admin snapshot: - blocks: - - id: root + text: "some text" aclChanges: - userAdd: identity: D diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go index db3e35d1..5cb9ca1b 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/data/threadbuilder/threadbuilder.go @@ -126,7 +126,7 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { newChange.readKey = k newChange.signKey = t.keychain.SigningKeys[ch.Identity] aclChange := &pb.ACLChange{} - aclChange.Identity = newChange.Identity + aclChange.Identity = ch.Identity if len(ch.AclChanges) > 0 || ch.AclSnapshot != nil { aclChange.AclData = &pb.ACLChangeACLData{} if ch.AclSnapshot != nil { @@ -368,7 +368,8 @@ func (t *ThreadBuilder) convertPermission(perm string) pb.ACLChangeUserPermissio func (t *ThreadBuilder) traverseFromHeads(f func(t *threadChange) error) error { uniqMap := map[string]struct{}{} - stack := t.heads + stack := make([]string, len(t.heads), 10) + copy(stack, t.heads) for len(stack) > 0 { id := stack[len(stack)-1] stack = stack[:len(stack)-1] diff --git a/data/threadbuilder/userjoinexample.yml b/data/threadbuilder/userjoinexample.yml index e70c9fc3..6df42e8f 100644 --- a/data/threadbuilder/userjoinexample.yml +++ b/data/threadbuilder/userjoinexample.yml @@ -1,75 +1,68 @@ thread: author: A -logs: - - id: A.1 +changes: + - id: A.1.1 identity: A - records: - - id: A.1.1 - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - snapshot: - blocks: - - id: root - aclChanges: - - userAdd: - identity: A - permission: admin - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - changes: - - blockAdd: - id: root - readKey: key.Read.1 - - id: A.1.2 - aclChanges: - - userInvite: - acceptKey: key.Sign.Onetime1 - encryptionKey: key.Enc.Onetime1 - encryptedReadKeys: [key.Read.1] - permissions: writer - - userAdd: - identity: C - permission: reader - encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 - - id: A.1.3 - changes: - - blockAdd: - id: second - targetId: root - readKey: key.Read.1 - - id: B.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + text: "some text" + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + changes: + - textAppend: + text: "some text" + readKey: key.Read.1 + - id: A.1.2 + identity: A + aclChanges: + - userInvite: + acceptKey: key.Sign.Onetime1 + encryptionKey: key.Enc.Onetime1 + encryptedReadKeys: [key.Read.1] + permissions: writer + - userAdd: + identity: C + permission: reader + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: A.1.3 + identity: A + changes: + - textAppend: + text: "second" + readKey: key.Read.1 + - id: B.1.1 identity: B - records: - - id: B.1.1 - aclChanges: - - userJoin: - identity: B - encryptionKey: key.Enc.B - acceptSignature: key.Sign.Onetime1 - inviteId: A.1.2 - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: B.1.2 - changes: - - blockAdd: - id: first - targetId: root - readKey: key.Read.1 - - id: C.1 + aclChanges: + - userJoin: + identity: B + encryptionKey: key.Enc.B + acceptSignature: key.Sign.Onetime1 + inviteId: A.1.2 + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1.2 + identity: B + changes: + - textAppend: + text: "first" + readKey: key.Read.1 + - id: C.1.1 identity: C - records: - - id: C.1.1 - changes: - - blockAdd: - id: third - targetId: root - readKey: key.Read.1 + changes: + - textAppend: + text: "third" + readKey: key.Read.1 keys: Enc: - A @@ -106,4 +99,5 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] - +heads: + - "A.1.3" diff --git a/data/threadbuilder/userremovebeforeexample.yml b/data/threadbuilder/userremovebeforeexample.yml index a5abfd13..fb214099 100644 --- a/data/threadbuilder/userremovebeforeexample.yml +++ b/data/threadbuilder/userremovebeforeexample.yml @@ -1,72 +1,70 @@ thread: author: A -logs: - - id: A.1 +changes: + - id: A.1.1 identity: A - records: - - id: A.1.1 - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: B - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - permission: admin - snapshot: - blocks: - - id: root - aclChanges: - - userAdd: - identity: A - permission: admin - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - - userAdd: - identity: B - permission: admin - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - changes: - - blockAdd: - id: root - readKey: key.Read.1 - - id: A.1.2 - aclChanges: - - userRemove: - removedIdentity: B - newReadKey: key.Read.2 - identitiesLeft: [A, C] - readKey: key.Read.2 - - id: A.1.3 - aclChanges: - - userAdd: - identity: E - permission: admin - encryptionKey: key.Enc.E - encryptedReadKeys: [key.Read.1, key.Read.2] - readKey: key.Read.2 - - id: B.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + text: "some text" + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + - userAdd: + identity: B + permission: admin + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + changes: + - textAppend: + text: "some text" + readKey: key.Read.1 + - id: A.1.2 + identity: A + aclChanges: + - userRemove: + removedIdentity: B + newReadKey: key.Read.2 + identitiesLeft: [A, C] + readKey: key.Read.2 + - id: A.1.3 + identity: A + aclChanges: + - userAdd: + identity: E + permission: admin + encryptionKey: key.Enc.E + encryptedReadKeys: [key.Read.1, key.Read.2] + readKey: key.Read.2 + - id: B.1.1 identity: B - records: - - id: B.1.1 - aclChanges: - - userAdd: - identity: C - permission: admin - encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 - - id: B.1.2 - aclChanges: - - userAdd: - identity: D - permission: admin - encryptionKey: key.Enc.D - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 + aclChanges: + - userAdd: + identity: C + permission: admin + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: B.1.2 + identity: B + aclChanges: + - userAdd: + identity: D + permission: admin + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 keys: Enc: - A @@ -103,3 +101,6 @@ graph: baseSnapshot: A.1.1 aclHeads: [A.1.2] treeHeads: [A.1.2] +heads: + - "A.1.3" + - "B.1.2" diff --git a/data/threadbuilder/userremoveexample.yml b/data/threadbuilder/userremoveexample.yml index 6176b0ac..51fa2d76 100644 --- a/data/threadbuilder/userremoveexample.yml +++ b/data/threadbuilder/userremoveexample.yml @@ -1,68 +1,65 @@ thread: author: A -logs: - - id: A.1 +changes: + - id: A.1.1 identity: A - records: - - id: A.1.1 - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - snapshot: - blocks: - - id: root - aclChanges: - - userAdd: - identity: A - permission: admin - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - changes: - - blockAdd: - id: root - readKey: key.Read.1 - - id: A.1.2 - aclChanges: - - userInvite: - acceptKey: key.Sign.Onetime1 - encryptionKey: key.Enc.Onetime1 - encryptedReadKeys: [key.Read.1] - permissions: writer - readKey: key.Read.1 - - id: A.1.3 - aclChanges: - - userRemove: - removedIdentity: B - newReadKey: key.Read.2 - identitiesLeft: [A] - readKey: key.Read.2 - - id: A.1.4 - changes: - - blockAdd: - id: second - targetId: root - readKey: key.Read.2 - - id: B.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + text: "some text" + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + changes: + - textAppend: + text: "some text" + readKey: key.Read.1 + - id: A.1.2 + identity: A + aclChanges: + - userInvite: + acceptKey: key.Sign.Onetime1 + encryptionKey: key.Enc.Onetime1 + encryptedReadKeys: [key.Read.1] + permissions: writer + readKey: key.Read.1 + - id: A.1.3 + identity: A + aclChanges: + - userRemove: + removedIdentity: B + newReadKey: key.Read.2 + identitiesLeft: [A] + readKey: key.Read.2 + - id: A.1.4 + identity: A + changes: + - textAppend: + text: "first" + readKey: key.Read.2 + - id: B.1.1 identity: B - records: - - id: B.1.1 - aclChanges: - - userJoin: - identity: B - encryptionKey: key.Enc.B - acceptSignature: key.Sign.Onetime1 - inviteId: A.1.2 - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: B.1.2 - changes: - - blockAdd: - id: first - targetId: root - readKey: key.Read.1 + aclChanges: + - userJoin: + identity: B + encryptionKey: key.Enc.B + acceptSignature: key.Sign.Onetime1 + inviteId: A.1.2 + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1.2 + identity: B + changes: + - textAppend: + text: "second" + readKey: key.Read.1 keys: Enc: - A @@ -104,3 +101,6 @@ graph: aclSnapshot: A.1.1 aclHeads: [A.1.3] treeHeads: [A.1.3] +heads: + - "A.1.4" + - "B.1.2" diff --git a/data/threadbuilder/validsnapshotexample.yml b/data/threadbuilder/validsnapshotexample.yml index abdcd093..2e3c60c6 100644 --- a/data/threadbuilder/validsnapshotexample.yml +++ b/data/threadbuilder/validsnapshotexample.yml @@ -1,91 +1,91 @@ thread: author: A -logs: - - id: A.1 +changes: + - id: A.1.1 identity: A - records: - - id: A.1.1 - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: B - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - permission: admin - snapshot: - blocks: - - id: root - aclChanges: - - userAdd: - identity: A - permission: admin - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - - userAdd: - identity: B - permission: admin - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: A.1.2 - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: B - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: C - encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] - permission: admin - - identity: D - encryptionKey: key.Enc.D - encryptedReadKeys: [ key.Read.1 ] - permission: admin - snapshot: - blocks: - - id: root - aclChanges: - - userAdd: - identity: D - permission: admin - encryptionKey: key.Enc.D - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: A.1.3 - aclChanges: - - userAdd: - identity: E - permission: admin - encryptionKey: key.Enc.E - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: B.1 + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + text: "some text" + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + - userAdd: + identity: B + permission: admin + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + changes: + - textAppend: + text: "some text" + - id: A.1.2 + identity: A + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: B + encryptionKey: key.Enc.B + encryptedReadKeys: [key.Read.1] + permission: admin + - identity: C + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + permission: admin + - identity: D + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + permission: admin + snapshot: + text: "some text" + aclChanges: + - userAdd: + identity: D + permission: admin + encryptionKey: key.Enc.D + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: A.1.3 + identity: A + aclChanges: + - userAdd: + identity: E + permission: admin + encryptionKey: key.Enc.E + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1.1 identity: B - records: - - id: B.1.1 - aclChanges: - - userAdd: - identity: C - permission: admin - encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 - - id: B.1.2 - aclChanges: - - userAdd: - identity: F - permission: admin - encryptionKey: key.Enc.F - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 + aclChanges: + - userAdd: + identity: C + permission: admin + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: B.1.2 + identity: B + aclChanges: + - userAdd: + identity: F + permission: admin + encryptionKey: key.Enc.F + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 keys: Enc: - A @@ -124,3 +124,7 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] +heads: + - "A.1.3" + - "B.1.2" + diff --git a/data/threadbuilder/ymlentities.go b/data/threadbuilder/ymlentities.go index 3ef03f05..8052deb4 100644 --- a/data/threadbuilder/ymlentities.go +++ b/data/threadbuilder/ymlentities.go @@ -67,7 +67,7 @@ type ACLChange struct { type PlainTextChange struct { TextAppend *struct { Text string `yaml:"text"` - } `yaml:"blockAdd"` + } `yaml:"textAppend"` } type YMLThread struct { diff --git a/data/threadbuilder/ymlentities_test.go b/data/threadbuilder/ymlentities_test.go index d2420eae..bd066301 100644 --- a/data/threadbuilder/ymlentities_test.go +++ b/data/threadbuilder/ymlentities_test.go @@ -6,7 +6,7 @@ import ( ) func Test_YamlParse(t *testing.T) { - tb, _ := NewThreadBuilderFromFile("userjoinexample.yml") + tb, _ := NewThreadBuilderFromFile("validsnapshotexample.yml") gr, _ := tb.Graph() fmt.Println(gr) } From 18963546ef11cc78958a613f1f6922ca80da13dd Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 3 Jul 2022 17:51:14 +0200 Subject: [PATCH 09/53] Add plain text document state --- data/plaintextdocstate.go | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 data/plaintextdocstate.go diff --git a/data/plaintextdocstate.go b/data/plaintextdocstate.go new file mode 100644 index 00000000..d661d635 --- /dev/null +++ b/data/plaintextdocstate.go @@ -0,0 +1,61 @@ +package data + +import ( + "fmt" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/gogo/protobuf/proto" +) + +type PlainTextDocumentState struct { + LastChangeId string + Text string +} + +func NewPlainTextDocumentState(text string, id string) *PlainTextDocumentState { + return &PlainTextDocumentState{ + LastChangeId: id, + Text: text, + } +} + +func (p *PlainTextDocumentState) ApplyChange(change []byte, id string) (DocumentState, error) { + var changesData pb.PlainTextChangeData + err := proto.Unmarshal(change, &changesData) + if err != nil { + return nil, err + } + + for _, content := range changesData.GetContent() { + err = p.applyChange(content) + if err != nil { + return nil, err + } + } + p.LastChangeId = id + return p, nil +} + +func (p *PlainTextDocumentState) applyChange(ch *pb.PlainTextChangeContent) error { + switch { + case ch.GetTextAppend() != nil: + text := ch.GetTextAppend().GetText() + p.Text += text + } + return nil +} + +type PlainTextDocumentStateProvider struct{} + +func (p *PlainTextDocumentStateProvider) ProvideFromInitialChange(change []byte, id string) (DocumentState, error) { + var changesData pb.PlainTextChangeData + err := proto.Unmarshal(change, &changesData) + if err != nil { + return nil, err + } + + if changesData.GetSnapshot() == nil { + return nil, fmt.Errorf("could not create state from empty snapshot") + } + return NewPlainTextDocumentState(changesData.GetSnapshot().GetText(), id), nil +} From 4a5da29cfec0ce13133a8b6ab31973b9ac21bfd2 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 3 Jul 2022 18:15:33 +0200 Subject: [PATCH 10/53] Fix first ACL test and threadbuilder --- data/aclstatebuilder_test.go | 5 +++++ data/documentstatebuilder_test.go | 26 ++++++-------------------- data/plaintextdocstate.go | 4 ++++ data/threadbuilder/threadbuilder.go | 2 +- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/data/aclstatebuilder_test.go b/data/aclstatebuilder_test.go index 9f81cc3f..ea6a6cfb 100644 --- a/data/aclstatebuilder_test.go +++ b/data/aclstatebuilder_test.go @@ -19,6 +19,7 @@ func TestACLStateBuilder_UserJoinBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], threadmodels.NewEd25519Decoder(), + NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -53,6 +54,7 @@ func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], threadmodels.NewEd25519Decoder(), + NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -83,6 +85,7 @@ func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], threadmodels.NewEd25519Decoder(), + NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -114,6 +117,7 @@ func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], threadmodels.NewEd25519Decoder(), + NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -144,6 +148,7 @@ func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], threadmodels.NewEd25519Decoder(), + NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) diff --git a/data/documentstatebuilder_test.go b/data/documentstatebuilder_test.go index e84b3805..9ca74602 100644 --- a/data/documentstatebuilder_test.go +++ b/data/documentstatebuilder_test.go @@ -17,21 +17,14 @@ func TestDocumentStateBuilder_UserJoinBuild(t *testing.T) { thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], + NewPlainTextDocumentStateProvider(), threadmodels.NewEd25519Decoder()) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) } - st := ctx.DocState - allIds := make(map[string]bool) - for _, b := range st.Blocks() { - allIds[b.Id] = true - } - if err != nil { - t.Fatalf("iterate should not return error: %v", err) - } - assert.True(t, allIds["root"]) - assert.True(t, allIds["first"]) + st := ctx.DocState.(*PlainTextDocumentState) + assert.Equal(t, st.Text, "") } func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { @@ -44,19 +37,12 @@ func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], + NewPlainTextDocumentStateProvider(), threadmodels.NewEd25519Decoder()) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) } - st := ctx.DocState - allIds := make(map[string]bool) - for _, b := range st.Blocks() { - allIds[b.Id] = true - } - if err != nil { - t.Fatalf("iterate should not return error: %v", err) - } - assert.True(t, allIds["root"]) - assert.True(t, allIds["second"]) + st := ctx.DocState.(*PlainTextDocumentState) + assert.Equal(t, st.Text, "") } diff --git a/data/plaintextdocstate.go b/data/plaintextdocstate.go index d661d635..3913abc2 100644 --- a/data/plaintextdocstate.go +++ b/data/plaintextdocstate.go @@ -47,6 +47,10 @@ func (p *PlainTextDocumentState) applyChange(ch *pb.PlainTextChangeContent) erro type PlainTextDocumentStateProvider struct{} +func NewPlainTextDocumentStateProvider() *PlainTextDocumentStateProvider { + return &PlainTextDocumentStateProvider{} +} + func (p *PlainTextDocumentStateProvider) ProvideFromInitialChange(change []byte, id string) (DocumentState, error) { var changesData pb.PlainTextChangeData err := proto.Unmarshal(change, &changesData) diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go index 5cb9ca1b..991a5ae1 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/data/threadbuilder/threadbuilder.go @@ -126,7 +126,7 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { newChange.readKey = k newChange.signKey = t.keychain.SigningKeys[ch.Identity] aclChange := &pb.ACLChange{} - aclChange.Identity = ch.Identity + aclChange.Identity = t.keychain.GetIdentity(ch.Identity) if len(ch.AclChanges) > 0 || ch.AclSnapshot != nil { aclChange.AclData = &pb.ACLChangeACLData{} if ch.AclSnapshot != nil { From 15deb66dff03099ce344cb635b1b879594838361 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Mon, 4 Jul 2022 11:22:37 +0200 Subject: [PATCH 11/53] Fix tree builder bugs related to dfs --- data/acltreebuilder.go | 6 ++++-- data/treebuilder.go | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index f97aa805..9b669671 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -109,10 +109,12 @@ func (tb *ACLTreeBuilder) buildTreeFromStart(heads []string) (err error) { return } -func (tb *ACLTreeBuilder) dfsFromStart(stack []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) + buf = make([]*Change, 0, len(stack)*2) uniqMap := make(map[string]struct{}) - for len(stack) > 0 { id := stack[len(stack)-1] stack = stack[:len(stack)-1] diff --git a/data/treebuilder.go b/data/treebuilder.go index 1d04804b..62cae49c 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -125,10 +125,12 @@ func (tb *TreeBuilder) buildTreeFromStart(heads []string) (err error) { return } -func (tb *TreeBuilder) dfsFromStart(stack []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) + buf = make([]*Change, 0, len(stack)*2) uniqMap := make(map[string]struct{}) - for len(stack) > 0 { id := stack[len(stack)-1] stack = stack[:len(stack)-1] @@ -166,7 +168,10 @@ func (tb *TreeBuilder) buildTree(heads []string, breakpoint string) (err error) return } -func (tb *TreeBuilder) dfs(stack []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) + buf = make([]*Change, 0, len(stack)*2) uniqMap := map[string]struct{}{breakpoint: {}} for len(stack) > 0 { From aa4222588ae45007dcfc44228d3d24296258cfd4 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Mon, 4 Jul 2022 13:28:16 +0200 Subject: [PATCH 12/53] Further fixes including tests --- data/acltreebuilder.go | 2 -- data/documentstatebuilder_test.go | 4 ++-- data/plaintextdocstate.go | 2 +- data/tree.go | 1 + data/treebuilder.go | 2 -- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index 9b669671..f3e400a9 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -13,7 +13,6 @@ import ( type ACLTreeBuilder struct { cache map[string]*Change - logHeads map[string]*Change identityKeys map[string]threadmodels.SigningPubKey signingPubKeyDecoder threadmodels.SigningPubKeyDecoder tree *Tree @@ -23,7 +22,6 @@ type ACLTreeBuilder struct { func NewACLTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *ACLTreeBuilder { return &ACLTreeBuilder{ cache: make(map[string]*Change), - logHeads: make(map[string]*Change), identityKeys: make(map[string]threadmodels.SigningPubKey), signingPubKeyDecoder: decoder, tree: &Tree{}, // TODO: add NewTree method diff --git a/data/documentstatebuilder_test.go b/data/documentstatebuilder_test.go index 9ca74602..b691b72e 100644 --- a/data/documentstatebuilder_test.go +++ b/data/documentstatebuilder_test.go @@ -24,7 +24,7 @@ func TestDocumentStateBuilder_UserJoinBuild(t *testing.T) { } st := ctx.DocState.(*PlainTextDocumentState) - assert.Equal(t, st.Text, "") + assert.Equal(t, st.Text, "some text|first") } func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { @@ -44,5 +44,5 @@ func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { } st := ctx.DocState.(*PlainTextDocumentState) - assert.Equal(t, st.Text, "") + assert.Equal(t, st.Text, "some text|first") } diff --git a/data/plaintextdocstate.go b/data/plaintextdocstate.go index 3913abc2..3357c02e 100644 --- a/data/plaintextdocstate.go +++ b/data/plaintextdocstate.go @@ -40,7 +40,7 @@ func (p *PlainTextDocumentState) applyChange(ch *pb.PlainTextChangeContent) erro switch { case ch.GetTextAppend() != nil: text := ch.GetTextAppend().GetText() - p.Text += text + p.Text += "|" + text } return nil } diff --git a/data/tree.go b/data/tree.go index 0e58823d..0c4bfb6f 100644 --- a/data/tree.go +++ b/data/tree.go @@ -130,6 +130,7 @@ func (t *Tree) RemoveInvalidChange(id string) { stack = append(stack, el.Id) } } + t.updateHeads() } func (t *Tree) add(c *Change) (attached bool) { diff --git a/data/treebuilder.go b/data/treebuilder.go index 62cae49c..3d7b7e3a 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -21,7 +21,6 @@ var ( type TreeBuilder struct { cache map[string]*Change - logHeads map[string]*Change identityKeys map[string]threadmodels.SigningPubKey signingPubKeyDecoder threadmodels.SigningPubKeyDecoder tree *Tree @@ -31,7 +30,6 @@ type TreeBuilder struct { func NewTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *TreeBuilder { return &TreeBuilder{ cache: make(map[string]*Change), - logHeads: make(map[string]*Change), identityKeys: make(map[string]threadmodels.SigningPubKey), signingPubKeyDecoder: decoder, tree: &Tree{}, // TODO: add NewTree method From 102b33c3dcbe99df901c7fe326ac46bbe50bb530 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Mon, 4 Jul 2022 13:42:29 +0200 Subject: [PATCH 13/53] Add some comments in tests --- data/threadbuilder/userjoinexample.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/threadbuilder/userjoinexample.yml b/data/threadbuilder/userjoinexample.yml index 6df42e8f..d41665ca 100644 --- a/data/threadbuilder/userjoinexample.yml +++ b/data/threadbuilder/userjoinexample.yml @@ -91,11 +91,11 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] - - id: A.1.3 + - id: A.1.3 # this should be invalid, because it is based on one of the invalid changes baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.2, C.1.1] - - id: C.1.1 + - id: C.1.1 # this should be invalid, because C is a reader baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] From 3395f35b59e0d04f950b777088bc4e483463402a Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 5 Jul 2022 11:59:01 +0200 Subject: [PATCH 14/53] WIP work on document structure --- data/document.go | 58 +++++++++++++++++++++++++++ data/documentcontext.go | 32 +++++++++++++++ data/pb/protos/plaintextchanges.proto | 2 + data/threadmodels/models.go | 3 +- 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 data/document.go create mode 100644 data/documentcontext.go diff --git a/data/document.go b/data/document.go new file mode 100644 index 00000000..1fb26417 --- /dev/null +++ b/data/document.go @@ -0,0 +1,58 @@ +package data + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" +) + +type AccountData struct { + Identity string + EncKey threadmodels.EncryptionPrivKey +} + +type Document struct { + thread threadmodels.Thread + stateProvider InitialStateProvider + accountData AccountData + decoder threadmodels.SigningPubKeyDecoder + aclTree *Tree + fullTree *Tree + aclTreeBuilder *ACLTreeBuilder + aclStateBuilder *ACLStateBuilder +} + +type UpdateResult int + +const ( + UpdateResultAppend = iota + UpdateResultRebuild + UpdateResultExists + UpdateResultNoAction +) + +func NewDocument( + thread threadmodels.Thread, + stateProvider InitialStateProvider, + accountData AccountData) *Document { + return &Document{ + thread: thread, + stateProvider: stateProvider, + accountData: accountData, + decoder: threadmodels.NewEd25519Decoder(), + } +} + +func (d *Document) Update(changes []*pb.ACLChange) (DocumentState, UpdateResult, error) { + return nil, 0, nil +} + +func (d *Document) Build() (DocumentState, error) { + treeBuilder := NewTreeBuilder(d.thread, threadmodels.NewEd25519Decoder()) + + return treeBuilder.Build(fromStart) + return nil, nil +} + +func (d *Document) State() DocumentState { + return nil +} diff --git a/data/documentcontext.go b/data/documentcontext.go new file mode 100644 index 00000000..eb4ff5cd --- /dev/null +++ b/data/documentcontext.go @@ -0,0 +1,32 @@ +package data + +import "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + +type documentContext struct { + aclTree *Tree + fullTree *Tree + identity string + encryptionKey threadmodels.EncryptionPrivKey + decoder threadmodels.SigningPubKeyDecoder + aclState *ACLState + docState DocumentState + changeCache map[string]*Change + identityKeys map[string]threadmodels.SigningPubKey +} + +func newDocumentContext( + identity string, + encryptionKey threadmodels.EncryptionPrivKey, + decoder threadmodels.SigningPubKeyDecoder) *documentContext { + return &documentContext{ + aclTree: &Tree{}, + fullTree: &Tree{}, + identity: identity, + encryptionKey: encryptionKey, + decoder: decoder, + aclState: nil, + docState: nil, + changeCache: make(map[string]*Change), + identityKeys: make(map[string]threadmodels.SigningPubKey), + } +} diff --git a/data/pb/protos/plaintextchanges.proto b/data/pb/protos/plaintextchanges.proto index 4b5e9d37..0f9177af 100644 --- a/data/pb/protos/plaintextchanges.proto +++ b/data/pb/protos/plaintextchanges.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package anytype; option go_package = "pb"; +// TODO: move to separate package + message PlainTextChange { message Content { oneof value { diff --git a/data/threadmodels/models.go b/data/threadmodels/models.go index aa388596..63367f28 100644 --- a/data/threadmodels/models.go +++ b/data/threadmodels/models.go @@ -8,8 +8,9 @@ import ( type Thread interface { ID() string Heads() []string - // TODO: add ACL heads GetChange(ctx context.Context, recordID string) (*RawChange, error) + //SetHeads(heads []string) + //AddChanges(*pb.ACLChange) PushChange(payload proto.Marshaler) (id string, err error) } From 376e5ba119a08ddc0f21049bbd71db26be50948a Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 5 Jul 2022 15:42:17 +0200 Subject: [PATCH 15/53] Unify changes loading in change loader --- data/aclstatebuilder.go | 50 +++++++++++++++----------- data/acltreebuilder.go | 61 +++++-------------------------- data/changeloader.go | 80 +++++++++++++++++++++++++++++++++++++++++ data/treebuilder.go | 61 +++++-------------------------- 4 files changed, 125 insertions(+), 127 deletions(-) create mode 100644 data/changeloader.go diff --git a/data/aclstatebuilder.go b/data/aclstatebuilder.go index 5aefc766..9ac3ddff 100644 --- a/data/aclstatebuilder.go +++ b/data/aclstatebuilder.go @@ -12,6 +12,7 @@ type ACLStateBuilder struct { aclState *ACLState identity string key threadmodels.EncryptionPrivKey + decoder threadmodels.SigningPubKeyDecoder } type decreasedPermissionsParameters struct { @@ -19,28 +20,10 @@ type decreasedPermissionsParameters struct { startChange string } -func NewACLStateBuilder( - tree *Tree, - identity string, - key threadmodels.EncryptionPrivKey, - decoder threadmodels.SigningPubKeyDecoder) (*ACLStateBuilder, error) { - root := tree.Root() - if !root.IsSnapshot { - return nil, fmt.Errorf("root should always be a snapshot") - } - - snapshot := root.Content.GetAclData().GetAclSnapshot() - state, err := NewACLStateFromSnapshot(snapshot, identity, key, decoder) - if err != nil { - return nil, fmt.Errorf("could not build aclState from snapshot: %w", err) - } - +func NewACLStateBuilder(decoder threadmodels.SigningPubKeyDecoder) *ACLStateBuilder { return &ACLStateBuilder{ - tree: tree, - aclState: state, - identity: identity, - key: key, - }, nil + decoder: decoder, + } } func (sb *ACLStateBuilder) Build() (*ACLState, error) { @@ -48,6 +31,31 @@ func (sb *ACLStateBuilder) Build() (*ACLState, error) { return state, err } +func (sb *ACLStateBuilder) Init( + tree *Tree, + accountData *AccountData) error { + root := tree.Root() + if !root.IsSnapshot { + return fmt.Errorf("root should always be a snapshot") + } + + snapshot := root.Content.GetAclData().GetAclSnapshot() + state, err := NewACLStateFromSnapshot( + snapshot, + accountData.Identity, + accountData.EncKey, + sb.decoder) + if err != nil { + return fmt.Errorf("could not build aclState from snapshot: %w", err) + } + sb.tree = tree + sb.identity = accountData.Identity + sb.key = accountData.EncKey + 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 ( diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index f3e400a9..6d327c74 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -1,14 +1,10 @@ package data import ( - "context" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/core/thread" - "time" ) type ACLTreeBuilder struct { @@ -17,64 +13,23 @@ type ACLTreeBuilder struct { signingPubKeyDecoder threadmodels.SigningPubKeyDecoder tree *Tree thread threadmodels.Thread + + *changeLoader } func NewACLTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *ACLTreeBuilder { return &ACLTreeBuilder{ - cache: make(map[string]*Change), - identityKeys: make(map[string]threadmodels.SigningPubKey), signingPubKeyDecoder: decoder, - tree: &Tree{}, // TODO: add NewTree method thread: t, + changeLoader: newChangeLoader(t, decoder), } } -func (tb *ACLTreeBuilder) loadChange(id string) (ch *Change, err error) { - if ch, ok := tb.cache[id]; ok { - return ch, nil - } - - // TODO: Add virtual changes logic - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - change, err := tb.thread.GetChange(ctx, id) - if err != nil { - return nil, err - } - - aclChange := new(pb.ACLChange) - - // 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 - } - var verified bool - verified, err = tb.verify(aclChange.Identity, change.Payload, change.Signature) - if err != nil { - return - } - if !verified { - err = fmt.Errorf("the signature of the payload cannot be verified") - return - } - - ch, err = NewACLChange(id, aclChange) - tb.cache[id] = ch - - return ch, nil -} - -func (tb *ACLTreeBuilder) verify(identity string, payload, signature []byte) (isVerified bool, err error) { - identityKey, exists := tb.identityKeys[identity] - if !exists { - identityKey, err = tb.signingPubKeyDecoder.DecodeFromString(identity) - if err != nil { - return - } - tb.identityKeys[identity] = identityKey - } - return identityKey.Verify(payload, signature) +func (tb *ACLTreeBuilder) Init() { + tb.cache = make(map[string]*Change) + tb.identityKeys = make(map[string]threadmodels.SigningPubKey) + tb.tree = &Tree{} + tb.changeLoader.init(tb.cache, tb.identityKeys) } func (tb *ACLTreeBuilder) Build() (*Tree, error) { diff --git a/data/changeloader.go b/data/changeloader.go new file mode 100644 index 00000000..b58e4cf5 --- /dev/null +++ b/data/changeloader.go @@ -0,0 +1,80 @@ +package data + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/gogo/protobuf/proto" + "time" +) + +type changeLoader struct { + cache map[string]*Change + identityKeys map[string]threadmodels.SigningPubKey + signingPubKeyDecoder threadmodels.SigningPubKeyDecoder + thread threadmodels.Thread +} + +func newChangeLoader( + thread threadmodels.Thread, + signingPubKeyDecoder threadmodels.SigningPubKeyDecoder) *changeLoader { + return &changeLoader{ + signingPubKeyDecoder: signingPubKeyDecoder, + thread: thread, + } +} + +func (c *changeLoader) init(cache map[string]*Change, + identityKeys map[string]threadmodels.SigningPubKey) { + c.cache = cache + c.identityKeys = identityKeys +} + +func (c *changeLoader) loadChange(id string) (ch *Change, err error) { + if ch, ok := c.cache[id]; ok { + return ch, nil + } + + // TODO: Add virtual changes logic + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + change, err := c.thread.GetChange(ctx, id) + if err != nil { + return nil, err + } + + aclChange := new(pb.ACLChange) + + // 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 + } + var verified bool + verified, err = c.verify(aclChange.Identity, change.Payload, change.Signature) + if err != nil { + return + } + if !verified { + err = fmt.Errorf("the signature of the payload cannot be verified") + return + } + + ch, err = NewACLChange(id, aclChange) + c.cache[id] = ch + + return ch, nil +} + +func (c *changeLoader) verify(identity string, payload, signature []byte) (isVerified bool, err error) { + identityKey, exists := c.identityKeys[identity] + if !exists { + identityKey, err = c.signingPubKeyDecoder.DecodeFromString(identity) + if err != nil { + return + } + c.identityKeys[identity] = identityKey + } + return identityKey.Verify(payload, signature) +} diff --git a/data/treebuilder.go b/data/treebuilder.go index 3d7b7e3a..d590c37e 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -1,17 +1,13 @@ package data import ( - "context" "errors" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" //"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "github.com/gogo/protobuf/proto" "github.com/prometheus/common/log" "github.com/textileio/go-threads/core/thread" - "time" ) var ( @@ -25,64 +21,23 @@ type TreeBuilder struct { signingPubKeyDecoder threadmodels.SigningPubKeyDecoder tree *Tree thread threadmodels.Thread + + *changeLoader } func NewTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *TreeBuilder { return &TreeBuilder{ - cache: make(map[string]*Change), - identityKeys: make(map[string]threadmodels.SigningPubKey), signingPubKeyDecoder: decoder, - tree: &Tree{}, // TODO: add NewTree method thread: t, + changeLoader: newChangeLoader(t, decoder), } } -func (tb *TreeBuilder) loadChange(id string) (ch *Change, err error) { - if ch, ok := tb.cache[id]; ok { - return ch, nil - } - - // TODO: Add virtual changes logic - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - change, err := tb.thread.GetChange(ctx, id) - if err != nil { - return nil, err - } - - aclChange := new(pb.ACLChange) - - // 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 - } - var verified bool - verified, err = tb.verify(aclChange.Identity, change.Payload, change.Signature) - if err != nil { - return - } - if !verified { - err = fmt.Errorf("the signature of the payload cannot be verified") - return - } - - ch, err = NewChange(id, aclChange) - tb.cache[id] = ch - - return ch, nil -} - -func (tb *TreeBuilder) verify(identity string, payload, signature []byte) (isVerified bool, err error) { - identityKey, exists := tb.identityKeys[identity] - if !exists { - identityKey, err = tb.signingPubKeyDecoder.DecodeFromString(identity) - if err != nil { - return - } - tb.identityKeys[identity] = identityKey - } - return identityKey.Verify(payload, signature) +func (tb *TreeBuilder) Init() { + tb.cache = make(map[string]*Change) + tb.identityKeys = make(map[string]threadmodels.SigningPubKey) + tb.tree = &Tree{} + tb.changeLoader.init(tb.cache, tb.identityKeys) } func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) { From 4e8585bf9ae56f1ea711d5bf1cb9af22a23ff92c Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 5 Jul 2022 15:58:11 +0200 Subject: [PATCH 16/53] WIP change init logic --- data/aclstatebuilder.go | 15 +++++++-------- data/documentstatebuilder.go | 13 +++++++++---- data/snapshotvalidator.go | 35 +++++++++++++++++------------------ 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/data/aclstatebuilder.go b/data/aclstatebuilder.go index 9ac3ddff..0f42b9a2 100644 --- a/data/aclstatebuilder.go +++ b/data/aclstatebuilder.go @@ -20,9 +20,11 @@ type decreasedPermissionsParameters struct { startChange string } -func NewACLStateBuilder(decoder threadmodels.SigningPubKeyDecoder) *ACLStateBuilder { +func NewACLStateBuilder(decoder threadmodels.SigningPubKeyDecoder, accountData *AccountData) *ACLStateBuilder { return &ACLStateBuilder{ - decoder: decoder, + decoder: decoder, + identity: accountData.Identity, + key: accountData.EncKey, } } @@ -32,8 +34,7 @@ func (sb *ACLStateBuilder) Build() (*ACLState, error) { } func (sb *ACLStateBuilder) Init( - tree *Tree, - accountData *AccountData) error { + tree *Tree) error { root := tree.Root() if !root.IsSnapshot { return fmt.Errorf("root should always be a snapshot") @@ -42,15 +43,13 @@ func (sb *ACLStateBuilder) Init( snapshot := root.Content.GetAclData().GetAclSnapshot() state, err := NewACLStateFromSnapshot( snapshot, - accountData.Identity, - accountData.EncKey, + sb.identity, + sb.key, sb.decoder) if err != nil { return fmt.Errorf("could not build aclState from snapshot: %w", err) } sb.tree = tree - sb.identity = accountData.Identity - sb.key = accountData.EncKey sb.aclState = state return nil diff --git a/data/documentstatebuilder.go b/data/documentstatebuilder.go index 3ab6e677..7bf74ffc 100644 --- a/data/documentstatebuilder.go +++ b/data/documentstatebuilder.go @@ -4,20 +4,25 @@ import ( "fmt" ) +// example -> + type documentStateBuilder struct { tree *Tree aclState *ACLState // TODO: decide if this is needed or not stateProvider InitialStateProvider } -func newDocumentStateBuilder(tree *Tree, state *ACLState, stateProvider InitialStateProvider) *documentStateBuilder { +func newDocumentStateBuilder(stateProvider InitialStateProvider) *documentStateBuilder { return &documentStateBuilder{ - tree: tree, - aclState: state, stateProvider: stateProvider, } } +func (d *documentStateBuilder) init(aclState *ACLState, tree *Tree) { + d.tree = tree + d.aclState = aclState +} + // TODO: we should probably merge the two builders into one func (d *documentStateBuilder) build() (s DocumentState, err error) { var ( @@ -45,7 +50,7 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) { return true } if c.DecryptedDocumentChange != nil { - _, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id) + s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id) if err != nil { return false } diff --git a/data/snapshotvalidator.go b/data/snapshotvalidator.go index 29a7ee43..58de1b03 100644 --- a/data/snapshotvalidator.go +++ b/data/snapshotvalidator.go @@ -6,32 +6,31 @@ import ( ) type SnapshotValidator struct { - aclTree *Tree - identity string - key threadmodels.EncryptionPrivKey - decoder threadmodels.SigningPubKeyDecoder + aclTree *Tree + identity string + key threadmodels.EncryptionPrivKey + decoder threadmodels.SigningPubKeyDecoder + stateBuilder *ACLStateBuilder } func NewSnapshotValidator( - aclTree *Tree, - identity string, - key threadmodels.EncryptionPrivKey, - decoder threadmodels.SigningPubKeyDecoder) *SnapshotValidator { + decoder threadmodels.SigningPubKeyDecoder, + accountData *AccountData) *SnapshotValidator { return &SnapshotValidator{ - aclTree: aclTree, - identity: identity, - key: key, - decoder: decoder, + identity: accountData.Identity, + key: accountData.EncKey, + decoder: decoder, + stateBuilder: NewACLStateBuilder(decoder, accountData), } } -func (s *SnapshotValidator) ValidateSnapshot(ch *Change) (bool, error) { - stateBuilder, err := NewACLStateBuilder(s.aclTree, s.identity, s.key, s.decoder) - if err != nil { - return false, err - } +func (s *SnapshotValidator) Init(aclTree *Tree) error { + s.aclTree = aclTree + return s.stateBuilder.Init(aclTree) +} - st, found, err := 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 } From 99d67d531b2ede6b9c887316709ec5c5359c5c88 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 5 Jul 2022 17:21:45 +0200 Subject: [PATCH 17/53] Change init logic and fix tests --- data/acltreebuilder.go | 5 ++++- data/changeloader.go | 7 +++++-- data/document.go | 4 ++-- data/threadhelpers.go | 16 +++++++++++++--- data/treebuilder.go | 5 ++++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index 6d327c74..99289d41 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -21,7 +21,10 @@ func NewACLTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKey return &ACLTreeBuilder{ signingPubKeyDecoder: decoder, thread: t, - changeLoader: newChangeLoader(t, decoder), + changeLoader: newChangeLoader( + t, + decoder, + NewACLChange), } } diff --git a/data/changeloader.go b/data/changeloader.go index b58e4cf5..700fd0d6 100644 --- a/data/changeloader.go +++ b/data/changeloader.go @@ -14,14 +14,17 @@ type changeLoader struct { identityKeys map[string]threadmodels.SigningPubKey signingPubKeyDecoder threadmodels.SigningPubKeyDecoder thread threadmodels.Thread + changeCreator func(id string, ch *pb.ACLChange) (*Change, error) } func newChangeLoader( thread threadmodels.Thread, - signingPubKeyDecoder threadmodels.SigningPubKeyDecoder) *changeLoader { + signingPubKeyDecoder threadmodels.SigningPubKeyDecoder, + changeCreator func(id string, ch *pb.ACLChange) (*Change, error)) *changeLoader { return &changeLoader{ signingPubKeyDecoder: signingPubKeyDecoder, thread: thread, + changeCreator: changeCreator, } } @@ -61,7 +64,7 @@ func (c *changeLoader) loadChange(id string) (ch *Change, err error) { return } - ch, err = NewACLChange(id, aclChange) + ch, err = c.changeCreator(id, aclChange) c.cache[id] = ch return ch, nil diff --git a/data/document.go b/data/document.go index 1fb26417..375fede1 100644 --- a/data/document.go +++ b/data/document.go @@ -47,9 +47,9 @@ func (d *Document) Update(changes []*pb.ACLChange) (DocumentState, UpdateResult, } func (d *Document) Build() (DocumentState, error) { - treeBuilder := NewTreeBuilder(d.thread, threadmodels.NewEd25519Decoder()) + //treeBuilder := NewTreeBuilder(d.thread, threadmodels.NewEd25519Decoder()) - return treeBuilder.Build(fromStart) + //return treeBuilder.Build(fromStart) return nil, nil } diff --git a/data/threadhelpers.go b/data/threadhelpers.go index ab179df9..4841c065 100644 --- a/data/threadhelpers.go +++ b/data/threadhelpers.go @@ -12,6 +12,7 @@ type ACLContext struct { func createTreeFromThread(t threadmodels.Thread, fromStart bool) (*Tree, error) { treeBuilder := NewTreeBuilder(t, threadmodels.NewEd25519Decoder()) + treeBuilder.Init() return treeBuilder.Build(fromStart) } @@ -27,14 +28,21 @@ func createACLStateFromThread( return nil, err } + accountData := &AccountData{ + Identity: identity, + EncKey: key, + } + aclTreeBuilder := NewACLTreeBuilder(t, decoder) + aclTreeBuilder.Init() aclTree, err := aclTreeBuilder.Build() if err != nil { return nil, err } if !fromStart { - snapshotValidator := NewSnapshotValidator(aclTree, identity, key, decoder) + snapshotValidator := NewSnapshotValidator(decoder, accountData) + snapshotValidator.Init(aclTree) valid, err := snapshotValidator.ValidateSnapshot(tree.root) if err != nil { return nil, err @@ -45,7 +53,8 @@ func createACLStateFromThread( } } - aclBuilder, err := NewACLStateBuilder(tree, identity, key, decoder) + aclBuilder := NewACLStateBuilder(decoder, accountData) + err = aclBuilder.Init(tree) if err != nil { return nil, err } @@ -71,7 +80,8 @@ func createDocumentStateFromThread( return nil, err } - docStateBuilder := newDocumentStateBuilder(context.Tree, context.ACLState, provider) + docStateBuilder := newDocumentStateBuilder(provider) + docStateBuilder.init(context.ACLState, context.Tree) docState, err := docStateBuilder.build() if err != nil { return nil, err diff --git a/data/treebuilder.go b/data/treebuilder.go index d590c37e..97054699 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -29,7 +29,10 @@ func NewTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDec return &TreeBuilder{ signingPubKeyDecoder: decoder, thread: t, - changeLoader: newChangeLoader(t, decoder), + changeLoader: newChangeLoader( + t, + decoder, + NewChange), } } From db0609616955574c1f9c2821b8222d5e64e31a86 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 5 Jul 2022 18:09:45 +0200 Subject: [PATCH 18/53] Add Build method to document --- data/document.go | 84 +++++++++++++++++++++++++++++++++-------- data/documentcontext.go | 32 ++-------------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/data/document.go b/data/document.go index 375fede1..ebad98dc 100644 --- a/data/document.go +++ b/data/document.go @@ -11,14 +11,18 @@ type AccountData struct { } type Document struct { - thread threadmodels.Thread - stateProvider InitialStateProvider - accountData AccountData - decoder threadmodels.SigningPubKeyDecoder - aclTree *Tree - fullTree *Tree - aclTreeBuilder *ACLTreeBuilder - aclStateBuilder *ACLStateBuilder + thread threadmodels.Thread + stateProvider InitialStateProvider + accountData *AccountData + decoder threadmodels.SigningPubKeyDecoder + + treeBuilder *TreeBuilder + aclTreeBuilder *ACLTreeBuilder + aclStateBuilder *ACLStateBuilder + snapshotValidator *SnapshotValidator + docStateBuilder *documentStateBuilder + + docContext *documentContext } type UpdateResult int @@ -33,12 +37,19 @@ const ( func NewDocument( thread threadmodels.Thread, stateProvider InitialStateProvider, - accountData AccountData) *Document { + accountData *AccountData) *Document { + decoder := threadmodels.NewEd25519Decoder() return &Document{ - thread: thread, - stateProvider: stateProvider, - accountData: accountData, - decoder: threadmodels.NewEd25519Decoder(), + thread: thread, + stateProvider: stateProvider, + accountData: accountData, + decoder: decoder, + aclTreeBuilder: NewACLTreeBuilder(thread, decoder), + treeBuilder: NewTreeBuilder(thread, decoder), + snapshotValidator: NewSnapshotValidator(decoder, accountData), + aclStateBuilder: NewACLStateBuilder(decoder, accountData), + docStateBuilder: newDocumentStateBuilder(stateProvider), + docContext: &documentContext{}, } } @@ -47,10 +58,51 @@ func (d *Document) Update(changes []*pb.ACLChange) (DocumentState, UpdateResult, } func (d *Document) Build() (DocumentState, error) { - //treeBuilder := NewTreeBuilder(d.thread, threadmodels.NewEd25519Decoder()) + return d.build(false) +} - //return treeBuilder.Build(fromStart) - return nil, nil +func (d *Document) build(fromStart bool) (DocumentState, error) { + d.treeBuilder.Init() + d.aclTreeBuilder.Init() + + var err error + d.docContext.fullTree, err = d.treeBuilder.Build(fromStart) + if err != nil { + return nil, err + } + + d.docContext.aclTree, err = d.aclTreeBuilder.Build() + if err != nil { + return nil, err + } + + if !fromStart { + d.snapshotValidator.Init(d.docContext.aclTree) + valid, err := d.snapshotValidator.ValidateSnapshot(d.docContext.fullTree.root) + if err != nil { + return nil, err + } + if !valid { + return d.build(true) + } + } + err = d.aclStateBuilder.Init(d.docContext.fullTree) + if err != nil { + return nil, err + } + + d.docContext.aclState, err = d.aclStateBuilder.Build() + if err != nil { + return nil, err + } + + d.docStateBuilder.init(d.docContext.aclState, d.docContext.fullTree) + d.docContext.docState, err = d.docStateBuilder.build() + if err != nil { + return nil, err + } + + return d.docContext.docState, nil } func (d *Document) State() DocumentState { diff --git a/data/documentcontext.go b/data/documentcontext.go index eb4ff5cd..5d723e9a 100644 --- a/data/documentcontext.go +++ b/data/documentcontext.go @@ -1,32 +1,8 @@ package data -import "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - type documentContext struct { - aclTree *Tree - fullTree *Tree - identity string - encryptionKey threadmodels.EncryptionPrivKey - decoder threadmodels.SigningPubKeyDecoder - aclState *ACLState - docState DocumentState - changeCache map[string]*Change - identityKeys map[string]threadmodels.SigningPubKey -} - -func newDocumentContext( - identity string, - encryptionKey threadmodels.EncryptionPrivKey, - decoder threadmodels.SigningPubKeyDecoder) *documentContext { - return &documentContext{ - aclTree: &Tree{}, - fullTree: &Tree{}, - identity: identity, - encryptionKey: encryptionKey, - decoder: decoder, - aclState: nil, - docState: nil, - changeCache: make(map[string]*Change), - identityKeys: make(map[string]threadmodels.SigningPubKey), - } + aclTree *Tree + fullTree *Tree + aclState *ACLState + docState DocumentState } From 3ad690d624f69c92f0373b89c7026610e3549a0d Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Wed, 6 Jul 2022 21:51:00 +0200 Subject: [PATCH 19/53] WIP document update --- data/change.go | 8 ++-- data/changeloader.go | 40 ++++++++++-------- data/document.go | 63 +++++++++++++++++++++++++---- data/documentstatebuilder.go | 16 ++++++++ data/threadbuilder/threadbuilder.go | 20 +++++++++ data/threadmodels/models.go | 8 ++-- 6 files changed, 124 insertions(+), 31 deletions(-) diff --git a/data/change.go b/data/change.go index eabc4128..906eb7c8 100644 --- a/data/change.go +++ b/data/change.go @@ -38,7 +38,7 @@ func (ch *Change) IsACLChange() bool { return ch.Content.GetAclData() != nil } -func NewChange(id string, ch *pb.ACLChange) (*Change, error) { +func NewChange(id string, ch *pb.ACLChange) *Change { return &Change{ Next: nil, PreviousIds: ch.TreeHeadIds, @@ -46,10 +46,10 @@ func NewChange(id string, ch *pb.ACLChange) (*Change, error) { Content: ch, SnapshotId: ch.SnapshotBaseId, IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - }, nil + } } -func NewACLChange(id string, ch *pb.ACLChange) (*Change, error) { +func NewACLChange(id string, ch *pb.ACLChange) *Change { return &Change{ Next: nil, PreviousIds: ch.AclHeadIds, @@ -57,5 +57,5 @@ func NewACLChange(id string, ch *pb.ACLChange) (*Change, error) { Content: ch, SnapshotId: ch.SnapshotBaseId, IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - }, nil + } } diff --git a/data/changeloader.go b/data/changeloader.go index 700fd0d6..5382aada 100644 --- a/data/changeloader.go +++ b/data/changeloader.go @@ -14,13 +14,13 @@ type changeLoader struct { identityKeys map[string]threadmodels.SigningPubKey signingPubKeyDecoder threadmodels.SigningPubKeyDecoder thread threadmodels.Thread - changeCreator func(id string, ch *pb.ACLChange) (*Change, error) + changeCreator func(id string, ch *pb.ACLChange) *Change } func newChangeLoader( thread threadmodels.Thread, signingPubKeyDecoder threadmodels.SigningPubKeyDecoder, - changeCreator func(id string, ch *pb.ACLChange) (*Change, error)) *changeLoader { + changeCreator func(id string, ch *pb.ACLChange) *Change) *changeLoader { return &changeLoader{ signingPubKeyDecoder: signingPubKeyDecoder, thread: thread, @@ -48,23 +48,12 @@ func (c *changeLoader) loadChange(id string) (ch *Change, err error) { return nil, err } - aclChange := new(pb.ACLChange) - - // 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 - } - var verified bool - verified, err = c.verify(aclChange.Identity, change.Payload, change.Signature) + aclChange, err := c.makeVerifiedACLChange(change) if err != nil { - return - } - if !verified { - err = fmt.Errorf("the signature of the payload cannot be verified") - return + return nil, err } - ch, err = c.changeCreator(id, aclChange) + ch = c.changeCreator(id, aclChange) c.cache[id] = ch return ch, nil @@ -81,3 +70,22 @@ func (c *changeLoader) verify(identity string, payload, signature []byte) (isVer } return identityKey.Verify(payload, signature) } + +func (c *changeLoader) makeVerifiedACLChange(change *threadmodels.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 + if err = proto.Unmarshal(change.Payload, aclChange); err != nil { + return + } + var verified bool + verified, err = c.verify(aclChange.Identity, change.Payload, change.Signature) + if err != nil { + return + } + if !verified { + err = fmt.Errorf("the signature of the payload cannot be verified") + return + } + return +} diff --git a/data/document.go b/data/document.go index ebad98dc..45eaa6a4 100644 --- a/data/document.go +++ b/data/document.go @@ -1,7 +1,7 @@ package data import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" ) @@ -28,10 +28,9 @@ type Document struct { type UpdateResult int const ( - UpdateResultAppend = iota + UpdateResultNoAction = iota + UpdateResultAppend UpdateResultRebuild - UpdateResultExists - UpdateResultNoAction ) func NewDocument( @@ -53,8 +52,54 @@ func NewDocument( } } -func (d *Document) Update(changes []*pb.ACLChange) (DocumentState, UpdateResult, error) { - return nil, 0, nil +func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) { + var treeChanges []*Change + + for _, ch := range changes { + aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch) + if err != nil { + return nil, UpdateResultNoAction, fmt.Errorf("change with id %s is incorrect: %w", ch.Id, err) + } + + treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) + treeChanges = append(treeChanges, treeChange) + + err = d.thread.AddChange(ch) + if err != nil { + return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) + } + } + + for _, ch := range treeChanges { + if ch.IsACLChange() { + res, err := d.Build() + return res, UpdateResultRebuild, err + } + } + + prevHeads := d.docContext.fullTree.Heads() + mode := d.docContext.fullTree.Add(treeChanges...) + switch mode { + case Nothing: + return d.docContext.docState, UpdateResultNoAction, nil + case Rebuild: + res, err := d.Build() + return res, UpdateResultRebuild, err + default: + break + } + + // because for every new change we know it was after any of the previous heads + // each of previous heads must have same "Next" nodes + // so it doesn't matter which one we choose + // so we choose first one + newState, err := d.docStateBuilder.appendFrom(prevHeads[0]) + if err != nil { + res, _ := d.Build() + return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") + } + + return newState, UpdateResultAppend, nil } func (d *Document) Build() (DocumentState, error) { @@ -77,7 +122,11 @@ func (d *Document) build(fromStart bool) (DocumentState, error) { } if !fromStart { - 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) if err != nil { return nil, err diff --git a/data/documentstatebuilder.go b/data/documentstatebuilder.go index 7bf74ffc..c0ca38e9 100644 --- a/data/documentstatebuilder.go +++ b/data/documentstatebuilder.go @@ -62,3 +62,19 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) { } return s, err } + +func (d *documentStateBuilder) appendFrom(fromId string) (s DocumentState, err error) { + d.tree.Iterate(fromId, func(c *Change) (isContinue bool) { + if c.DecryptedDocumentChange != nil { + s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id) + if err != nil { + return false + } + } + return true + }) + if err != nil { + return + } + return s, err +} diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go index 991a5ae1..92424ab1 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/data/threadbuilder/threadbuilder.go @@ -30,6 +30,26 @@ type ThreadBuilder struct { keychain *Keychain } +func (t *ThreadBuilder) AddChange(change *threadmodels.RawChange) error { + //TODO implement me + panic("implement me") + return nil +} + +func (t *ThreadBuilder) MaybeHeads() []string { + //TODO implement me + panic("implement me") +} + +func (t *ThreadBuilder) SetMaybeHeads(heads []string) { + //TODO implement me + panic("implement me") +} + +func (t *ThreadBuilder) SetHeads(heads []string) { + //TODO implement me +} + func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { return &ThreadBuilder{ allChanges: make(map[string]*threadChange), diff --git a/data/threadmodels/models.go b/data/threadmodels/models.go index 63367f28..71b6d433 100644 --- a/data/threadmodels/models.go +++ b/data/threadmodels/models.go @@ -2,16 +2,16 @@ package threadmodels import ( "context" - "github.com/gogo/protobuf/proto" ) type Thread interface { ID() string Heads() []string + MaybeHeads() []string GetChange(ctx context.Context, recordID string) (*RawChange, error) - //SetHeads(heads []string) - //AddChanges(*pb.ACLChange) - PushChange(payload proto.Marshaler) (id string, err error) + SetHeads(heads []string) + SetMaybeHeads(heads []string) + AddChange(change *RawChange) error } type RawChange struct { From a09e7ca1cb3a1a68a2207fb2aa66d77e88e05fc7 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Wed, 6 Jul 2022 23:24:11 +0200 Subject: [PATCH 20/53] Add new heads logic --- data/acltreebuilder.go | 2 +- data/document.go | 12 +++ data/threadbuilder/invalidsnapshotexample.yml | 2 +- data/threadbuilder/keychain.go | 17 +++- data/threadbuilder/threadbuilder.go | 92 ++++++++++++------- data/threadbuilder/threadbuildergraph_nix.go | 6 +- data/threadbuilder/userjoinexample.yml | 2 +- .../threadbuilder/userremovebeforeexample.yml | 2 +- data/threadbuilder/userremoveexample.yml | 2 +- data/threadbuilder/validsnapshotexample.yml | 2 +- data/threadbuilder/ymlentities.go | 3 +- 11 files changed, 95 insertions(+), 47 deletions(-) diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index 99289d41..1308ec43 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -36,7 +36,7 @@ func (tb *ACLTreeBuilder) Init() { } func (tb *ACLTreeBuilder) Build() (*Tree, error) { - heads := tb.thread.Heads() + heads := tb.thread.MaybeHeads() aclHeads, err := tb.getACLHeads(heads) if err != nil { return nil, err diff --git a/data/document.go b/data/document.go index 45eaa6a4..4fbabcac 100644 --- a/data/document.go +++ b/data/document.go @@ -64,6 +64,8 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) treeChanges = append(treeChanges, treeChange) + // this already sets MaybeHeads to include new changes + // TODO: change this behaviour as non-obvious, because it is not evident from the interface err = d.thread.AddChange(ch) if err != nil { return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) @@ -99,6 +101,11 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") } + // setting all heads + d.thread.SetHeads(d.docContext.fullTree.Heads()) + // this should be the entrypoint when we build the document + d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) + return newState, UpdateResultAppend, nil } @@ -151,6 +158,11 @@ func (d *Document) build(fromStart bool) (DocumentState, error) { return nil, err } + // setting all heads + d.thread.SetHeads(d.docContext.fullTree.Heads()) + // this should be the entrypoint when we build the document + d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) + return d.docContext.docState, nil } diff --git a/data/threadbuilder/invalidsnapshotexample.yml b/data/threadbuilder/invalidsnapshotexample.yml index eb49fa95..fff71365 100644 --- a/data/threadbuilder/invalidsnapshotexample.yml +++ b/data/threadbuilder/invalidsnapshotexample.yml @@ -117,6 +117,6 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] -heads: +maybeHeads: - A.1.3 - B.1.2 diff --git a/data/threadbuilder/keychain.go b/data/threadbuilder/keychain.go index 69051c79..be3119ab 100644 --- a/data/threadbuilder/keychain.go +++ b/data/threadbuilder/keychain.go @@ -14,11 +14,13 @@ type SymKey struct { } type Keychain struct { - SigningKeys map[string]threadmodels.SigningPrivKey - EncryptionKeys map[string]threadmodels.EncryptionPrivKey - ReadKeys map[string]*SymKey - GeneratedIdentities map[string]string - coder *threadmodels.Ed25519SigningPubKeyDecoder + SigningKeys map[string]threadmodels.SigningPrivKey + SigningKeysByIdentity map[string]threadmodels.SigningPrivKey + EncryptionKeys map[string]threadmodels.EncryptionPrivKey + ReadKeys map[string]*SymKey + ReadKeysByHash map[uint64]*SymKey + GeneratedIdentities map[string]string + coder *threadmodels.Ed25519SigningPubKeyDecoder } func NewKeychain() *Keychain { @@ -71,6 +73,7 @@ func (k *Keychain) AddSigningKey(name string) { if err != nil { panic(err) } + k.SigningKeysByIdentity[res] = newPrivKey k.GeneratedIdentities[name] = res } @@ -87,6 +90,10 @@ func (k *Keychain) AddReadKey(name string) { Hash: hasher.Sum64(), Key: key, } + k.ReadKeysByHash[hasher.Sum64()] = &SymKey{ + Hash: hasher.Sum64(), + Key: key, + } } func (k *Keychain) AddKey(key string) { diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go index 92424ab1..c1de52b5 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/data/threadbuilder/threadbuilder.go @@ -20,36 +20,17 @@ type threadChange struct { readKey *SymKey signKey threadmodels.SigningPrivKey - changesData *pb.PlainTextChangeData + changesDataDecrypted []byte } type ThreadBuilder struct { threadId string allChanges map[string]*threadChange heads []string + maybeHeads []string keychain *Keychain } -func (t *ThreadBuilder) AddChange(change *threadmodels.RawChange) error { - //TODO implement me - panic("implement me") - return nil -} - -func (t *ThreadBuilder) MaybeHeads() []string { - //TODO implement me - panic("implement me") -} - -func (t *ThreadBuilder) SetMaybeHeads(heads []string) { - //TODO implement me - panic("implement me") -} - -func (t *ThreadBuilder) SetHeads(heads []string) { - //TODO implement me -} - func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { return &ThreadBuilder{ allChanges: make(map[string]*threadChange), @@ -92,17 +73,58 @@ func (t *ThreadBuilder) Heads() []string { return t.heads } +func (t *ThreadBuilder) AddChange(change *threadmodels.RawChange) error { + aclChange := new(pb.ACLChange) + var err error + + // 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 fmt.Errorf("could not unmarshall changes") + } + var changesData []byte + + // get correct readkey + readKey := t.keychain.ReadKeysByHash[aclChange.CurrentReadKeyHash] + if aclChange.ChangesData != nil { + changesData, err = readKey.Key.Decrypt(aclChange.ChangesData) + if err != nil { + return fmt.Errorf("failed to decrypt changes data: %w", err) + } + } + + // get correct signing key + signKey := t.keychain.SigningKeysByIdentity[aclChange.Identity] + t.maybeHeads = append(t.maybeHeads, change.Id) + + t.allChanges[change.Id] = &threadChange{ + ACLChange: aclChange, + id: change.Id, + readKey: readKey, + signKey: signKey, + changesDataDecrypted: changesData, + } + return nil +} + +func (t *ThreadBuilder) MaybeHeads() []string { + return t.maybeHeads +} + +func (t *ThreadBuilder) SetMaybeHeads(heads []string) { + // we should copy here instead of just setting the value + t.maybeHeads = heads +} + +func (t *ThreadBuilder) SetHeads(heads []string) { + // we should copy here instead of just setting the value + t.heads = heads +} + func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*threadmodels.RawChange, error) { rec := t.allChanges[recordID] - var encrypted []byte - if rec.changesData != nil { - m, err := proto.Marshal(rec.changesData) - if err != nil { - panic("should be able to marshal data!") - } - - encrypted, err = rec.readKey.Key.Encrypt(m) + if rec.changesDataDecrypted != nil { + encrypted, err := rec.readKey.Key.Encrypt(rec.changesDataDecrypted) if err != nil { panic("should be able to encrypt data with read key!") } @@ -162,9 +184,9 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { } } if len(ch.Changes) > 0 || ch.Snapshot != nil { - newChange.changesData = &pb.PlainTextChangeData{} + changesData := &pb.PlainTextChangeData{} if ch.Snapshot != nil { - newChange.changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) + changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) } if len(ch.Changes) > 0 { var changeContents []*pb.PlainTextChangeContent @@ -172,8 +194,13 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { aclChangeContent := t.parseDocumentChange(ch) changeContents = append(changeContents, aclChangeContent) } - newChange.changesData.Content = changeContents + changesData.Content = changeContents } + m, err := proto.Marshal(changesData) + if err != nil { + return + } + newChange.changesDataDecrypted = m } aclChange.CurrentReadKeyHash = k.Hash newChange.ACLChange = aclChange @@ -421,4 +448,5 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) { func (t *ThreadBuilder) parseHeads(thread *YMLThread) { t.heads = thread.Heads + t.maybeHeads = thread.MaybeHeads } diff --git a/data/threadbuilder/threadbuildergraph_nix.go b/data/threadbuilder/threadbuildergraph_nix.go index 1ea60a5a..d3d188b7 100644 --- a/data/threadbuilder/threadbuildergraph_nix.go +++ b/data/threadbuilder/threadbuildergraph_nix.go @@ -36,13 +36,13 @@ func (t *ThreadBuilder) Graph() (string, error) { style := "solid" if r.GetAclData() != nil { style = "filled" - } else if r.changesData != nil { + } else if r.changesDataDecrypted != nil { style = "dashed" } var chSymbs []string - if r.changesData != nil { - for _, chc := range r.changesData.Content { + if r.changesDataDecrypted != nil { + for _, chc := range r.changesDataDecrypted.Content { tp := fmt.Sprintf("%T", chc.Value) tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) res := "" diff --git a/data/threadbuilder/userjoinexample.yml b/data/threadbuilder/userjoinexample.yml index d41665ca..883ba5e1 100644 --- a/data/threadbuilder/userjoinexample.yml +++ b/data/threadbuilder/userjoinexample.yml @@ -99,5 +99,5 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] -heads: +maybeHeads: - "A.1.3" diff --git a/data/threadbuilder/userremovebeforeexample.yml b/data/threadbuilder/userremovebeforeexample.yml index fb214099..fe4d2b6e 100644 --- a/data/threadbuilder/userremovebeforeexample.yml +++ b/data/threadbuilder/userremovebeforeexample.yml @@ -101,6 +101,6 @@ graph: baseSnapshot: A.1.1 aclHeads: [A.1.2] treeHeads: [A.1.2] -heads: +maybeHeads: - "A.1.3" - "B.1.2" diff --git a/data/threadbuilder/userremoveexample.yml b/data/threadbuilder/userremoveexample.yml index 51fa2d76..ab131fb5 100644 --- a/data/threadbuilder/userremoveexample.yml +++ b/data/threadbuilder/userremoveexample.yml @@ -101,6 +101,6 @@ graph: aclSnapshot: A.1.1 aclHeads: [A.1.3] treeHeads: [A.1.3] -heads: +maybeHeads: - "A.1.4" - "B.1.2" diff --git a/data/threadbuilder/validsnapshotexample.yml b/data/threadbuilder/validsnapshotexample.yml index 2e3c60c6..2c3e49d0 100644 --- a/data/threadbuilder/validsnapshotexample.yml +++ b/data/threadbuilder/validsnapshotexample.yml @@ -124,7 +124,7 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] -heads: +maybeHeads: - "A.1.3" - "B.1.2" diff --git a/data/threadbuilder/ymlentities.go b/data/threadbuilder/ymlentities.go index 8052deb4..78775758 100644 --- a/data/threadbuilder/ymlentities.go +++ b/data/threadbuilder/ymlentities.go @@ -94,5 +94,6 @@ type YMLThread struct { TreeHeads []string `yaml:"treeHeads"` } `yaml:"graph"` - Heads []string `yaml:"heads"` + Heads []string `yaml:"heads"` + MaybeHeads []string `yaml:"maybeHeads"` } From 906d2b6614f7fa18dceed837e06cdd8ee29af6f3 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Wed, 6 Jul 2022 23:35:43 +0200 Subject: [PATCH 21/53] Fix some bugs --- data/threadbuilder/invalidsnapshotexample.yml | 1 + data/threadbuilder/keychain.go | 12 +++++++----- data/threadbuilder/threadbuildergraph_nix.go | 10 +++++++++- data/treebuilder.go | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/data/threadbuilder/invalidsnapshotexample.yml b/data/threadbuilder/invalidsnapshotexample.yml index fff71365..317e575b 100644 --- a/data/threadbuilder/invalidsnapshotexample.yml +++ b/data/threadbuilder/invalidsnapshotexample.yml @@ -120,3 +120,4 @@ graph: maybeHeads: - A.1.3 - B.1.2 + diff --git a/data/threadbuilder/keychain.go b/data/threadbuilder/keychain.go index be3119ab..501fb404 100644 --- a/data/threadbuilder/keychain.go +++ b/data/threadbuilder/keychain.go @@ -25,11 +25,13 @@ type Keychain struct { func NewKeychain() *Keychain { return &Keychain{ - SigningKeys: map[string]threadmodels.SigningPrivKey{}, - EncryptionKeys: map[string]threadmodels.EncryptionPrivKey{}, - GeneratedIdentities: map[string]string{}, - ReadKeys: map[string]*SymKey{}, - coder: threadmodels.NewEd25519Decoder(), + SigningKeys: map[string]threadmodels.SigningPrivKey{}, + SigningKeysByIdentity: map[string]threadmodels.SigningPrivKey{}, + EncryptionKeys: map[string]threadmodels.EncryptionPrivKey{}, + GeneratedIdentities: map[string]string{}, + ReadKeys: map[string]*SymKey{}, + ReadKeysByHash: map[uint64]*SymKey{}, + coder: threadmodels.NewEd25519Decoder(), } } diff --git a/data/threadbuilder/threadbuildergraph_nix.go b/data/threadbuilder/threadbuildergraph_nix.go index d3d188b7..30722a53 100644 --- a/data/threadbuilder/threadbuildergraph_nix.go +++ b/data/threadbuilder/threadbuildergraph_nix.go @@ -9,6 +9,8 @@ package threadbuilder import ( "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/gogo/protobuf/proto" "strings" "unicode" @@ -42,7 +44,13 @@ func (t *ThreadBuilder) Graph() (string, error) { var chSymbs []string if r.changesDataDecrypted != nil { - for _, chc := range r.changesDataDecrypted.Content { + res := &pb.PlainTextChangeData{} + err := proto.Unmarshal(r.changesDataDecrypted, res) + if err != nil { + return err + } + + for _, chc := range res.Content { tp := fmt.Sprintf("%T", chc.Value) tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) res := "" diff --git a/data/treebuilder.go b/data/treebuilder.go index 97054699..3bcdda49 100644 --- a/data/treebuilder.go +++ b/data/treebuilder.go @@ -44,7 +44,7 @@ func (tb *TreeBuilder) Init() { } func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) { - heads := tb.thread.Heads() + heads := tb.thread.MaybeHeads() if fromStart { if err := tb.buildTreeFromStart(heads); err != nil { From d08fb78357224732e6ea47e0e34bfcac75502eee Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 7 Jul 2022 08:47:11 +0200 Subject: [PATCH 22/53] Add updated changes to thread builder --- data/threadbuilder/threadbuilder.go | 147 +++++++++++++++---------- data/threadbuilder/userjoinexample.yml | 12 ++ data/threadbuilder/ymlentities.go | 44 ++++---- 3 files changed, 125 insertions(+), 78 deletions(-) diff --git a/data/threadbuilder/threadbuilder.go b/data/threadbuilder/threadbuilder.go index c1de52b5..411204a7 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/data/threadbuilder/threadbuilder.go @@ -24,17 +24,19 @@ type threadChange struct { } type ThreadBuilder struct { - threadId string - allChanges map[string]*threadChange - heads []string - maybeHeads []string - keychain *Keychain + threadId string + allChanges map[string]*threadChange + updatedChanges map[string]*threadChange + heads []string + maybeHeads []string + keychain *Keychain } func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { return &ThreadBuilder{ - allChanges: make(map[string]*threadChange), - keychain: keychain, + allChanges: make(map[string]*threadChange), + updatedChanges: make(map[string]*threadChange), + keychain: keychain, } } @@ -121,7 +123,20 @@ func (t *ThreadBuilder) SetHeads(heads []string) { } func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*threadmodels.RawChange, error) { - rec := t.allChanges[recordID] + return t.getChange(recordID, t.allChanges), nil +} + +func (t *ThreadBuilder) GetUpdatedChanges() []*threadmodels.RawChange { + var res []*threadmodels.RawChange + for _, ch := range t.updatedChanges { + rawCh := t.getChange(ch.id, t.updatedChanges) + res = append(res, rawCh) + } + return res +} + +func (t *ThreadBuilder) getChange(changeId string, m map[string]*threadChange) *threadmodels.RawChange { + rec := m[changeId] if rec.changesDataDecrypted != nil { encrypted, err := rec.readKey.Key.Encrypt(rec.changesDataDecrypted) @@ -145,13 +160,9 @@ func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*thread transformedRec := &threadmodels.RawChange{ Payload: aclMarshaled, Signature: signature, - Id: recordID, + Id: changeId, } - return transformedRec, nil -} - -func (t *ThreadBuilder) PushChange(payload proto.Marshaler) (id string, err error) { - panic("implement me") + return transformedRec } func (t *ThreadBuilder) Parse(thread *YMLThread) { @@ -161,55 +172,66 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { t.keychain.ParseKeys(&thread.Keys) t.threadId = t.parseThreadId(thread.Description) for _, ch := range thread.Changes { - newChange := &threadChange{ - id: ch.Id, - } - k := t.keychain.GetKey(ch.ReadKey).(*SymKey) - newChange.readKey = k - newChange.signKey = t.keychain.SigningKeys[ch.Identity] - aclChange := &pb.ACLChange{} - aclChange.Identity = t.keychain.GetIdentity(ch.Identity) - if len(ch.AclChanges) > 0 || ch.AclSnapshot != nil { - aclChange.AclData = &pb.ACLChangeACLData{} - if ch.AclSnapshot != nil { - aclChange.AclData.AclSnapshot = t.parseACLSnapshot(ch.AclSnapshot) - } - if ch.AclChanges != nil { - var aclChangeContents []*pb.ACLChangeACLContentValue - for _, ch := range ch.AclChanges { - aclChangeContent := t.parseACLChange(ch) - aclChangeContents = append(aclChangeContents, aclChangeContent) - } - aclChange.AclData.AclContent = aclChangeContents - } - } - if len(ch.Changes) > 0 || ch.Snapshot != nil { - changesData := &pb.PlainTextChangeData{} - if ch.Snapshot != nil { - changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) - } - if len(ch.Changes) > 0 { - var changeContents []*pb.PlainTextChangeContent - for _, ch := range ch.Changes { - aclChangeContent := t.parseDocumentChange(ch) - changeContents = append(changeContents, aclChangeContent) - } - changesData.Content = changeContents - } - m, err := proto.Marshal(changesData) - if err != nil { - return - } - newChange.changesDataDecrypted = m - } - aclChange.CurrentReadKeyHash = k.Hash - newChange.ACLChange = aclChange + newChange := t.parseChange(ch) t.allChanges[newChange.id] = newChange } + + for _, ch := range thread.UpdatedChanges { + newChange := t.parseChange(ch) + t.updatedChanges[newChange.id] = newChange + } + t.parseGraph(thread) t.parseHeads(thread) } +func (t *ThreadBuilder) parseChange(ch *Change) *threadChange { + newChange := &threadChange{ + id: ch.Id, + } + k := t.keychain.GetKey(ch.ReadKey).(*SymKey) + newChange.readKey = k + newChange.signKey = t.keychain.SigningKeys[ch.Identity] + aclChange := &pb.ACLChange{} + aclChange.Identity = t.keychain.GetIdentity(ch.Identity) + if len(ch.AclChanges) > 0 || ch.AclSnapshot != nil { + aclChange.AclData = &pb.ACLChangeACLData{} + if ch.AclSnapshot != nil { + aclChange.AclData.AclSnapshot = t.parseACLSnapshot(ch.AclSnapshot) + } + if ch.AclChanges != nil { + var aclChangeContents []*pb.ACLChangeACLContentValue + for _, ch := range ch.AclChanges { + aclChangeContent := t.parseACLChange(ch) + aclChangeContents = append(aclChangeContents, aclChangeContent) + } + aclChange.AclData.AclContent = aclChangeContents + } + } + if len(ch.Changes) > 0 || ch.Snapshot != nil { + changesData := &pb.PlainTextChangeData{} + if ch.Snapshot != nil { + changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) + } + if len(ch.Changes) > 0 { + var changeContents []*pb.PlainTextChangeContent + for _, ch := range ch.Changes { + aclChangeContent := t.parseDocumentChange(ch) + changeContents = append(changeContents, aclChangeContent) + } + changesData.Content = changeContents + } + m, err := proto.Marshal(changesData) + if err != nil { + return nil + } + newChange.changesDataDecrypted = m + } + aclChange.CurrentReadKeyHash = k.Hash + newChange.ACLChange = aclChange + return newChange +} + func (t *ThreadBuilder) parseThreadId(description *ThreadDescription) string { if description == nil { panic("no author in thread") @@ -415,8 +437,8 @@ func (t *ThreadBuilder) convertPermission(perm string) pb.ACLChangeUserPermissio func (t *ThreadBuilder) traverseFromHeads(f func(t *threadChange) error) error { uniqMap := map[string]struct{}{} - stack := make([]string, len(t.heads), 10) - copy(stack, t.heads) + stack := make([]string, len(t.maybeHeads), 10) + copy(stack, t.maybeHeads) for len(stack) > 0 { id := stack[len(stack)-1] stack = stack[:len(stack)-1] @@ -444,6 +466,13 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) { rec.TreeHeadIds = node.TreeHeads rec.SnapshotBaseId = node.BaseSnapshot } + + for _, node := range thread.UpdatedGraph { + rec := t.updatedChanges[node.Id] + rec.AclHeadIds = node.ACLHeads + rec.TreeHeadIds = node.TreeHeads + rec.SnapshotBaseId = node.BaseSnapshot + } } func (t *ThreadBuilder) parseHeads(thread *YMLThread) { diff --git a/data/threadbuilder/userjoinexample.yml b/data/threadbuilder/userjoinexample.yml index 883ba5e1..59e32c01 100644 --- a/data/threadbuilder/userjoinexample.yml +++ b/data/threadbuilder/userjoinexample.yml @@ -101,3 +101,15 @@ graph: treeHeads: [B.1.1] maybeHeads: - "A.1.3" +updatedChanges: + - id: B.1.3 + identity: B + changes: + - textAppend: + text: "first" + readKey: key.Read.1 +updatedGraph: + - id: B.1.3 + baseSnapshot: A.1.1 + aclHeads: [ B.1.1 ] + treeHeads: [ B.1.2 ] diff --git a/data/threadbuilder/ymlentities.go b/data/threadbuilder/ymlentities.go index 78775758..33a9a34e 100644 --- a/data/threadbuilder/ymlentities.go +++ b/data/threadbuilder/ymlentities.go @@ -70,29 +70,35 @@ type PlainTextChange struct { } `yaml:"textAppend"` } +type GraphNode struct { + Id string `yaml:"id"` + BaseSnapshot string `yaml:"baseSnapshot"` + AclSnapshot string `yaml:"aclSnapshot"` + ACLHeads []string `yaml:"aclHeads"` + TreeHeads []string `yaml:"treeHeads"` +} + +type Change struct { + Id string `yaml:"id"` + Identity string `yaml:"identity"` + + AclSnapshot *ACLSnapshot `yaml:"aclSnapshot"` + Snapshot *PlainTextSnapshot `yaml:"snapshot"` + AclChanges []*ACLChange `yaml:"aclChanges"` + Changes []*PlainTextChange `yaml:"changes"` + + ReadKey string `yaml:"readKey"` +} + type YMLThread struct { - Description *ThreadDescription `yaml:"thread"` - Changes []struct { - Id string `yaml:"id"` - Identity string `yaml:"identity"` - - AclSnapshot *ACLSnapshot `yaml:"aclSnapshot"` - Snapshot *PlainTextSnapshot `yaml:"snapshot"` - AclChanges []*ACLChange `yaml:"aclChanges"` - Changes []*PlainTextChange `yaml:"changes"` - - ReadKey string `yaml:"readKey"` - } `yaml:"changes"` + Description *ThreadDescription `yaml:"thread"` + Changes []*Change `yaml:"changes"` + UpdatedChanges []*Change `yaml:"updatedChanges"` Keys Keys `yaml:"keys"` - Graph []struct { - Id string `yaml:"id"` - BaseSnapshot string `yaml:"baseSnapshot"` - AclSnapshot string `yaml:"aclSnapshot"` - ACLHeads []string `yaml:"aclHeads"` - TreeHeads []string `yaml:"treeHeads"` - } `yaml:"graph"` + Graph []*GraphNode `yaml:"graph"` + UpdatedGraph []*GraphNode `yaml:"updatedGraph"` Heads []string `yaml:"heads"` MaybeHeads []string `yaml:"maybeHeads"` From a18938d0b11f8ce58d45355e100c49e5d641c20f Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 7 Jul 2022 08:53:47 +0200 Subject: [PATCH 23/53] Add document build test --- data/document.go | 1 + data/document_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 data/document_test.go diff --git a/data/document.go b/data/document.go index 4fbabcac..ca52f7f5 100644 --- a/data/document.go +++ b/data/document.go @@ -37,6 +37,7 @@ func NewDocument( thread threadmodels.Thread, stateProvider InitialStateProvider, accountData *AccountData) *Document { + decoder := threadmodels.NewEd25519Decoder() return &Document{ thread: thread, diff --git a/data/document_test.go b/data/document_test.go new file mode 100644 index 00000000..b3abc82b --- /dev/null +++ b/data/document_test.go @@ -0,0 +1,28 @@ +package data + +import ( + "github.com/stretchr/testify/assert" + "testing" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadbuilder" +) + +func TestDocument_Build(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + accountData := &AccountData{ + Identity: keychain.GetIdentity("A"), + EncKey: keychain.EncryptionKeys["A"], + } + doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) + res, err := doc.Build() + if err != nil { + t.Fatal(err) + } + + st := res.(*PlainTextDocumentState) + assert.Equal(t, st.Text, "some text|first") +} From 17c556607a051e79cf8a76114bf18bdaac4652b0 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 7 Jul 2022 09:18:55 +0200 Subject: [PATCH 24/53] Add simple update test --- data/document.go | 33 ++++++++++++++++++++------ data/document_test.go | 25 +++++++++++++++++++ data/documentstatebuilder.go | 6 ++++- data/threadbuilder/userjoinexample.yml | 2 +- 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/data/document.go b/data/document.go index ca52f7f5..908479d3 100644 --- a/data/document.go +++ b/data/document.go @@ -28,7 +28,7 @@ type Document struct { type UpdateResult int const ( - UpdateResultNoAction = iota + UpdateResultNoAction UpdateResult = iota UpdateResultAppend UpdateResultRebuild ) @@ -56,6 +56,7 @@ func NewDocument( func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) { var treeChanges []*Change + var foundACLChange bool for _, ch := range changes { aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch) if err != nil { @@ -71,13 +72,14 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up if err != nil { return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) } + if treeChange.IsACLChange() { + foundACLChange = true + } } - for _, ch := range treeChanges { - if ch.IsACLChange() { - res, err := d.Build() - return res, UpdateResultRebuild, err - } + if foundACLChange { + res, err := d.Build() + return res, UpdateResultRebuild, err } prevHeads := d.docContext.fullTree.Heads() @@ -92,11 +94,28 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up break } + // decrypting everything, because we have no new keys + for _, ch := range treeChanges { + if ch.Content.GetChangesData() != nil { + key, exists := d.docContext.aclState.userReadKeys[ch.Content.CurrentReadKeyHash] + if !exists { + err := fmt.Errorf("failed to find key with hash: %d", ch.Content.CurrentReadKeyHash) + return nil, UpdateResultNoAction, err + } + + err := ch.DecryptContents(key) + if err != nil { + err = fmt.Errorf("failed to decrypt contents for hash: %d", ch.Content.CurrentReadKeyHash) + return nil, UpdateResultNoAction, err + } + } + } + // because for every new change we know it was after any of the previous heads // each of previous heads must have same "Next" nodes // so it doesn't matter which one we choose // so we choose first one - newState, err := d.docStateBuilder.appendFrom(prevHeads[0]) + newState, err := d.docStateBuilder.appendFrom(prevHeads[0], d.docContext.docState) if err != nil { res, _ := d.Build() return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") diff --git a/data/document_test.go b/data/document_test.go index b3abc82b..6cd128a6 100644 --- a/data/document_test.go +++ b/data/document_test.go @@ -26,3 +26,28 @@ func TestDocument_Build(t *testing.T) { st := res.(*PlainTextDocumentState) assert.Equal(t, st.Text, "some text|first") } + +func TestDocument_Update(t *testing.T) { + thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thread.GetKeychain() + accountData := &AccountData{ + Identity: keychain.GetIdentity("A"), + EncKey: keychain.EncryptionKeys["A"], + } + doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) + res, err := doc.Build() + if err != nil { + t.Fatal(err) + } + + st := res.(*PlainTextDocumentState) + assert.Equal(t, st.Text, "some text|first") + + rawChs := thread.GetUpdatedChanges() + res, updateResult, err := doc.Update(rawChs...) + assert.Equal(t, updateResult, UpdateResultAppend) + assert.Equal(t, res.(*PlainTextDocumentState).Text, "some text|first|second") +} diff --git a/data/documentstatebuilder.go b/data/documentstatebuilder.go index c0ca38e9..241c3acc 100644 --- a/data/documentstatebuilder.go +++ b/data/documentstatebuilder.go @@ -63,8 +63,12 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) { return s, err } -func (d *documentStateBuilder) appendFrom(fromId string) (s DocumentState, err error) { +func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s DocumentState, err error) { + s = init d.tree.Iterate(fromId, func(c *Change) (isContinue bool) { + if c.Id == fromId { + return true + } if c.DecryptedDocumentChange != nil { s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id) if err != nil { diff --git a/data/threadbuilder/userjoinexample.yml b/data/threadbuilder/userjoinexample.yml index 59e32c01..02092a8a 100644 --- a/data/threadbuilder/userjoinexample.yml +++ b/data/threadbuilder/userjoinexample.yml @@ -106,7 +106,7 @@ updatedChanges: identity: B changes: - textAppend: - text: "first" + text: "second" readKey: key.Read.1 updatedGraph: - id: B.1.3 From 9fe502057ea68a6b3dd3628b92590af375dfe0e1 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 7 Jul 2022 11:11:34 +0200 Subject: [PATCH 25/53] WIP change creation logic --- data/aclstatebuilder.go | 3 +- data/acltreebuilder.go | 2 +- data/document.go | 93 +++++++++++++ data/documentstatebuilder.go | 1 + data/threadbuilder/userjoinexampleupdate.yml | 133 +++++++++++++++++++ 5 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 data/threadbuilder/userjoinexampleupdate.yml diff --git a/data/aclstatebuilder.go b/data/aclstatebuilder.go index 0f42b9a2..2dbe7e5d 100644 --- a/data/aclstatebuilder.go +++ b/data/aclstatebuilder.go @@ -33,8 +33,7 @@ func (sb *ACLStateBuilder) Build() (*ACLState, error) { 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") diff --git a/data/acltreebuilder.go b/data/acltreebuilder.go index 1308ec43..a5f994e7 100644 --- a/data/acltreebuilder.go +++ b/data/acltreebuilder.go @@ -128,7 +128,7 @@ func (tb *ACLTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, er } precedingHeads, err := tb.getPrecedingACLHeads(head) if err != nil { - return nil, err + continue } for _, aclHead := range precedingHeads { diff --git a/data/document.go b/data/document.go index 908479d3..2888f862 100644 --- a/data/document.go +++ b/data/document.go @@ -2,11 +2,15 @@ package data import ( "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" + "github.com/gogo/protobuf/proto" ) type AccountData struct { Identity string + SignKey threadmodels.SigningPrivKey EncKey threadmodels.EncryptionPrivKey } @@ -33,6 +37,12 @@ const ( UpdateResultRebuild ) +type CreateChangePayload struct { + ChangesData proto.Marshaler + ACLData *pb.ACLChangeACLData + Id string // TODO: this is just for testing, because id should be created automatically from content +} + func NewDocument( thread threadmodels.Thread, stateProvider InitialStateProvider, @@ -53,6 +63,59 @@ func NewDocument( } } +func (d *Document) Create(payload *CreateChangePayload) error { + // TODO: add snapshot creation logic + marshalled, err := payload.ChangesData.Marshal() + if err != nil { + return err + } + + encrypted, err := d.docContext.aclState.userReadKeys[d.docContext.aclState.currentReadKeyHash]. + Encrypt(marshalled) + if err != nil { + return err + } + + aclChange := &pb.ACLChange{ + TreeHeadIds: d.docContext.fullTree.Heads(), + AclHeadIds: d.getACLHeads(), + SnapshotBaseId: d.docContext.fullTree.RootId(), + AclData: payload.ACLData, + ChangesData: encrypted, + CurrentReadKeyHash: d.docContext.aclState.currentReadKeyHash, + Timestamp: 0, + Identity: d.accountData.Identity, + } + + // TODO: add CID creation logic based on content + ch := NewChange(payload.Id, aclChange) + ch.DecryptedDocumentChange = marshalled + + fullMarshalledChange, err := proto.Marshal(aclChange) + if err != nil { + return err + } + signature, err := d.accountData.SignKey.Sign(fullMarshalledChange) + if err != nil { + return err + } + d.docContext.fullTree.AddFast(ch) + + err = d.thread.AddChange(&threadmodels.RawChange{ + Payload: marshalled, + Signature: signature, + Id: payload.Id, + }) + if err != nil { + return err + } + + d.thread.SetHeads([]string{ch.Id}) + d.thread.SetMaybeHeads([]string{ch.Id}) + + return nil +} + func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) { var treeChanges []*Change @@ -133,6 +196,35 @@ func (d *Document) Build() (DocumentState, error) { return d.build(false) } +// TODO: this should not be the responsibility of Document, move it somewhere else after testing +func (d *Document) getACLHeads() []string { + var aclTreeHeads []string + for _, head := range d.docContext.fullTree.Heads() { + if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads + continue + } + precedingHeads := d.getPrecedingACLHeads(head) + + for _, aclHead := range precedingHeads { + if slice.FindPos(aclTreeHeads, aclHead) != -1 { + continue + } + aclTreeHeads = append(aclTreeHeads, aclHead) + } + } + return aclTreeHeads +} + +func (d *Document) getPrecedingACLHeads(head string) []string { + headChange := d.docContext.fullTree.attached[head] + + if headChange.Content.GetAclData() != nil { + return []string{head} + } else { + return headChange.Content.AclHeadIds + } +} + func (d *Document) build(fromStart bool) (DocumentState, error) { d.treeBuilder.Init() d.aclTreeBuilder.Init() @@ -143,6 +235,7 @@ func (d *Document) build(fromStart bool) (DocumentState, error) { return nil, err } + // TODO: remove this from context as this is used only to validate snapshot d.docContext.aclTree, err = d.aclTreeBuilder.Build() if err != nil { return nil, err diff --git a/data/documentstatebuilder.go b/data/documentstatebuilder.go index 241c3acc..c0cb55a9 100644 --- a/data/documentstatebuilder.go +++ b/data/documentstatebuilder.go @@ -64,6 +64,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 d.tree.Iterate(fromId, func(c *Change) (isContinue bool) { if c.Id == fromId { diff --git a/data/threadbuilder/userjoinexampleupdate.yml b/data/threadbuilder/userjoinexampleupdate.yml new file mode 100644 index 00000000..80f4fcda --- /dev/null +++ b/data/threadbuilder/userjoinexampleupdate.yml @@ -0,0 +1,133 @@ +thread: + author: A +changes: + - id: A.1.1 + identity: A + aclSnapshot: + userStates: + - identity: A + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + permission: admin + snapshot: + text: "some text" + aclChanges: + - userAdd: + identity: A + permission: admin + encryptionKey: key.Enc.A + encryptedReadKeys: [key.Read.1] + changes: + - textAppend: + text: "some text" + readKey: key.Read.1 + - id: A.1.2 + identity: A + aclChanges: + - userInvite: + acceptKey: key.Sign.Onetime1 + encryptionKey: key.Enc.Onetime1 + encryptedReadKeys: [key.Read.1] + permissions: writer + - userAdd: + identity: C + permission: reader + encryptionKey: key.Enc.C + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + - id: A.1.3 + identity: A + changes: + - textAppend: + text: "second" + readKey: key.Read.1 + - id: B.1.1 + identity: B + aclChanges: + - userJoin: + identity: B + encryptionKey: key.Enc.B + acceptSignature: key.Sign.Onetime1 + inviteId: A.1.2 + encryptedReadKeys: [key.Read.1] + readKey: key.Read.1 + - id: B.1.2 + identity: B + changes: + - textAppend: + text: "first" + readKey: key.Read.1 + - id: C.1.1 + identity: C + changes: + - textAppend: + text: "third" + readKey: key.Read.1 +keys: + Enc: + - A + - B + - C + - D + - Onetime1 + Sign: + - A + - B + - C + - D + - Onetime1 + Read: + - 1 +graph: + - id: A.1.1 + baseSnapshot: A.1.1 + - id: A.1.2 + baseSnapshot: A.1.1 + aclHeads: [A.1.1] + treeHeads: [A.1.1] + - id: B.1.1 + baseSnapshot: A.1.1 + aclHeads: [A.1.2] + treeHeads: [A.1.2] + - id: B.1.2 + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] + - id: A.1.3 # this should be invalid, because it is based on one of the invalid changes + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.2, C.1.1] + - id: C.1.1 # this should be invalid, because C is a reader + baseSnapshot: A.1.1 + aclHeads: [B.1.1] + treeHeads: [B.1.1] +maybeHeads: + - "A.1.3" +updatedChanges: + - id: B.1.3 + identity: B + changes: + - textAppend: + text: "second" + readKey: key.Read.1 + - id: A.1.4 + identity: A + aclChanges: + - userAdd: + identity: D + permission: writer + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + changes: + - textAppend: + text: "second" + readKey: key.Read.1 +updatedGraph: + - id: B.1.3 + baseSnapshot: A.1.1 + aclHeads: [ B.1.1 ] + treeHeads: [ B.1.2 ] + - id: A.1.4 + baseSnapshot: A.1.1 + aclHeads: [ B.1.1 ] + treeHeads: [ B.1.3 ] From 94f53aabe1f70c3d7a98b3314f26a0ae2c840473 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Thu, 7 Jul 2022 11:32:02 +0200 Subject: [PATCH 26/53] WIP create change continue --- data/document.go | 10 ++++++++++ data/documentcontext.go | 2 +- data/documentstatebuilder.go | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/data/document.go b/data/document.go index 2888f862..44706213 100644 --- a/data/document.go +++ b/data/document.go @@ -15,6 +15,7 @@ type AccountData struct { } type Document struct { + // TODO: ensure that every operation on Document is synchronized thread threadmodels.Thread stateProvider InitialStateProvider accountData *AccountData @@ -99,6 +100,14 @@ func (d *Document) Create(payload *CreateChangePayload) error { if err != nil { return err } + + if aclChange.AclData != nil { + // we can apply change right away without going through builder, because + err = d.docContext.aclState.ApplyChange(payload.Id, aclChange) + if err != nil { + return err + } + } d.docContext.fullTree.AddFast(ch) err = d.thread.AddChange(&threadmodels.RawChange{ @@ -157,6 +166,7 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up break } + // TODO: we should still check if the user making those changes are able to write using "aclState" // decrypting everything, because we have no new keys for _, ch := range treeChanges { if ch.Content.GetChangesData() != nil { diff --git a/data/documentcontext.go b/data/documentcontext.go index 5d723e9a..4acfb76f 100644 --- a/data/documentcontext.go +++ b/data/documentcontext.go @@ -1,7 +1,7 @@ package data type documentContext struct { - aclTree *Tree + aclTree *Tree // TODO: remove it, because we don't use it fullTree *Tree aclState *ACLState docState DocumentState diff --git a/data/documentstatebuilder.go b/data/documentstatebuilder.go index c0cb55a9..3c9b0ea9 100644 --- a/data/documentstatebuilder.go +++ b/data/documentstatebuilder.go @@ -66,6 +66,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 d.tree.Iterate(fromId, func(c *Change) (isContinue bool) { if c.Id == fromId { return true From ee123efd7ded2f29ab8e1d48f5f10c1a22302f0a Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 8 Jul 2022 10:19:43 +0200 Subject: [PATCH 27/53] WIP change package structure --- {data => acltree}/aclstate.go | 46 +++++++++---------- {data => acltree}/aclstatebuilder.go | 18 ++++---- {data => acltree}/aclstatebuilder_test.go | 22 +++++---- {data => acltree}/acltreebuilder.go | 22 +++++---- {data => acltree}/change.go | 4 +- {data => acltree}/changeloader.go | 24 +++++----- {data => acltree}/docstate.go | 2 +- {data => acltree}/document.go | 13 ++++-- {data => acltree}/document_test.go | 5 +- {data => acltree}/documentcontext.go | 4 +- {data => acltree}/documentstatebuilder.go | 6 +-- .../documentstatebuilder_test.go | 5 +- {data => acltree}/pb/aclchanges.pb.go | 0 {data => acltree}/pb/protos/aclchanges.proto | 0 {data => acltree}/plaintextdocstate.go | 6 ++- {data => acltree}/snapshotvalidator.go | 3 +- {data => acltree}/threadhelpers.go | 8 +--- {data => acltree}/tree.go | 2 +- {data => acltree}/treebuilder.go | 22 +++++---- {data => acltree}/treebuilder_test.go | 2 +- {data => acltree}/treegraph.go | 2 +- {data => acltree}/treegraph_nix.go | 2 +- {data => acltree}/treeiterator.go | 2 +- .../threadbuilder/invalidsnapshotexample.yml | 0 {data => testutils}/threadbuilder/keychain.go | 1 - .../pb/protos/testdocumentchanges.proto | 0 .../pb/testdocumentchanges.pb.go | 0 .../threadbuilder/threadbuilder.go | 3 +- .../threadbuilder/threadbuildergraph.go | 0 .../threadbuilder/threadbuildergraph_nix.go | 0 .../threadbuilder/userjoinexample.yml | 0 .../threadbuilder/userjoinexampleupdate.yml | 0 .../threadbuilder/userremovebeforeexample.yml | 0 .../threadbuilder/userremoveexample.yml | 0 .../threadbuilder/validsnapshotexample.yml | 0 .../threadbuilder/ymlentities.go | 0 .../threadbuilder/ymlentities_test.go | 2 +- {data/threadmodels => thread}/models.go | 2 +- {data/threadmodels => thread}/threadid.go | 7 +-- .../threadmodels => thread}/threadid_test.go | 5 +- {data/threadmodels => util/keys}/keys.go | 2 +- 41 files changed, 127 insertions(+), 115 deletions(-) rename {data => acltree}/aclstate.go (86%) rename {data => acltree}/aclstatebuilder.go (90%) rename {data => acltree}/aclstatebuilder_test.go (92%) rename {data => acltree}/acltreebuilder.go (84%) rename {data => acltree}/change.go (93%) rename {data => acltree}/changeloader.go (75%) rename {data => acltree}/docstate.go (92%) rename {data => acltree}/document.go (96%) rename {data => acltree}/document_test.go (93%) rename {data => acltree}/documentcontext.go (78%) rename {data => acltree}/documentstatebuilder.go (92%) rename {data => acltree}/documentstatebuilder_test.go (87%) rename {data => acltree}/pb/aclchanges.pb.go (100%) rename {data => acltree}/pb/protos/aclchanges.proto (100%) rename {data => acltree}/plaintextdocstate.go (93%) rename {data => acltree}/snapshotvalidator.go (92%) rename {data => acltree}/threadhelpers.go (94%) rename {data => acltree}/tree.go (99%) rename {data => acltree}/treebuilder.go (92%) rename {data => acltree}/treebuilder_test.go (99%) rename {data => acltree}/treegraph.go (93%) rename {data => acltree}/treegraph_nix.go (99%) rename {data => acltree}/treeiterator.go (99%) rename {data => testutils}/threadbuilder/invalidsnapshotexample.yml (100%) rename {data => testutils}/threadbuilder/keychain.go (97%) rename data/pb/protos/plaintextchanges.proto => testutils/threadbuilder/pb/protos/testdocumentchanges.proto (100%) rename data/pb/plaintextchanges.pb.go => testutils/threadbuilder/pb/testdocumentchanges.pb.go (100%) rename {data => testutils}/threadbuilder/threadbuilder.go (99%) rename {data => testutils}/threadbuilder/threadbuildergraph.go (100%) rename {data => testutils}/threadbuilder/threadbuildergraph_nix.go (100%) rename {data => testutils}/threadbuilder/userjoinexample.yml (100%) rename {data => testutils}/threadbuilder/userjoinexampleupdate.yml (100%) rename {data => testutils}/threadbuilder/userremovebeforeexample.yml (100%) rename {data => testutils}/threadbuilder/userremoveexample.yml (100%) rename {data => testutils}/threadbuilder/validsnapshotexample.yml (100%) rename {data => testutils}/threadbuilder/ymlentities.go (100%) rename {data => testutils}/threadbuilder/ymlentities_test.go (67%) rename {data/threadmodels => thread}/models.go (94%) rename {data/threadmodels => thread}/threadid.go (84%) rename {data/threadmodels => thread}/threadid_test.go (77%) rename {data/threadmodels => util/keys}/keys.go (99%) diff --git a/data/aclstate.go b/acltree/aclstate.go similarity index 86% rename from data/aclstate.go rename to acltree/aclstate.go index 5a7a5cf8..ad64dad4 100644 --- a/data/aclstate.go +++ b/acltree/aclstate.go @@ -1,4 +1,4 @@ -package data +package acltree import ( "bytes" @@ -6,8 +6,8 @@ import ( "fmt" "hash/fnv" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/textileio/go-threads/crypto/symmetric" ) @@ -17,22 +17,22 @@ 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 userInvites map[string]*pb.ACLChangeUserInvite - signingPubKeyDecoder threadmodels.SigningPubKeyDecoder - encryptionKey threadmodels.EncryptionPrivKey + signingPubKeyDecoder keys.SigningPubKeyDecoder + encryptionKey keys.EncryptionPrivKey identity string } func NewACLStateFromSnapshot( snapshot *pb.ACLChangeACLSnapshot, identity string, - encryptionKey threadmodels.EncryptionPrivKey, - signingPubKeyDecoder threadmodels.SigningPubKeyDecoder) (*ACLState, error) { - st := &ACLState{ + encryptionKey keys.EncryptionPrivKey, + 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,7 +74,7 @@ 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 @@ -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) applyChange(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 } diff --git a/data/aclstatebuilder.go b/acltree/aclstatebuilder.go similarity index 90% rename from data/aclstatebuilder.go rename to acltree/aclstatebuilder.go index 2dbe7e5d..2bb52301 100644 --- a/data/aclstatebuilder.go +++ b/acltree/aclstatebuilder.go @@ -1,18 +1,18 @@ -package data +package acltree import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" ) type ACLStateBuilder struct { tree *Tree - aclState *ACLState + aclState *aclState identity string - key threadmodels.EncryptionPrivKey - decoder threadmodels.SigningPubKeyDecoder + key keys.EncryptionPrivKey + decoder keys.SigningPubKeyDecoder } type decreasedPermissionsParameters struct { @@ -20,7 +20,7 @@ type decreasedPermissionsParameters struct { startChange string } -func NewACLStateBuilder(decoder threadmodels.SigningPubKeyDecoder, accountData *AccountData) *ACLStateBuilder { +func NewACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *AccountData) *ACLStateBuilder { return &ACLStateBuilder{ decoder: decoder, identity: accountData.Identity, @@ -28,7 +28,7 @@ func NewACLStateBuilder(decoder threadmodels.SigningPubKeyDecoder, accountData * } } -func (sb *ACLStateBuilder) Build() (*ACLState, error) { +func (sb *ACLStateBuilder) Build() (*aclState, error) { state, _, err := sb.BuildBefore("") return state, err } @@ -55,7 +55,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 diff --git a/data/aclstatebuilder_test.go b/acltree/aclstatebuilder_test.go similarity index 92% rename from data/aclstatebuilder_test.go rename to acltree/aclstatebuilder_test.go index ea6a6cfb..518e1547 100644 --- a/data/aclstatebuilder_test.go +++ b/acltree/aclstatebuilder_test.go @@ -1,11 +1,13 @@ -package data +package acltree import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadbuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/stretchr/testify/assert" "testing" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + + "github.com/stretchr/testify/assert" ) func TestACLStateBuilder_UserJoinBuild(t *testing.T) { @@ -18,7 +20,7 @@ func TestACLStateBuilder_UserJoinBuild(t *testing.T) { thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], - threadmodels.NewEd25519Decoder(), + keys.NewEd25519Decoder(), NewPlainTextDocumentStateProvider(), false) if err != nil { @@ -53,7 +55,7 @@ func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], - threadmodels.NewEd25519Decoder(), + keys.NewEd25519Decoder(), NewPlainTextDocumentStateProvider(), false) if err != nil { @@ -84,7 +86,7 @@ func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], - threadmodels.NewEd25519Decoder(), + keys.NewEd25519Decoder(), NewPlainTextDocumentStateProvider(), false) if err != nil { @@ -116,7 +118,7 @@ func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], - threadmodels.NewEd25519Decoder(), + keys.NewEd25519Decoder(), NewPlainTextDocumentStateProvider(), false) if err != nil { @@ -147,7 +149,7 @@ func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], - threadmodels.NewEd25519Decoder(), + keys.NewEd25519Decoder(), NewPlainTextDocumentStateProvider(), false) if err != nil { diff --git a/data/acltreebuilder.go b/acltree/acltreebuilder.go similarity index 84% rename from data/acltreebuilder.go rename to acltree/acltreebuilder.go index a5f994e7..680c9c79 100644 --- a/data/acltreebuilder.go +++ b/acltree/acltreebuilder.go @@ -1,23 +1,25 @@ -package data +package acltree import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "github.com/textileio/go-threads/core/thread" + gothread "github.com/textileio/go-threads/core/thread" ) type ACLTreeBuilder struct { cache map[string]*Change - identityKeys map[string]threadmodels.SigningPubKey - signingPubKeyDecoder threadmodels.SigningPubKeyDecoder + identityKeys map[string]keys.SigningPubKey + signingPubKeyDecoder keys.SigningPubKeyDecoder tree *Tree - thread threadmodels.Thread + thread thread.Thread *changeLoader } -func NewACLTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *ACLTreeBuilder { +func NewACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *ACLTreeBuilder { return &ACLTreeBuilder{ signingPubKeyDecoder: decoder, thread: t, @@ -30,7 +32,7 @@ func NewACLTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKey func (tb *ACLTreeBuilder) Init() { tb.cache = make(map[string]*Change) - tb.identityKeys = make(map[string]threadmodels.SigningPubKey) + tb.identityKeys = make(map[string]keys.SigningPubKey) tb.tree = &Tree{} tb.changeLoader.init(tb.cache, tb.identityKeys) } @@ -97,7 +99,7 @@ func (tb *ACLTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleR } func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { - threadId, err := thread.Decode(tb.thread.ID()) + threadId, err := gothread.Decode(tb.thread.ID()) if err != nil { return nil, err } @@ -109,7 +111,7 @@ func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { continue } - res, err := threadmodels.VerifyACLThreadID(sk, threadId) + res, err := thread.VerifyACLThreadID(sk, threadId) if err != nil { continue } diff --git a/data/change.go b/acltree/change.go similarity index 93% rename from data/change.go rename to acltree/change.go index 906eb7c8..f07f19b1 100644 --- a/data/change.go +++ b/acltree/change.go @@ -1,9 +1,9 @@ -package data +package acltree import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" "github.com/textileio/go-threads/crypto/symmetric" ) diff --git a/data/changeloader.go b/acltree/changeloader.go similarity index 75% rename from data/changeloader.go rename to acltree/changeloader.go index 5382aada..3947cee7 100644 --- a/data/changeloader.go +++ b/acltree/changeloader.go @@ -1,25 +1,27 @@ -package data +package acltree import ( "context" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" - "github.com/gogo/protobuf/proto" "time" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + "github.com/gogo/protobuf/proto" ) type changeLoader struct { cache map[string]*Change - identityKeys map[string]threadmodels.SigningPubKey - signingPubKeyDecoder threadmodels.SigningPubKeyDecoder - thread threadmodels.Thread + identityKeys map[string]keys.SigningPubKey + signingPubKeyDecoder keys.SigningPubKeyDecoder + thread thread.Thread changeCreator func(id string, ch *pb.ACLChange) *Change } func newChangeLoader( - thread threadmodels.Thread, - signingPubKeyDecoder threadmodels.SigningPubKeyDecoder, + thread thread.Thread, + signingPubKeyDecoder keys.SigningPubKeyDecoder, changeCreator func(id string, ch *pb.ACLChange) *Change) *changeLoader { return &changeLoader{ signingPubKeyDecoder: signingPubKeyDecoder, @@ -29,7 +31,7 @@ func newChangeLoader( } func (c *changeLoader) init(cache map[string]*Change, - identityKeys map[string]threadmodels.SigningPubKey) { + identityKeys map[string]keys.SigningPubKey) { c.cache = cache c.identityKeys = identityKeys } @@ -71,7 +73,7 @@ func (c *changeLoader) verify(identity string, payload, signature []byte) (isVer return identityKey.Verify(payload, signature) } -func (c *changeLoader) makeVerifiedACLChange(change *threadmodels.RawChange) (aclChange *pb.ACLChange, err error) { +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 diff --git a/data/docstate.go b/acltree/docstate.go similarity index 92% rename from data/docstate.go rename to acltree/docstate.go index 693319d8..dd6f69d0 100644 --- a/data/docstate.go +++ b/acltree/docstate.go @@ -1,4 +1,4 @@ -package data +package acltree type DocumentState interface { ApplyChange(change []byte, id string) (DocumentState, error) diff --git a/data/document.go b/acltree/document.go similarity index 96% rename from data/document.go rename to acltree/document.go index 44706213..8a89792a 100644 --- a/data/document.go +++ b/acltree/document.go @@ -1,9 +1,8 @@ -package data +package acltree import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" ) @@ -64,7 +63,13 @@ func NewDocument( } } -func (d *Document) Create(payload *CreateChangePayload) error { +//sync layer -> Object cache -> document -> Update (..raw changes) +//client layer -> Object cache -> document -> CreateChange(...) +// + +// smartblock -> CreateChange(payload) +// SmartTree iterate etc +func (d *Document) CreateChange(payload *CreateChangePayload) error { // TODO: add snapshot creation logic marshalled, err := payload.ChangesData.Marshal() if err != nil { @@ -275,6 +280,8 @@ func (d *Document) build(fromStart bool) (DocumentState, error) { return nil, err } + // tree should be exposed + d.docStateBuilder.init(d.docContext.aclState, d.docContext.fullTree) d.docContext.docState, err = d.docStateBuilder.build() if err != nil { diff --git a/data/document_test.go b/acltree/document_test.go similarity index 93% rename from data/document_test.go rename to acltree/document_test.go index 6cd128a6..f6c868ff 100644 --- a/data/document_test.go +++ b/acltree/document_test.go @@ -1,10 +1,9 @@ -package data +package acltree import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" "github.com/stretchr/testify/assert" "testing" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadbuilder" ) func TestDocument_Build(t *testing.T) { diff --git a/data/documentcontext.go b/acltree/documentcontext.go similarity index 78% rename from data/documentcontext.go rename to acltree/documentcontext.go index 4acfb76f..b1b29e1a 100644 --- a/data/documentcontext.go +++ b/acltree/documentcontext.go @@ -1,8 +1,8 @@ -package data +package acltree type documentContext struct { aclTree *Tree // TODO: remove it, because we don't use it fullTree *Tree - aclState *ACLState + aclState *aclState docState DocumentState } diff --git a/data/documentstatebuilder.go b/acltree/documentstatebuilder.go similarity index 92% rename from data/documentstatebuilder.go rename to acltree/documentstatebuilder.go index 3c9b0ea9..227999a4 100644 --- a/data/documentstatebuilder.go +++ b/acltree/documentstatebuilder.go @@ -1,4 +1,4 @@ -package data +package acltree import ( "fmt" @@ -8,7 +8,7 @@ import ( type documentStateBuilder struct { tree *Tree - aclState *ACLState // TODO: decide if this is needed or not + aclState *aclState // TODO: decide if this is needed or not stateProvider InitialStateProvider } @@ -18,7 +18,7 @@ func newDocumentStateBuilder(stateProvider InitialStateProvider) *documentStateB } } -func (d *documentStateBuilder) init(aclState *ACLState, tree *Tree) { +func (d *documentStateBuilder) init(aclState *aclState, tree *Tree) { d.tree = tree d.aclState = aclState } diff --git a/data/documentstatebuilder_test.go b/acltree/documentstatebuilder_test.go similarity index 87% rename from data/documentstatebuilder_test.go rename to acltree/documentstatebuilder_test.go index b691b72e..dd91de24 100644 --- a/data/documentstatebuilder_test.go +++ b/acltree/documentstatebuilder_test.go @@ -1,8 +1,7 @@ -package data +package acltree import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadbuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" "github.com/stretchr/testify/assert" "testing" ) diff --git a/data/pb/aclchanges.pb.go b/acltree/pb/aclchanges.pb.go similarity index 100% rename from data/pb/aclchanges.pb.go rename to acltree/pb/aclchanges.pb.go diff --git a/data/pb/protos/aclchanges.proto b/acltree/pb/protos/aclchanges.proto similarity index 100% rename from data/pb/protos/aclchanges.proto rename to acltree/pb/protos/aclchanges.proto diff --git a/data/plaintextdocstate.go b/acltree/plaintextdocstate.go similarity index 93% rename from data/plaintextdocstate.go rename to acltree/plaintextdocstate.go index 3357c02e..91ba2c3b 100644 --- a/data/plaintextdocstate.go +++ b/acltree/plaintextdocstate.go @@ -1,4 +1,4 @@ -package data +package acltree import ( "fmt" @@ -7,6 +7,10 @@ import ( "github.com/gogo/protobuf/proto" ) +// TestDocumentState -> testutils +// ThreadBuilder -> testutils +// move protos to test utils + type PlainTextDocumentState struct { LastChangeId string Text string diff --git a/data/snapshotvalidator.go b/acltree/snapshotvalidator.go similarity index 92% rename from data/snapshotvalidator.go rename to acltree/snapshotvalidator.go index 58de1b03..8b40ec97 100644 --- a/data/snapshotvalidator.go +++ b/acltree/snapshotvalidator.go @@ -1,8 +1,7 @@ -package data +package acltree import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" ) type SnapshotValidator struct { diff --git a/data/threadhelpers.go b/acltree/threadhelpers.go similarity index 94% rename from data/threadhelpers.go rename to acltree/threadhelpers.go index 4841c065..7e46b697 100644 --- a/data/threadhelpers.go +++ b/acltree/threadhelpers.go @@ -1,12 +1,8 @@ -package data - -import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" -) +package acltree type ACLContext struct { Tree *Tree - ACLState *ACLState + ACLState *aclState DocState DocumentState } diff --git a/data/tree.go b/acltree/tree.go similarity index 99% rename from data/tree.go rename to acltree/tree.go index 0c4bfb6f..a437d42d 100644 --- a/data/tree.go +++ b/acltree/tree.go @@ -1,4 +1,4 @@ -package data +package acltree import ( "bytes" diff --git a/data/treebuilder.go b/acltree/treebuilder.go similarity index 92% rename from data/treebuilder.go rename to acltree/treebuilder.go index 3bcdda49..05800980 100644 --- a/data/treebuilder.go +++ b/acltree/treebuilder.go @@ -1,13 +1,15 @@ -package data +package acltree import ( "errors" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" //"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/prometheus/common/log" - "github.com/textileio/go-threads/core/thread" + gothread "github.com/textileio/go-threads/core/thread" ) var ( @@ -17,15 +19,15 @@ var ( type TreeBuilder struct { cache map[string]*Change - identityKeys map[string]threadmodels.SigningPubKey - signingPubKeyDecoder threadmodels.SigningPubKeyDecoder + identityKeys map[string]keys.SigningPubKey + signingPubKeyDecoder keys.SigningPubKeyDecoder tree *Tree - thread threadmodels.Thread + thread thread.Thread *changeLoader } -func NewTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDecoder) *TreeBuilder { +func NewTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *TreeBuilder { return &TreeBuilder{ signingPubKeyDecoder: decoder, thread: t, @@ -38,7 +40,7 @@ func NewTreeBuilder(t threadmodels.Thread, decoder threadmodels.SigningPubKeyDec func (tb *TreeBuilder) Init() { tb.cache = make(map[string]*Change) - tb.identityKeys = make(map[string]threadmodels.SigningPubKey) + tb.identityKeys = make(map[string]keys.SigningPubKey) tb.tree = &Tree{} tb.changeLoader.init(tb.cache, tb.identityKeys) } @@ -285,7 +287,7 @@ func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e } func (tb *TreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { - threadId, err := thread.Decode(tb.thread.ID()) + threadId, err := gothread.Decode(tb.thread.ID()) if err != nil { return nil, err } @@ -297,7 +299,7 @@ func (tb *TreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { continue } - res, err := threadmodels.VerifyACLThreadID(sk, threadId) + res, err := thread.VerifyACLThreadID(sk, threadId) if err != nil { continue } diff --git a/data/treebuilder_test.go b/acltree/treebuilder_test.go similarity index 99% rename from data/treebuilder_test.go rename to acltree/treebuilder_test.go index fa4a8971..311fbbc4 100644 --- a/data/treebuilder_test.go +++ b/acltree/treebuilder_test.go @@ -1,4 +1,4 @@ -package data +package acltree //func TestACLTreeBuilder_UserJoinCorrectHeadsAndLen(t *testing.T) { // thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") diff --git a/data/treegraph.go b/acltree/treegraph.go similarity index 93% rename from data/treegraph.go rename to acltree/treegraph.go index 60dc9967..3b37fa2e 100644 --- a/data/treegraph.go +++ b/acltree/treegraph.go @@ -2,7 +2,7 @@ // +build !linux,!darwin android ios nographviz // +build !amd64 -package data +package acltree import "fmt" diff --git a/data/treegraph_nix.go b/acltree/treegraph_nix.go similarity index 99% rename from data/treegraph_nix.go rename to acltree/treegraph_nix.go index 0adbb58f..175007aa 100644 --- a/data/treegraph_nix.go +++ b/acltree/treegraph_nix.go @@ -5,7 +5,7 @@ // +build !nographviz // +build amd64 arm64 -package data +package acltree import ( "bytes" diff --git a/data/treeiterator.go b/acltree/treeiterator.go similarity index 99% rename from data/treeiterator.go rename to acltree/treeiterator.go index dd08b6df..addcf307 100644 --- a/data/treeiterator.go +++ b/acltree/treeiterator.go @@ -1,4 +1,4 @@ -package data +package acltree import "sync" diff --git a/data/threadbuilder/invalidsnapshotexample.yml b/testutils/threadbuilder/invalidsnapshotexample.yml similarity index 100% rename from data/threadbuilder/invalidsnapshotexample.yml rename to testutils/threadbuilder/invalidsnapshotexample.yml diff --git a/data/threadbuilder/keychain.go b/testutils/threadbuilder/keychain.go similarity index 97% rename from data/threadbuilder/keychain.go rename to testutils/threadbuilder/keychain.go index 501fb404..74ecf649 100644 --- a/data/threadbuilder/keychain.go +++ b/testutils/threadbuilder/keychain.go @@ -4,7 +4,6 @@ import ( "hash/fnv" "strings" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" "github.com/textileio/go-threads/crypto/symmetric" ) diff --git a/data/pb/protos/plaintextchanges.proto b/testutils/threadbuilder/pb/protos/testdocumentchanges.proto similarity index 100% rename from data/pb/protos/plaintextchanges.proto rename to testutils/threadbuilder/pb/protos/testdocumentchanges.proto diff --git a/data/pb/plaintextchanges.pb.go b/testutils/threadbuilder/pb/testdocumentchanges.pb.go similarity index 100% rename from data/pb/plaintextchanges.pb.go rename to testutils/threadbuilder/pb/testdocumentchanges.pb.go diff --git a/data/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go similarity index 99% rename from data/threadbuilder/threadbuilder.go rename to testutils/threadbuilder/threadbuilder.go index 411204a7..6663281f 100644 --- a/data/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -9,7 +9,6 @@ import ( "gopkg.in/yaml.v3" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" ) const plainTextDocType uint16 = 1 @@ -46,7 +45,7 @@ func NewThreadBuilderFromFile(file string) (*ThreadBuilder, error) { return nil, err } - thread := YMLThread{} + thread := YMLThread{Some: &Super{}} err = yaml.Unmarshal(content, &thread) if err != nil { return nil, err diff --git a/data/threadbuilder/threadbuildergraph.go b/testutils/threadbuilder/threadbuildergraph.go similarity index 100% rename from data/threadbuilder/threadbuildergraph.go rename to testutils/threadbuilder/threadbuildergraph.go diff --git a/data/threadbuilder/threadbuildergraph_nix.go b/testutils/threadbuilder/threadbuildergraph_nix.go similarity index 100% rename from data/threadbuilder/threadbuildergraph_nix.go rename to testutils/threadbuilder/threadbuildergraph_nix.go diff --git a/data/threadbuilder/userjoinexample.yml b/testutils/threadbuilder/userjoinexample.yml similarity index 100% rename from data/threadbuilder/userjoinexample.yml rename to testutils/threadbuilder/userjoinexample.yml diff --git a/data/threadbuilder/userjoinexampleupdate.yml b/testutils/threadbuilder/userjoinexampleupdate.yml similarity index 100% rename from data/threadbuilder/userjoinexampleupdate.yml rename to testutils/threadbuilder/userjoinexampleupdate.yml diff --git a/data/threadbuilder/userremovebeforeexample.yml b/testutils/threadbuilder/userremovebeforeexample.yml similarity index 100% rename from data/threadbuilder/userremovebeforeexample.yml rename to testutils/threadbuilder/userremovebeforeexample.yml diff --git a/data/threadbuilder/userremoveexample.yml b/testutils/threadbuilder/userremoveexample.yml similarity index 100% rename from data/threadbuilder/userremoveexample.yml rename to testutils/threadbuilder/userremoveexample.yml diff --git a/data/threadbuilder/validsnapshotexample.yml b/testutils/threadbuilder/validsnapshotexample.yml similarity index 100% rename from data/threadbuilder/validsnapshotexample.yml rename to testutils/threadbuilder/validsnapshotexample.yml diff --git a/data/threadbuilder/ymlentities.go b/testutils/threadbuilder/ymlentities.go similarity index 100% rename from data/threadbuilder/ymlentities.go rename to testutils/threadbuilder/ymlentities.go diff --git a/data/threadbuilder/ymlentities_test.go b/testutils/threadbuilder/ymlentities_test.go similarity index 67% rename from data/threadbuilder/ymlentities_test.go rename to testutils/threadbuilder/ymlentities_test.go index bd066301..d2420eae 100644 --- a/data/threadbuilder/ymlentities_test.go +++ b/testutils/threadbuilder/ymlentities_test.go @@ -6,7 +6,7 @@ import ( ) func Test_YamlParse(t *testing.T) { - tb, _ := NewThreadBuilderFromFile("validsnapshotexample.yml") + tb, _ := NewThreadBuilderFromFile("userjoinexample.yml") gr, _ := tb.Graph() fmt.Println(gr) } diff --git a/data/threadmodels/models.go b/thread/models.go similarity index 94% rename from data/threadmodels/models.go rename to thread/models.go index 71b6d433..3dee82e1 100644 --- a/data/threadmodels/models.go +++ b/thread/models.go @@ -1,4 +1,4 @@ -package threadmodels +package thread import ( "context" diff --git a/data/threadmodels/threadid.go b/thread/threadid.go similarity index 84% rename from data/threadmodels/threadid.go rename to thread/threadid.go index c2bfe178..406f9eea 100644 --- a/data/threadmodels/threadid.go +++ b/thread/threadid.go @@ -1,15 +1,16 @@ -package threadmodels +package thread import ( "crypto/rand" "encoding/binary" "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "hash/fnv" "github.com/textileio/go-threads/core/thread" ) -func CreateACLThreadID(k SigningPubKey, docType uint16) (thread.ID, error) { +func CreateACLThreadID(k keys.SigningPubKey, docType uint16) (thread.ID, error) { rndlen := 32 buf := make([]byte, 8+rndlen) @@ -34,7 +35,7 @@ func CreateACLThreadID(k SigningPubKey, docType uint16) (thread.ID, error) { return threadIDFromBytes(docType, buf) } -func VerifyACLThreadID(k SigningPubKey, threadId thread.ID) (bool, error) { +func VerifyACLThreadID(k keys.SigningPubKey, threadId thread.ID) (bool, error) { bytes := threadId.Bytes() pubKeyBytes := threadId.Bytes()[len(bytes)-40 : len(bytes)-32] hash := binary.LittleEndian.Uint64(pubKeyBytes) diff --git a/data/threadmodels/threadid_test.go b/thread/threadid_test.go similarity index 77% rename from data/threadmodels/threadid_test.go rename to thread/threadid_test.go index df562a77..bddef215 100644 --- a/data/threadmodels/threadid_test.go +++ b/thread/threadid_test.go @@ -1,11 +1,12 @@ -package threadmodels +package thread import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "testing" ) func TestCreateACLThreadIDVerify(t *testing.T) { - _, pubKey, err := GenerateRandomEd25519KeyPair() + _, pubKey, err := keys.GenerateRandomEd25519KeyPair() if err != nil { t.Fatalf("should not return error after generating key pair: %v", err) } diff --git a/data/threadmodels/keys.go b/util/keys/keys.go similarity index 99% rename from data/threadmodels/keys.go rename to util/keys/keys.go index deb7967b..796b6ab3 100644 --- a/data/threadmodels/keys.go +++ b/util/keys/keys.go @@ -1,4 +1,4 @@ -package threadmodels +package keys import ( "crypto/rand" From f889aa1b713d1456f104f065bac5ac0e7ba85c4f Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 8 Jul 2022 10:52:06 +0200 Subject: [PATCH 28/53] Continue adjusting folder structure --- Makefile | 1 + account/accountdata.go | 9 ++ {acltree => aclchanges}/pb/aclchanges.pb.go | 0 .../pb/protos/aclchanges.proto | 0 acltree/aclstate.go | 2 +- acltree/aclstatebuilder.go | 5 +- acltree/aclstatebuilder_test.go | 72 +++++++++++++-- acltree/acltreebuilder.go | 2 +- acltree/change.go | 2 +- acltree/changeloader.go | 2 +- acltree/documentcontext.go | 8 -- acltree/snapshotvalidator.go | 11 ++- acltree/threadhelpers.go | 88 ------------------- acltree/treebuilder.go | 2 +- {acltree => exampledocument}/docstate.go | 2 +- {acltree => exampledocument}/document.go | 39 ++++---- {acltree => exampledocument}/document_test.go | 2 +- exampledocument/documentcontext.go | 10 +++ .../documentstatebuilder.go | 13 +-- .../documentstatebuilder_test.go | 7 +- .../plaintextdocstate.go | 3 +- .../pb/protos/testdocumentchanges.proto | 0 .../pb/testdocumentchanges.pb.go | 0 testutils/threadbuilder/keychain.go | 22 ++--- testutils/threadbuilder/threadbuilder.go | 53 +++++------ .../threadbuilder/threadbuildergraph_nix.go | 6 +- 26 files changed, 179 insertions(+), 182 deletions(-) create mode 100644 account/accountdata.go rename {acltree => aclchanges}/pb/aclchanges.pb.go (100%) rename {acltree => aclchanges}/pb/protos/aclchanges.proto (100%) delete mode 100644 acltree/documentcontext.go delete mode 100644 acltree/threadhelpers.go rename {acltree => exampledocument}/docstate.go (89%) rename {acltree => exampledocument}/document.go (88%) rename {acltree => exampledocument}/document_test.go (98%) create mode 100644 exampledocument/documentcontext.go rename {acltree => exampledocument}/documentstatebuilder.go (79%) rename {acltree => exampledocument}/documentstatebuilder_test.go (86%) rename {acltree => exampledocument}/plaintextdocstate.go (98%) rename testutils/{threadbuilder => testchanges}/pb/protos/testdocumentchanges.proto (100%) rename testutils/{threadbuilder => testchanges}/pb/testdocumentchanges.pb.go (100%) diff --git a/Makefile b/Makefile index 38a94af5..31dda6ee 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ endif export PATH=$(GOPATH)/bin:$(shell echo $$PATH) +# TODO: folders were changed, so we should update Makefile and protos generation protos-go: @echo 'Generating protobuf packages (Go)...' $(eval P_TIMESTAMP := Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types) diff --git a/account/accountdata.go b/account/accountdata.go new file mode 100644 index 00000000..2c06b02a --- /dev/null +++ b/account/accountdata.go @@ -0,0 +1,9 @@ +package account + +import "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + +type AccountData struct { + Identity string + SignKey keys.SigningPrivKey + EncKey keys.EncryptionPrivKey +} diff --git a/acltree/pb/aclchanges.pb.go b/aclchanges/pb/aclchanges.pb.go similarity index 100% rename from acltree/pb/aclchanges.pb.go rename to aclchanges/pb/aclchanges.pb.go diff --git a/acltree/pb/protos/aclchanges.proto b/aclchanges/pb/protos/aclchanges.proto similarity index 100% rename from acltree/pb/protos/aclchanges.proto rename to aclchanges/pb/protos/aclchanges.proto diff --git a/acltree/aclstate.go b/acltree/aclstate.go index ad64dad4..7fce4f3e 100644 --- a/acltree/aclstate.go +++ b/acltree/aclstate.go @@ -6,7 +6,7 @@ import ( "fmt" "hash/fnv" - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/textileio/go-threads/crypto/symmetric" ) diff --git a/acltree/aclstatebuilder.go b/acltree/aclstatebuilder.go index 2bb52301..e09e2538 100644 --- a/acltree/aclstatebuilder.go +++ b/acltree/aclstatebuilder.go @@ -3,7 +3,8 @@ package acltree import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "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" ) @@ -20,7 +21,7 @@ type decreasedPermissionsParameters struct { startChange string } -func NewACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *AccountData) *ACLStateBuilder { +func NewACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *account.AccountData) *ACLStateBuilder { return &ACLStateBuilder{ decoder: decoder, identity: accountData.Identity, diff --git a/acltree/aclstatebuilder_test.go b/acltree/aclstatebuilder_test.go index 518e1547..4cdc07f2 100644 --- a/acltree/aclstatebuilder_test.go +++ b/acltree/aclstatebuilder_test.go @@ -3,13 +3,78 @@ package acltree import ( "testing" - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/stretchr/testify/assert" ) +type ACLContext struct { + Tree *Tree + ACLState *aclState +} + +func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) { + treeBuilder := NewTreeBuilder(t, keys.NewEd25519Decoder()) + treeBuilder.Init() + return treeBuilder.Build(fromStart) +} + +func createACLStateFromThread( + t thread.Thread, + identity string, + key keys.EncryptionPrivKey, + decoder keys.SigningPubKeyDecoder, + fromStart bool) (*ACLContext, error) { + tree, err := createTreeFromThread(t, fromStart) + if err != nil { + return nil, err + } + + accountData := &account.AccountData{ + Identity: identity, + EncKey: key, + } + + 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) + 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 + return createACLStateFromThread(t, identity, key, decoder, true) + } + } + + aclBuilder := NewACLStateBuilder(decoder, accountData) + err = aclBuilder.Init(tree) + if err != nil { + return nil, err + } + + aclState, err := aclBuilder.Build() + if err != nil { + return nil, err + } + return &ACLContext{ + Tree: tree, + ACLState: aclState, + }, nil +} + func TestACLStateBuilder_UserJoinBuild(t *testing.T) { thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") if err != nil { @@ -21,7 +86,6 @@ func TestACLStateBuilder_UserJoinBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], keys.NewEd25519Decoder(), - NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -56,7 +120,6 @@ func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], keys.NewEd25519Decoder(), - NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -87,7 +150,6 @@ func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], keys.NewEd25519Decoder(), - NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -119,7 +181,6 @@ func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], keys.NewEd25519Decoder(), - NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) @@ -150,7 +211,6 @@ func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], keys.NewEd25519Decoder(), - NewPlainTextDocumentStateProvider(), false) if err != nil { t.Fatalf("should build acl aclState without err: %v", err) diff --git a/acltree/acltreebuilder.go b/acltree/acltreebuilder.go index 680c9c79..ca566359 100644 --- a/acltree/acltreebuilder.go +++ b/acltree/acltreebuilder.go @@ -2,9 +2,9 @@ package acltree import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "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/slice" gothread "github.com/textileio/go-threads/core/thread" ) diff --git a/acltree/change.go b/acltree/change.go index f07f19b1..7af0e511 100644 --- a/acltree/change.go +++ b/acltree/change.go @@ -3,7 +3,7 @@ package acltree import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" "github.com/textileio/go-threads/crypto/symmetric" ) diff --git a/acltree/changeloader.go b/acltree/changeloader.go index 3947cee7..72004477 100644 --- a/acltree/changeloader.go +++ b/acltree/changeloader.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/gogo/protobuf/proto" diff --git a/acltree/documentcontext.go b/acltree/documentcontext.go deleted file mode 100644 index b1b29e1a..00000000 --- a/acltree/documentcontext.go +++ /dev/null @@ -1,8 +0,0 @@ -package acltree - -type documentContext struct { - aclTree *Tree // TODO: remove it, because we don't use it - fullTree *Tree - aclState *aclState - docState DocumentState -} diff --git a/acltree/snapshotvalidator.go b/acltree/snapshotvalidator.go index 8b40ec97..5a7aeda1 100644 --- a/acltree/snapshotvalidator.go +++ b/acltree/snapshotvalidator.go @@ -2,19 +2,22 @@ package acltree import ( "fmt" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" ) type SnapshotValidator struct { aclTree *Tree identity string - key threadmodels.EncryptionPrivKey - decoder threadmodels.SigningPubKeyDecoder + key keys.EncryptionPrivKey + decoder keys.SigningPubKeyDecoder stateBuilder *ACLStateBuilder } func NewSnapshotValidator( - decoder threadmodels.SigningPubKeyDecoder, - accountData *AccountData) *SnapshotValidator { + decoder keys.SigningPubKeyDecoder, + accountData *account.AccountData) *SnapshotValidator { return &SnapshotValidator{ identity: accountData.Identity, key: accountData.EncKey, diff --git a/acltree/threadhelpers.go b/acltree/threadhelpers.go deleted file mode 100644 index 7e46b697..00000000 --- a/acltree/threadhelpers.go +++ /dev/null @@ -1,88 +0,0 @@ -package acltree - -type ACLContext struct { - Tree *Tree - ACLState *aclState - DocState DocumentState -} - -func createTreeFromThread(t threadmodels.Thread, fromStart bool) (*Tree, error) { - treeBuilder := NewTreeBuilder(t, threadmodels.NewEd25519Decoder()) - treeBuilder.Init() - return treeBuilder.Build(fromStart) -} - -func createACLStateFromThread( - t threadmodels.Thread, - identity string, - key threadmodels.EncryptionPrivKey, - decoder threadmodels.SigningPubKeyDecoder, - provider InitialStateProvider, - fromStart bool) (*ACLContext, error) { - tree, err := createTreeFromThread(t, fromStart) - if err != nil { - return nil, err - } - - accountData := &AccountData{ - Identity: identity, - EncKey: key, - } - - 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) - 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 - return createACLStateFromThread(t, identity, key, decoder, provider, true) - } - } - - aclBuilder := NewACLStateBuilder(decoder, accountData) - err = aclBuilder.Init(tree) - if err != nil { - return nil, err - } - - aclState, err := aclBuilder.Build() - if err != nil { - return nil, err - } - return &ACLContext{ - Tree: tree, - ACLState: aclState, - }, nil -} - -func createDocumentStateFromThread( - t threadmodels.Thread, - identity string, - key threadmodels.EncryptionPrivKey, - provider InitialStateProvider, - decoder threadmodels.SigningPubKeyDecoder) (*ACLContext, error) { - context, err := createACLStateFromThread(t, identity, key, decoder, provider, false) - if err != nil { - return nil, err - } - - docStateBuilder := newDocumentStateBuilder(provider) - docStateBuilder.init(context.ACLState, context.Tree) - docState, err := docStateBuilder.build() - if err != nil { - return nil, err - } - context.DocState = docState - - return context, nil -} diff --git a/acltree/treebuilder.go b/acltree/treebuilder.go index 05800980..f56790d4 100644 --- a/acltree/treebuilder.go +++ b/acltree/treebuilder.go @@ -3,9 +3,9 @@ package acltree import ( "errors" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" //"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/prometheus/common/log" diff --git a/acltree/docstate.go b/exampledocument/docstate.go similarity index 89% rename from acltree/docstate.go rename to exampledocument/docstate.go index dd6f69d0..95bc41b6 100644 --- a/acltree/docstate.go +++ b/exampledocument/docstate.go @@ -1,4 +1,4 @@ -package acltree +package exampledocument type DocumentState interface { ApplyChange(change []byte, id string) (DocumentState, error) diff --git a/acltree/document.go b/exampledocument/document.go similarity index 88% rename from acltree/document.go rename to exampledocument/document.go index 8a89792a..fe378a2d 100644 --- a/acltree/document.go +++ b/exampledocument/document.go @@ -1,7 +1,8 @@ -package acltree +package exampledocument import ( "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" @@ -20,13 +21,13 @@ type Document struct { accountData *AccountData decoder threadmodels.SigningPubKeyDecoder - treeBuilder *TreeBuilder - aclTreeBuilder *ACLTreeBuilder - aclStateBuilder *ACLStateBuilder - snapshotValidator *SnapshotValidator - docStateBuilder *documentStateBuilder + treeBuilder *acltree.TreeBuilder + aclTreeBuilder *acltree.ACLTreeBuilder + aclStateBuilder *acltree.ACLStateBuilder + snapshotValidator *acltree.SnapshotValidator + docStateBuilder *acltree.documentStateBuilder - docContext *documentContext + docContext *acltree.documentContext } type UpdateResult int @@ -54,12 +55,12 @@ func NewDocument( stateProvider: stateProvider, accountData: accountData, decoder: decoder, - aclTreeBuilder: NewACLTreeBuilder(thread, decoder), - treeBuilder: NewTreeBuilder(thread, decoder), - snapshotValidator: NewSnapshotValidator(decoder, accountData), - aclStateBuilder: NewACLStateBuilder(decoder, accountData), - docStateBuilder: newDocumentStateBuilder(stateProvider), - docContext: &documentContext{}, + 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{}, } } @@ -94,7 +95,7 @@ func (d *Document) CreateChange(payload *CreateChangePayload) error { } // TODO: add CID creation logic based on content - ch := NewChange(payload.Id, aclChange) + ch := acltree.NewChange(payload.Id, aclChange) ch.DecryptedDocumentChange = marshalled fullMarshalledChange, err := proto.Marshal(aclChange) @@ -115,7 +116,7 @@ func (d *Document) CreateChange(payload *CreateChangePayload) error { } d.docContext.fullTree.AddFast(ch) - err = d.thread.AddChange(&threadmodels.RawChange{ + err = d.thread.AddChange(&thread.RawChange{ Payload: marshalled, Signature: signature, Id: payload.Id, @@ -130,8 +131,8 @@ func (d *Document) CreateChange(payload *CreateChangePayload) error { return nil } -func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) { - var treeChanges []*Change +func (d *Document) Update(changes ...*thread.RawChange) (DocumentState, UpdateResult, error) { + var treeChanges []*acltree.Change var foundACLChange bool for _, ch := range changes { @@ -162,9 +163,9 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up prevHeads := d.docContext.fullTree.Heads() mode := d.docContext.fullTree.Add(treeChanges...) switch mode { - case Nothing: + case acltree.Nothing: return d.docContext.docState, UpdateResultNoAction, nil - case Rebuild: + case acltree.Rebuild: res, err := d.Build() return res, UpdateResultRebuild, err default: diff --git a/acltree/document_test.go b/exampledocument/document_test.go similarity index 98% rename from acltree/document_test.go rename to exampledocument/document_test.go index f6c868ff..99fea827 100644 --- a/acltree/document_test.go +++ b/exampledocument/document_test.go @@ -1,4 +1,4 @@ -package acltree +package exampledocument import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" diff --git a/exampledocument/documentcontext.go b/exampledocument/documentcontext.go new file mode 100644 index 00000000..08a415bb --- /dev/null +++ b/exampledocument/documentcontext.go @@ -0,0 +1,10 @@ +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 + aclState *acltree.aclState + docState DocumentState +} diff --git a/acltree/documentstatebuilder.go b/exampledocument/documentstatebuilder.go similarity index 79% rename from acltree/documentstatebuilder.go rename to exampledocument/documentstatebuilder.go index 227999a4..8cff9dc0 100644 --- a/acltree/documentstatebuilder.go +++ b/exampledocument/documentstatebuilder.go @@ -1,14 +1,15 @@ -package acltree +package exampledocument import ( "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" ) // example -> type documentStateBuilder struct { - tree *Tree - aclState *aclState // TODO: decide if this is needed or not + tree *acltree.Tree + aclState *acltree.aclState // TODO: decide if this is needed or not stateProvider InitialStateProvider } @@ -18,7 +19,7 @@ func newDocumentStateBuilder(stateProvider InitialStateProvider) *documentStateB } } -func (d *documentStateBuilder) init(aclState *aclState, tree *Tree) { +func (d *documentStateBuilder) init(aclState *acltree.aclState, tree *acltree.Tree) { d.tree = tree d.aclState = aclState } @@ -44,7 +45,7 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) { t := d.tree startId = rootChange.Id - t.Iterate(startId, func(c *Change) (isContinue bool) { + t.Iterate(startId, func(c *acltree.Change) (isContinue bool) { count++ if startId == c.Id { return true @@ -67,7 +68,7 @@ func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s // 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 - d.tree.Iterate(fromId, func(c *Change) (isContinue bool) { + d.tree.Iterate(fromId, func(c *acltree.Change) (isContinue bool) { if c.Id == fromId { return true } diff --git a/acltree/documentstatebuilder_test.go b/exampledocument/documentstatebuilder_test.go similarity index 86% rename from acltree/documentstatebuilder_test.go rename to exampledocument/documentstatebuilder_test.go index dd91de24..3f1d7232 100644 --- a/acltree/documentstatebuilder_test.go +++ b/exampledocument/documentstatebuilder_test.go @@ -1,6 +1,7 @@ -package acltree +package exampledocument import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" "github.com/stretchr/testify/assert" "testing" @@ -12,7 +13,7 @@ func TestDocumentStateBuilder_UserJoinBuild(t *testing.T) { t.Fatal(err) } keychain := thread.GetKeychain() - ctx, err := createDocumentStateFromThread( + ctx, err := acltree.createDocumentStateFromThread( thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], @@ -32,7 +33,7 @@ func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { t.Fatal(err) } keychain := thread.GetKeychain() - ctx, err := createDocumentStateFromThread( + ctx, err := acltree.createDocumentStateFromThread( thread, keychain.GetIdentity("A"), keychain.EncryptionKeys["A"], diff --git a/acltree/plaintextdocstate.go b/exampledocument/plaintextdocstate.go similarity index 98% rename from acltree/plaintextdocstate.go rename to exampledocument/plaintextdocstate.go index 91ba2c3b..e5296e42 100644 --- a/acltree/plaintextdocstate.go +++ b/exampledocument/plaintextdocstate.go @@ -1,8 +1,7 @@ -package acltree +package exampledocument import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "github.com/gogo/protobuf/proto" ) diff --git a/testutils/threadbuilder/pb/protos/testdocumentchanges.proto b/testutils/testchanges/pb/protos/testdocumentchanges.proto similarity index 100% rename from testutils/threadbuilder/pb/protos/testdocumentchanges.proto rename to testutils/testchanges/pb/protos/testdocumentchanges.proto diff --git a/testutils/threadbuilder/pb/testdocumentchanges.pb.go b/testutils/testchanges/pb/testdocumentchanges.pb.go similarity index 100% rename from testutils/threadbuilder/pb/testdocumentchanges.pb.go rename to testutils/testchanges/pb/testdocumentchanges.pb.go diff --git a/testutils/threadbuilder/keychain.go b/testutils/threadbuilder/keychain.go index 74ecf649..c8532a41 100644 --- a/testutils/threadbuilder/keychain.go +++ b/testutils/threadbuilder/keychain.go @@ -4,6 +4,8 @@ import ( "hash/fnv" "strings" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + "github.com/textileio/go-threads/crypto/symmetric" ) @@ -13,24 +15,24 @@ type SymKey struct { } type Keychain struct { - SigningKeys map[string]threadmodels.SigningPrivKey - SigningKeysByIdentity map[string]threadmodels.SigningPrivKey - EncryptionKeys map[string]threadmodels.EncryptionPrivKey + SigningKeys map[string]keys.SigningPrivKey + SigningKeysByIdentity map[string]keys.SigningPrivKey + EncryptionKeys map[string]keys.EncryptionPrivKey ReadKeys map[string]*SymKey ReadKeysByHash map[uint64]*SymKey GeneratedIdentities map[string]string - coder *threadmodels.Ed25519SigningPubKeyDecoder + coder *keys.Ed25519SigningPubKeyDecoder } func NewKeychain() *Keychain { return &Keychain{ - SigningKeys: map[string]threadmodels.SigningPrivKey{}, - SigningKeysByIdentity: map[string]threadmodels.SigningPrivKey{}, - EncryptionKeys: map[string]threadmodels.EncryptionPrivKey{}, + SigningKeys: map[string]keys.SigningPrivKey{}, + SigningKeysByIdentity: map[string]keys.SigningPrivKey{}, + EncryptionKeys: map[string]keys.EncryptionPrivKey{}, GeneratedIdentities: map[string]string{}, ReadKeys: map[string]*SymKey{}, ReadKeysByHash: map[uint64]*SymKey{}, - coder: threadmodels.NewEd25519Decoder(), + coder: keys.NewEd25519Decoder(), } } @@ -52,7 +54,7 @@ func (k *Keychain) AddEncryptionKey(name string) { if _, exists := k.EncryptionKeys[name]; exists { return } - newPrivKey, _, err := threadmodels.GenerateRandomRSAKeyPair(2048) + newPrivKey, _, err := keys.GenerateRandomRSAKeyPair(2048) if err != nil { panic(err) } @@ -64,7 +66,7 @@ func (k *Keychain) AddSigningKey(name string) { if _, exists := k.SigningKeys[name]; exists { return } - newPrivKey, pubKey, err := threadmodels.GenerateRandomEd25519KeyPair() + newPrivKey, pubKey, err := keys.GenerateRandomEd25519KeyPair() if err != nil { panic(err) } diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 6663281f..b7507b9f 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -8,7 +8,10 @@ import ( "github.com/gogo/protobuf/proto" "gopkg.in/yaml.v3" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" + testpb "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" ) const plainTextDocType uint16 = 1 @@ -17,7 +20,7 @@ type threadChange struct { *pb.ACLChange id string readKey *SymKey - signKey threadmodels.SigningPrivKey + signKey keys.SigningPrivKey changesDataDecrypted []byte } @@ -45,7 +48,7 @@ func NewThreadBuilderFromFile(file string) (*ThreadBuilder, error) { return nil, err } - thread := YMLThread{Some: &Super{}} + thread := YMLThread{} err = yaml.Unmarshal(content, &thread) if err != nil { return nil, err @@ -74,7 +77,7 @@ func (t *ThreadBuilder) Heads() []string { return t.heads } -func (t *ThreadBuilder) AddChange(change *threadmodels.RawChange) error { +func (t *ThreadBuilder) AddChange(change *thread.RawChange) error { aclChange := new(pb.ACLChange) var err error @@ -121,12 +124,12 @@ func (t *ThreadBuilder) SetHeads(heads []string) { t.heads = heads } -func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*threadmodels.RawChange, error) { +func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*thread.RawChange, error) { return t.getChange(recordID, t.allChanges), nil } -func (t *ThreadBuilder) GetUpdatedChanges() []*threadmodels.RawChange { - var res []*threadmodels.RawChange +func (t *ThreadBuilder) GetUpdatedChanges() []*thread.RawChange { + var res []*thread.RawChange for _, ch := range t.updatedChanges { rawCh := t.getChange(ch.id, t.updatedChanges) res = append(res, rawCh) @@ -134,7 +137,7 @@ func (t *ThreadBuilder) GetUpdatedChanges() []*threadmodels.RawChange { return res } -func (t *ThreadBuilder) getChange(changeId string, m map[string]*threadChange) *threadmodels.RawChange { +func (t *ThreadBuilder) getChange(changeId string, m map[string]*threadChange) *thread.RawChange { rec := m[changeId] if rec.changesDataDecrypted != nil { @@ -156,7 +159,7 @@ func (t *ThreadBuilder) getChange(changeId string, m map[string]*threadChange) * panic("should be able to sign final acl message!") } - transformedRec := &threadmodels.RawChange{ + transformedRec := &thread.RawChange{ Payload: aclMarshaled, Signature: signature, Id: changeId, @@ -208,12 +211,12 @@ func (t *ThreadBuilder) parseChange(ch *Change) *threadChange { } } if len(ch.Changes) > 0 || ch.Snapshot != nil { - changesData := &pb.PlainTextChangeData{} + changesData := &testpb.PlainTextChangeData{} if ch.Snapshot != nil { changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) } if len(ch.Changes) > 0 { - var changeContents []*pb.PlainTextChangeContent + var changeContents []*testpb.PlainTextChangeContent for _, ch := range ch.Changes { aclChangeContent := t.parseDocumentChange(ch) changeContents = append(changeContents, aclChangeContent) @@ -236,7 +239,7 @@ func (t *ThreadBuilder) parseThreadId(description *ThreadDescription) string { panic("no author in thread") } key := t.keychain.SigningKeys[description.Author] - id, err := threadmodels.CreateACLThreadID(key.GetPublic(), plainTextDocType) + id, err := thread.CreateACLThreadID(key.GetPublic(), plainTextDocType) if err != nil { panic(err) } @@ -244,8 +247,8 @@ func (t *ThreadBuilder) parseThreadId(description *ThreadDescription) string { return id.String() } -func (t *ThreadBuilder) parseChangeSnapshot(s *PlainTextSnapshot) *pb.PlainTextChangeSnapshot { - return &pb.PlainTextChangeSnapshot{ +func (t *ThreadBuilder) parseChangeSnapshot(s *PlainTextSnapshot) *testpb.PlainTextChangeSnapshot { + return &testpb.PlainTextChangeSnapshot{ Text: s.Text, } } @@ -257,7 +260,7 @@ func (t *ThreadBuilder) parseACLSnapshot(s *ACLSnapshot) *pb.ACLChangeACLSnapsho aclUserState.Identity = t.keychain.GetIdentity(state.Identity) encKey := t.keychain. - GetKey(state.EncryptionKey).(threadmodels.EncryptionPrivKey) + GetKey(state.EncryptionKey).(keys.EncryptionPrivKey) rawKey, _ := encKey.GetPublic().Raw() aclUserState.EncryptionKey = rawKey @@ -270,12 +273,12 @@ func (t *ThreadBuilder) parseACLSnapshot(s *ACLSnapshot) *pb.ACLChangeACLSnapsho } } -func (t *ThreadBuilder) parseDocumentChange(ch *PlainTextChange) (convCh *pb.PlainTextChangeContent) { +func (t *ThreadBuilder) parseDocumentChange(ch *PlainTextChange) (convCh *testpb.PlainTextChangeContent) { switch { case ch.TextAppend != nil: - convCh = &pb.PlainTextChangeContent{ - Value: &pb.PlainTextChangeContentValueOfTextAppend{ - TextAppend: &pb.PlainTextChangeTextAppend{ + convCh = &testpb.PlainTextChangeContent{ + Value: &testpb.PlainTextChangeContentValueOfTextAppend{ + TextAppend: &testpb.PlainTextChangeTextAppend{ Text: ch.TextAppend.Text, }, }, @@ -294,7 +297,7 @@ func (t *ThreadBuilder) parseACLChange(ch *ACLChange) (convCh *pb.ACLChangeACLCo add := ch.UserAdd encKey := t.keychain. - GetKey(add.EncryptionKey).(threadmodels.EncryptionPrivKey) + GetKey(add.EncryptionKey).(keys.EncryptionPrivKey) rawKey, _ := encKey.GetPublic().Raw() convCh = &pb.ACLChangeACLContentValue{ @@ -311,11 +314,11 @@ func (t *ThreadBuilder) parseACLChange(ch *ACLChange) (convCh *pb.ACLChangeACLCo join := ch.UserJoin encKey := t.keychain. - GetKey(join.EncryptionKey).(threadmodels.EncryptionPrivKey) + GetKey(join.EncryptionKey).(keys.EncryptionPrivKey) rawKey, _ := encKey.GetPublic().Raw() idKey, _ := t.keychain.SigningKeys[join.Identity].GetPublic().Raw() - signKey := t.keychain.GetKey(join.AcceptSignature).(threadmodels.SigningPrivKey) + signKey := t.keychain.GetKey(join.AcceptSignature).(keys.SigningPrivKey) signature, err := signKey.Sign(idKey) if err != nil { panic(err) @@ -334,9 +337,9 @@ func (t *ThreadBuilder) parseACLChange(ch *ACLChange) (convCh *pb.ACLChangeACLCo } case ch.UserInvite != nil: invite := ch.UserInvite - rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(threadmodels.SigningPrivKey).GetPublic().Raw() + rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(keys.SigningPrivKey).GetPublic().Raw() encKey := t.keychain. - GetKey(invite.EncryptionKey).(threadmodels.EncryptionPrivKey) + GetKey(invite.EncryptionKey).(keys.EncryptionPrivKey) rawEncKey, _ := encKey.GetPublic().Raw() convCh = &pb.ACLChangeACLContentValue{ @@ -408,7 +411,7 @@ func (t *ThreadBuilder) parseACLChange(ch *ACLChange) (convCh *pb.ACLChangeACLCo return convCh } -func (t *ThreadBuilder) encryptReadKeys(keys []string, encKey threadmodels.EncryptionPrivKey) (enc [][]byte) { +func (t *ThreadBuilder) encryptReadKeys(keys []string, encKey keys.EncryptionPrivKey) (enc [][]byte) { for _, k := range keys { realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes() res, err := encKey.GetPublic().Encrypt(realKey) diff --git a/testutils/threadbuilder/threadbuildergraph_nix.go b/testutils/threadbuilder/threadbuildergraph_nix.go index 30722a53..cca8ad30 100644 --- a/testutils/threadbuilder/threadbuildergraph_nix.go +++ b/testutils/threadbuilder/threadbuildergraph_nix.go @@ -9,12 +9,14 @@ package threadbuilder import ( "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/gogo/protobuf/proto" "strings" "unicode" "github.com/awalterschulze/gographviz" + + testpb "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb" ) // To quickly look at visualized string you can use https://dreampuf.github.io/GraphvizOnline @@ -44,7 +46,7 @@ func (t *ThreadBuilder) Graph() (string, error) { var chSymbs []string if r.changesDataDecrypted != nil { - res := &pb.PlainTextChangeData{} + res := &testpb.PlainTextChangeData{} err := proto.Unmarshal(r.changesDataDecrypted, res) if err != nil { return err From 11fcb088e5c24ec4c43fdaf85c35179030beabee Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 8 Jul 2022 11:33:32 +0200 Subject: [PATCH 29/53] WIP ACLTree and moving files to packages --- acltree/aclstate.go | 47 +++++++++++++----------- acltree/aclstatebuilder.go | 34 +++++++++--------- acltree/aclstatebuilder_test.go | 48 ++++++++++++------------- acltree/acltree.go | 39 ++++++++++++++++++++ acltree/acltreebuilder.go | 22 ++++++------ acltree/changeloader.go | 2 +- acltree/snapshotvalidator.go | 26 +++++++------- acltree/tree.go | 7 ++++ acltree/treebuilder.go | 42 +++++++++++----------- acltree/treebuilder_test.go | 22 ++++++------ acltree/treegraph_nix.go | 2 +- exampledocument/document.go | 32 ++++++++--------- exampledocument/documentcontext.go | 4 +-- exampledocument/documentstatebuilder.go | 6 ++-- 14 files changed, 192 insertions(+), 141 deletions(-) create mode 100644 acltree/acltree.go 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 From c07b26d568601365ed8abce47169ec27c3dfeae8 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 8 Jul 2022 12:31:06 +0200 Subject: [PATCH 30/53] WIP ACLTree --- acltree/acltree.go | 132 +++++++++++++++++++++++++++++++++++++++++---- acltree/change.go | 7 +++ acltree/tree.go | 2 +- 3 files changed, 130 insertions(+), 11 deletions(-) diff --git a/acltree/acltree.go b/acltree/acltree.go index e8d9872e..c4d936f6 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -3,16 +3,9 @@ package acltree import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" ) -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 ( @@ -31,9 +24,128 @@ type AddResult struct { type ACLTree interface { ACLState() *ACLState - AddChanges(changes ...*Change) (AddResult, error) + AddContent(changeContent *ChangeContent) (*Change, error) + AddChanges(changes ...*Change) (AddResult, error) // TODO: Make change as interface Heads() []string Iterate(func(change *Change) bool) IterateFrom(string, func(change *Change) bool) - HasChange(change *Change) + HasChange(change *Change) bool +} + +type aclTree struct { + thread thread.Thread + accountData *account.AccountData + + fullTree *Tree + aclState *ACLState + + treeBuilder *treeBuilder + aclTreeBuilder *aclTreeBuilder + aclStateBuilder *aclStateBuilder + snapshotValidator *snapshotValidator +} + +func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) { + decoder := keys.NewEd25519Decoder() + aclTreeBuilder := newACLTreeBuilder(t, decoder) + treeBuilder := newTreeBuilder(t, decoder) + snapshotValidator := newSnapshotValidator(decoder, acc) + aclStateBuilder := newACLStateBuilder(decoder, acc) + + aclTree := &aclTree{ + thread: t, + accountData: acc, + fullTree: nil, + aclState: nil, + treeBuilder: treeBuilder, + aclTreeBuilder: aclTreeBuilder, + aclStateBuilder: aclStateBuilder, + snapshotValidator: snapshotValidator, + } + err := aclTree.rebuildFromThread(false) + if err != nil { + return nil, err + } + + return aclTree, nil +} + +func (a *aclTree) rebuildFromThread(fromStart bool) error { + var aclTree *Tree + + a.treeBuilder.init() + a.aclTreeBuilder.init() + + var err error + 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 + aclTree, err = a.aclTreeBuilder.build() + if err != nil { + return err + } + + if !fromStart { + err = a.snapshotValidator.init(aclTree) + if err != nil { + return err + } + + valid, err := a.snapshotValidator.validateSnapshot(a.fullTree.root) + if err != nil { + return err + } + if !valid { + return a.rebuildFromThread(true) + } + } + err = a.aclStateBuilder.init(a.fullTree) + if err != nil { + return err + } + + a.aclState, err = a.aclStateBuilder.build() + if err != nil { + return err + } + + return nil +} + +func (a *aclTree) ACLState() *ACLState { + //TODO implement me + panic("implement me") +} + +func (a *aclTree) AddContent(changeContent *ChangeContent) (*Change, error) { + //TODO implement me + panic("implement me") +} + +func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { + //TODO implement me + panic("implement me") +} + +func (a *aclTree) Heads() []string { + //TODO implement me + panic("implement me") +} + +func (a *aclTree) Iterate(f func(change *Change) bool) { + //TODO implement me + panic("implement me") +} + +func (a *aclTree) IterateFrom(s string, f func(change *Change) bool) { + //TODO implement me + panic("implement me") +} + +func (a *aclTree) HasChange(change *Change) bool { + //TODO implement me + panic("implement me") } diff --git a/acltree/change.go b/acltree/change.go index 7af0e511..0973d55e 100644 --- a/acltree/change.go +++ b/acltree/change.go @@ -2,11 +2,18 @@ package acltree import ( "fmt" + "github.com/gogo/protobuf/proto" "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" "github.com/textileio/go-threads/crypto/symmetric" ) +type ChangeContent struct { + ChangesData proto.Marshaler + ACLData *pb.ACLChangeACLData + Id string // TODO: this is just for testing, because id should be created automatically from content +} + // Change is an abstract type for all types of changes type Change struct { Next []*Change diff --git a/acltree/tree.go b/acltree/tree.go index 288929d2..6b4525e9 100644 --- a/acltree/tree.go +++ b/acltree/tree.go @@ -15,7 +15,7 @@ const ( Nothing ) -// TODO: consider abstracting into separate package with iterator +// TODO: consider abstracting into separate package with iterator, remove type Tree struct { root *Change headIds []string From d5614eb5ee9e27e8744e2ddf48e4bc5d73d23b32 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 8 Jul 2022 15:13:01 +0200 Subject: [PATCH 31/53] WIP ACLTree --- acltree/acltree.go | 152 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 142 insertions(+), 10 deletions(-) diff --git a/acltree/acltree.go b/acltree/acltree.go index c4d936f6..13f7e769 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -2,8 +2,11 @@ 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/thread" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" + "github.com/gogo/protobuf/proto" ) type AddResultSummary int @@ -37,6 +40,7 @@ type aclTree struct { accountData *account.AccountData fullTree *Tree + aclTree *Tree // this tree is built from start of the document aclState *ACLState treeBuilder *treeBuilder @@ -70,9 +74,36 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) { return aclTree, nil } -func (a *aclTree) rebuildFromThread(fromStart bool) error { - var aclTree *Tree +func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) { + if validateSnapshot { + err = a.snapshotValidator.init(a.aclTree) + if err != nil { + return err + } + valid, err := a.snapshotValidator.validateSnapshot(a.fullTree.root) + if err != nil { + return err + } + if !valid { + return a.rebuildFromThread(true) + } + } + + err = a.aclStateBuilder.init(a.fullTree) + if err != nil { + return err + } + + a.aclState, err = a.aclStateBuilder.build() + if err != nil { + return err + } + + return nil +} + +func (a *aclTree) rebuildFromThread(fromStart bool) error { a.treeBuilder.init() a.aclTreeBuilder.init() @@ -83,13 +114,13 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error { } // TODO: remove this from context as this is used only to validate snapshot - aclTree, err = a.aclTreeBuilder.build() + a.aclTree, err = a.aclTreeBuilder.build() if err != nil { return err } if !fromStart { - err = a.snapshotValidator.init(aclTree) + err = a.snapshotValidator.init(a.aclTree) if err != nil { return err } @@ -115,19 +146,120 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error { 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 { - //TODO implement me - panic("implement me") + return a.aclState } func (a *aclTree) AddContent(changeContent *ChangeContent) (*Change, error) { - //TODO implement me - panic("implement me") + // TODO: add snapshot creation logic + marshalled, err := changeContent.ChangesData.Marshal() + if err != nil { + 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) + + err = a.thread.AddChange(&thread.RawChange{ + Payload: marshalled, + Signature: signature, + Id: changeContent.Id, + }) + if err != nil { + return nil, err + } + + a.thread.SetHeads([]string{ch.Id}) + return a.fullTree.attached[changeContent.Id], nil } func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { - //TODO implement me - panic("implement me") + var aclChanges []*Change + for _, ch := range changes { + if ch.IsACLChange() { + aclChanges = append(aclChanges, ch) + break + } + } + + // TODO: understand the common snapshot problem + prevHeads := a.fullTree.Heads() + prevRoot := a. + mode := a.fullTree.Add(changes...) + switch mode { + case acltree.Nothing: + return d.docContext.docState, UpdateResultNoAction, nil + case acltree.Rebuild: + res, err := d.Build() + return res, UpdateResultRebuild, err + default: + break + } } func (a *aclTree) Heads() []string { From 126d080c63b90fab2f97f896ff6a2da236e28542 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 10 Jul 2022 11:44:02 +0200 Subject: [PATCH 32/53] WIP Change interface rework --- aclchanges/change.go | 10 ++++++ acltree/acltree.go | 21 +++++-------- acltree/acltreebuilder.go | 2 +- acltree/change.go | 17 ++++++++++ acltree/tree.go | 4 +++ acltree/treebuilder.go | 2 +- exampledocument/document.go | 3 +- testutils/threadbuilder/threadbuilder.go | 40 +++++++++++++++++++++--- thread/models.go | 15 ++++++--- 9 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 aclchanges/change.go diff --git a/aclchanges/change.go b/aclchanges/change.go new file mode 100644 index 00000000..f8253ace --- /dev/null +++ b/aclchanges/change.go @@ -0,0 +1,10 @@ +package aclchanges + +import "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" + +type Change interface { + ProtoChange() *pb.ACLChange + DecryptedChangeContent() []byte + Signature() []byte + CID() string +} diff --git a/acltree/acltree.go b/acltree/acltree.go index 13f7e769..1cb83da7 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -32,7 +32,7 @@ type ACLTree interface { Heads() []string Iterate(func(change *Change) bool) IterateFrom(string, func(change *Change) bool) - HasChange(change *Change) bool + HasChange(string) bool } type aclTree struct { @@ -225,7 +225,7 @@ func (a *aclTree) AddContent(changeContent *ChangeContent) (*Change, error) { } a.fullTree.AddFast(ch) - err = a.thread.AddChange(&thread.RawChange{ + err = a.thread.AddRawChange(&thread.RawChange{ Payload: marshalled, Signature: signature, Id: changeContent.Id, @@ -245,28 +245,23 @@ func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { aclChanges = append(aclChanges, ch) break } + a.thread.A } // TODO: understand the common snapshot problem prevHeads := a.fullTree.Heads() - prevRoot := a. mode := a.fullTree.Add(changes...) switch mode { - case acltree.Nothing: - return d.docContext.docState, UpdateResultNoAction, nil - case acltree.Rebuild: + case Nothing: + return AddResult{Summary: AddResultSummaryNothing}, nil + case Rebuild: res, err := d.Build() - return res, UpdateResultRebuild, err + return AddResult{Summary: Rebuild}, err default: break } } -func (a *aclTree) Heads() []string { - //TODO implement me - panic("implement me") -} - func (a *aclTree) Iterate(f func(change *Change) bool) { //TODO implement me panic("implement me") @@ -277,7 +272,7 @@ func (a *aclTree) IterateFrom(s string, f func(change *Change) bool) { panic("implement me") } -func (a *aclTree) HasChange(change *Change) bool { +func (a *aclTree) HasChange(s string) bool { //TODO implement me panic("implement me") } diff --git a/acltree/acltreebuilder.go b/acltree/acltreebuilder.go index 4396da6e..83d7afd3 100644 --- a/acltree/acltreebuilder.go +++ b/acltree/acltreebuilder.go @@ -38,7 +38,7 @@ func (tb *aclTreeBuilder) init() { } func (tb *aclTreeBuilder) build() (*Tree, error) { - heads := tb.thread.MaybeHeads() + heads := tb.thread.PossibleHeads() aclHeads, err := tb.getACLHeads(heads) if err != nil { return nil, err diff --git a/acltree/change.go b/acltree/change.go index 0973d55e..40a168d0 100644 --- a/acltree/change.go +++ b/acltree/change.go @@ -25,6 +25,7 @@ type Change struct { DecryptedDocumentChange []byte Content *pb.ACLChange + Sign []byte } func (ch *Change) DecryptContents(key *symmetric.Key) error { @@ -66,3 +67,19 @@ func NewACLChange(id string, ch *pb.ACLChange) *Change { IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, } } + +func (ch *Change) ProtoChange() *pb.ACLChange { + return ch.Content +} + +func (ch *Change) DecryptedChangeContent() []byte { + return ch.DecryptedDocumentChange +} + +func (ch *Change) Signature() []byte { + return ch.Sign +} + +func (ch *Change) CID() string { + return ch.Id +} diff --git a/acltree/tree.go b/acltree/tree.go index 6b4525e9..326ebcad 100644 --- a/acltree/tree.go +++ b/acltree/tree.go @@ -33,6 +33,10 @@ type Tree struct { duplicateEvents int } +func (t *Tree) GetUnattachedChanges(changes ...*Change) []*Change { + return nil +} + func (t *Tree) RootId() string { if t.root != nil { return t.root.Id diff --git a/acltree/treebuilder.go b/acltree/treebuilder.go index 96de435f..8196046e 100644 --- a/acltree/treebuilder.go +++ b/acltree/treebuilder.go @@ -46,7 +46,7 @@ func (tb *treeBuilder) init() { } func (tb *treeBuilder) build(fromStart bool) (*Tree, error) { - heads := tb.thread.MaybeHeads() + heads := tb.thread.PossibleHeads() if fromStart { if err := tb.buildTreeFromStart(heads); err != nil { diff --git a/exampledocument/document.go b/exampledocument/document.go index f9f9e1d6..d196b111 100644 --- a/exampledocument/document.go +++ b/exampledocument/document.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/gogo/protobuf/proto" ) @@ -144,7 +145,7 @@ func (d *Document) Update(changes ...*thread.RawChange) (DocumentState, UpdateRe treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) treeChanges = append(treeChanges, treeChange) - // this already sets MaybeHeads to include new changes + // this already sets PossibleHeads to include new changes // TODO: change this behaviour as non-obvious, because it is not evident from the interface err = d.thread.AddChange(ch) if err != nil { diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index b7507b9f..9d695479 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -3,6 +3,7 @@ package threadbuilder import ( "context" "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges" "io/ioutil" "github.com/gogo/protobuf/proto" @@ -77,11 +78,10 @@ func (t *ThreadBuilder) Heads() []string { return t.heads } -func (t *ThreadBuilder) AddChange(change *thread.RawChange) error { +func (t *ThreadBuilder) AddRawChange(change *thread.RawChange) error { aclChange := new(pb.ACLChange) var err error - // 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 fmt.Errorf("could not unmarshall changes") } @@ -98,7 +98,6 @@ func (t *ThreadBuilder) AddChange(change *thread.RawChange) error { // get correct signing key signKey := t.keychain.SigningKeysByIdentity[aclChange.Identity] - t.maybeHeads = append(t.maybeHeads, change.Id) t.allChanges[change.Id] = &threadChange{ ACLChange: aclChange, @@ -110,11 +109,42 @@ func (t *ThreadBuilder) AddChange(change *thread.RawChange) error { return nil } -func (t *ThreadBuilder) MaybeHeads() []string { +func (t *ThreadBuilder) AddPossibleHead(head string) { + t.maybeHeads = append(t.maybeHeads, head) +} + +func (t *ThreadBuilder) AddChange(change aclchanges.Change) error { + aclChange := change.ProtoChange() + var err error + var changesData []byte + + // get correct readkey + readKey := t.keychain.ReadKeysByHash[aclChange.CurrentReadKeyHash] + if aclChange.ChangesData != nil { + changesData, err = readKey.Key.Decrypt(aclChange.ChangesData) + if err != nil { + return fmt.Errorf("failed to decrypt changes data: %w", err) + } + } + + // get correct signing key + signKey := t.keychain.SigningKeysByIdentity[aclChange.Identity] + + t.allChanges[change.CID()] = &threadChange{ + ACLChange: aclChange, + id: change.CID(), + readKey: readKey, + signKey: signKey, + changesDataDecrypted: changesData, + } + return nil +} + +func (t *ThreadBuilder) PossibleHeads() []string { return t.maybeHeads } -func (t *ThreadBuilder) SetMaybeHeads(heads []string) { +func (t *ThreadBuilder) SetPossibleHeads(heads []string) { // we should copy here instead of just setting the value t.maybeHeads = heads } diff --git a/thread/models.go b/thread/models.go index 3dee82e1..89e0737c 100644 --- a/thread/models.go +++ b/thread/models.go @@ -2,16 +2,23 @@ package thread import ( "context" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges" ) +// TODO: change methods to have errors as a return parameter, because we will be dealing with a real database type Thread interface { ID() string + Heads() []string - MaybeHeads() []string - GetChange(ctx context.Context, recordID string) (*RawChange, error) + PossibleHeads() []string SetHeads(heads []string) - SetMaybeHeads(heads []string) - AddChange(change *RawChange) error + SetPossibleHeads(heads []string) + AddPossibleHead(head string) + + AddRawChange(change *RawChange) error + AddChange(change aclchanges.Change) error + GetChange(ctx context.Context, recordID string) (*RawChange, error) } type RawChange struct { From fc01b705a594ba106482f6897966131a8b55010b Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 10 Jul 2022 15:53:50 +0200 Subject: [PATCH 33/53] Add logic update and create logic --- acltree/aclstatebuilder.go | 56 +++++----- acltree/aclstatebuilder_test.go | 26 ++--- acltree/acltree.go | 126 +++++++++++++++++------ acltree/acltreebuilder.go | 13 ++- acltree/changeloader.go | 2 +- acltree/snapshotvalidator.go | 8 +- acltree/treebuilder.go | 16 +-- testutils/threadbuilder/threadbuilder.go | 4 +- thread/models.go | 7 +- 9 files changed, 164 insertions(+), 94 deletions(-) diff --git a/acltree/aclstatebuilder.go b/acltree/aclstatebuilder.go index 3eb62783..609ab793 100644 --- a/acltree/aclstatebuilder.go +++ b/acltree/aclstatebuilder.go @@ -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 } diff --git a/acltree/aclstatebuilder_test.go b/acltree/aclstatebuilder_test.go index 228744e5..c5ea0c79 100644 --- a/acltree/aclstatebuilder_test.go +++ b/acltree/aclstatebuilder_test.go @@ -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()) diff --git a/acltree/acltree.go b/acltree/acltree.go index 1cb83da7..0162cd33 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -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() } diff --git a/acltree/acltreebuilder.go b/acltree/acltreebuilder.go index 83d7afd3..7c552407 100644 --- a/acltree/acltreebuilder.go +++ b/acltree/acltreebuilder.go @@ -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 } diff --git a/acltree/changeloader.go b/acltree/changeloader.go index 0dfe9e25..82c3df99 100644 --- a/acltree/changeloader.go +++ b/acltree/changeloader.go @@ -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 diff --git a/acltree/snapshotvalidator.go b/acltree/snapshotvalidator.go index b64f3d4a..8bf19451 100644 --- a/acltree/snapshotvalidator.go +++ b/acltree/snapshotvalidator.go @@ -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 } diff --git a/acltree/treebuilder.go b/acltree/treebuilder.go index 8196046e..0e3bc79d 100644 --- a/acltree/treebuilder.go +++ b/acltree/treebuilder.go @@ -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) } } diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 9d695479..081a961f 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -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 } diff --git a/thread/models.go b/thread/models.go index 89e0737c..1e57beb7 100644 --- a/thread/models.go +++ b/thread/models.go @@ -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) } From 0129c97c83928baecf9be92e53a23f0e22e5cb65 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 10 Jul 2022 17:14:15 +0200 Subject: [PATCH 34/53] Change ACLTree methods to include change builder --- acltree/acltree.go | 108 +++++++++------------------------------ acltree/changebuilder.go | 101 ++++++++++++++++++++++++++++++++++++ acltree/tree.go | 29 +++++++++++ 3 files changed, 155 insertions(+), 83 deletions(-) create mode 100644 acltree/changebuilder.go diff --git a/acltree/acltree.go b/acltree/acltree.go index 0162cd33..59268b34 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -1,13 +1,11 @@ package acltree import ( + "sync" + "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/util/keys" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "github.com/gogo/protobuf/proto" - "sync" ) type AddResultSummary int @@ -25,10 +23,9 @@ type AddResult struct { Summary AddResultSummary } -// TODO: Change add change content to include ACLChangeBuilder type ACLTree interface { ACLState() *ACLState - AddContent(changeContent *ChangeContent) (*Change, error) + AddContent(f func(builder ChangeBuilder)) (*Change, error) AddChanges(changes ...*Change) (AddResult, error) Heads() []string Iterate(func(change *Change) bool) @@ -40,14 +37,15 @@ type aclTree struct { thread thread.Thread accountData *account.AccountData - 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 - aclState *ACLState + fullTree *Tree + 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 treeBuilder *treeBuilder aclTreeBuilder *aclTreeBuilder aclStateBuilder *aclStateBuilder snapshotValidator *snapshotValidator + changeBuilder *changeBuilder sync.Mutex } @@ -58,6 +56,7 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) { treeBuilder := newTreeBuilder(t, decoder) snapshotValidator := newSnapshotValidator(decoder, acc) aclStateBuilder := newACLStateBuilder(decoder, acc) + changeBuilder := newChangeBuilder() aclTree := &aclTree{ thread: t, @@ -68,6 +67,7 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) { aclTreeBuilder: aclTreeBuilder, aclStateBuilder: aclStateBuilder, snapshotValidator: snapshotValidator, + changeBuilder: changeBuilder, } err := aclTree.rebuildFromThread(false) 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 func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) { if validateSnapshot { - err = a.snapshotValidator.Init(a.aclTree) + err = a.snapshotValidator.Init(a.aclTreeFromStart) if err != nil { 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 - a.aclTree, err = a.aclTreeBuilder.Build() + a.aclTreeFromStart, err = a.aclTreeBuilder.Build() if err != nil { return err } if !fromStart { - err = a.snapshotValidator.Init(a.aclTree) + err = a.snapshotValidator.Init(a.aclTreeFromStart) if err != nil { return err } @@ -150,98 +150,40 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error { 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 { 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 a.Lock() 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 { 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) err = a.thread.AddRawChange(&thread.RawChange{ Payload: marshalled, - Signature: signature, - Id: changeContent.Id, + Signature: ch.Signature(), + Id: ch.Id, }) if err != nil { return nil, err } a.thread.SetHeads([]string{ch.Id}) - return a.fullTree.attached[changeContent.Id], nil + return ch, nil } func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go new file mode 100644 index 00000000..458bc85b --- /dev/null +++ b/acltree/changebuilder.go @@ -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 +} diff --git a/acltree/tree.go b/acltree/tree.go index 326ebcad..e47ed212 100644 --- a/acltree/tree.go +++ b/acltree/tree.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/md5" "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "sort" ) @@ -288,6 +289,34 @@ func (t *Tree) updateHeads() { 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)) { it := newIterator() defer freeIterator(it) From cc802ebc698b0cd248ee2eb0d21ca9dd16051ad5 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Sun, 10 Jul 2022 17:33:27 +0200 Subject: [PATCH 35/53] WIP ACLTree tests --- acltree/aclstatebuilder_test.go | 231 ------------------ acltree/acltree_test.go | 166 +++++++++++++ acltree/treebuilder_test.go | 6 + .../threadbuilder/invalidsnapshotexample.yml | 2 +- testutils/threadbuilder/threadbuilder.go | 29 ++- testutils/threadbuilder/userjoinexample.yml | 2 +- .../threadbuilder/userjoinexampleupdate.yml | 2 +- .../threadbuilder/userremovebeforeexample.yml | 2 +- testutils/threadbuilder/userremoveexample.yml | 2 +- .../threadbuilder/validsnapshotexample.yml | 2 +- testutils/threadbuilder/ymlentities.go | 4 +- 11 files changed, 194 insertions(+), 254 deletions(-) delete mode 100644 acltree/aclstatebuilder_test.go create mode 100644 acltree/acltree_test.go diff --git a/acltree/aclstatebuilder_test.go b/acltree/aclstatebuilder_test.go deleted file mode 100644 index c5ea0c79..00000000 --- a/acltree/aclstatebuilder_test.go +++ /dev/null @@ -1,231 +0,0 @@ -package acltree - -import ( - "testing" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" - - "github.com/stretchr/testify/assert" -) - -type ACLContext struct { - Tree *Tree - ACLState *ACLState -} - -func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) { - treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder()) - treeBuilder.Init() - return treeBuilder.Build(fromStart) -} - -func createACLStateFromThread( - t thread.Thread, - identity string, - key keys.EncryptionPrivKey, - decoder keys.SigningPubKeyDecoder, - fromStart bool) (*ACLContext, error) { - tree, err := createTreeFromThread(t, fromStart) - if err != nil { - return nil, err - } - - accountData := &account.AccountData{ - Identity: identity, - EncKey: key, - } - - 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) - 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 - return createACLStateFromThread(t, identity, key, decoder, true) - } - } - - aclBuilder := newACLStateBuilder(decoder, accountData) - err = aclBuilder.Init(tree) - if err != nil { - return nil, err - } - - aclState, err := aclBuilder.Build() - if err != nil { - return nil, err - } - return &ACLContext{ - Tree: tree, - ACLState: aclState, - }, nil -} - -func TestACLStateBuilder_UserJoinBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - aId := keychain.GeneratedIdentities["A"] - bId := keychain.GeneratedIdentities["B"] - cId := keychain.GeneratedIdentities["C"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, pb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, 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", "B.1.2"}) -} - -func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - aId := keychain.GeneratedIdentities["A"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, 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", "A.1.4"}) -} - -func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremovebeforeexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - for _, s := range []string{"A", "C", "E"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")]) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3"}) -} - -func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/invalidsnapshotexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3", "B.1.2"}, changeIds) -} - -func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/validsnapshotexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, []string{"A.1.2", "A.1.3", "B.1.2"}, changeIds) -} diff --git a/acltree/acltree_test.go b/acltree/acltree_test.go new file mode 100644 index 00000000..ebf3e4c2 --- /dev/null +++ b/acltree/acltree_test.go @@ -0,0 +1,166 @@ +package acltree + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" +) + +func TestACLTree_UserJoinBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + aId := keychain.GeneratedIdentities["A"] + bId := keychain.GeneratedIdentities["B"] + cId := keychain.GeneratedIdentities["C"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + assert.Equal(t, aclState.userStates[bId].Permissions, pb.ACLChange_Writer) + assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) + + var changeIds []string + tree.Iterate(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", "B.1.2"}) +} + +func TestACLTree_UserRemoveBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + aId := keychain.GeneratedIdentities["A"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + + var changeIds []string + tree.Iterate(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", "A.1.4"}) +} + +func TestACLTree_UserRemoveBeforeBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremovebeforeexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "C", "E"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")]) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, changeIds, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3"}) +} + +func TestACLTree_InvalidSnapshotBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/invalidsnapshotexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "B", "C", "D", "E", "F"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3", "B.1.2"}, changeIds) +} + +func TestACLTree_ValidSnapshotBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/validsnapshotexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "B", "C", "D", "E", "F"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, []string{"A.1.2", "A.1.3", "B.1.2"}, changeIds) +} diff --git a/acltree/treebuilder_test.go b/acltree/treebuilder_test.go index e7f8145a..02d0a317 100644 --- a/acltree/treebuilder_test.go +++ b/acltree/treebuilder_test.go @@ -1,5 +1,11 @@ package acltree +//func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) { +// treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder()) +// treeBuilder.Init() +// return treeBuilder.Build(fromStart) +//} +// //func TestACLTreeBuilder_UserJoinCorrectHeadsAndLen(t *testing.T) { // thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") // if err != nil { diff --git a/testutils/threadbuilder/invalidsnapshotexample.yml b/testutils/threadbuilder/invalidsnapshotexample.yml index 317e575b..e3877b47 100644 --- a/testutils/threadbuilder/invalidsnapshotexample.yml +++ b/testutils/threadbuilder/invalidsnapshotexample.yml @@ -117,7 +117,7 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] -maybeHeads: +orphans: - A.1.3 - B.1.2 diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 081a961f..72784452 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "io/ioutil" "github.com/gogo/protobuf/proto" @@ -31,7 +32,7 @@ type ThreadBuilder struct { allChanges map[string]*threadChange updatedChanges map[string]*threadChange heads []string - maybeHeads []string + orphans []string keychain *Keychain } @@ -109,8 +110,8 @@ func (t *ThreadBuilder) AddRawChange(change *thread.RawChange) error { return nil } -func (t *ThreadBuilder) AddOrphans(head string) { - t.maybeHeads = append(t.maybeHeads, head) +func (t *ThreadBuilder) AddOrphans(orphans ...string) { + t.orphans = append(t.orphans, orphans...) } func (t *ThreadBuilder) AddChange(change aclchanges.Change) error { @@ -141,12 +142,7 @@ func (t *ThreadBuilder) AddChange(change aclchanges.Change) error { } func (t *ThreadBuilder) Orphans() []string { - return t.maybeHeads -} - -func (t *ThreadBuilder) SetPossibleHeads(heads []string) { - // we should copy here instead of just setting the value - t.maybeHeads = heads + return t.orphans } func (t *ThreadBuilder) SetHeads(heads []string) { @@ -154,6 +150,10 @@ func (t *ThreadBuilder) SetHeads(heads []string) { t.heads = heads } +func (t *ThreadBuilder) RemoveOrphans(orphans ...string) { + t.orphans = slice.Difference(t.orphans, orphans) +} + func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*thread.RawChange, error) { return t.getChange(recordID, t.allChanges), nil } @@ -214,7 +214,7 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { } t.parseGraph(thread) - t.parseHeads(thread) + t.parseOrphans(thread) } func (t *ThreadBuilder) parseChange(ch *Change) *threadChange { @@ -469,8 +469,8 @@ func (t *ThreadBuilder) convertPermission(perm string) pb.ACLChangeUserPermissio func (t *ThreadBuilder) traverseFromHeads(f func(t *threadChange) error) error { uniqMap := map[string]struct{}{} - stack := make([]string, len(t.maybeHeads), 10) - copy(stack, t.maybeHeads) + stack := make([]string, len(t.orphans), 10) + copy(stack, t.orphans) for len(stack) > 0 { id := stack[len(stack)-1] stack = stack[:len(stack)-1] @@ -507,7 +507,6 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) { } } -func (t *ThreadBuilder) parseHeads(thread *YMLThread) { - t.heads = thread.Heads - t.maybeHeads = thread.MaybeHeads +func (t *ThreadBuilder) parseOrphans(thread *YMLThread) { + t.orphans = thread.Orphans } diff --git a/testutils/threadbuilder/userjoinexample.yml b/testutils/threadbuilder/userjoinexample.yml index 02092a8a..c2a0b11b 100644 --- a/testutils/threadbuilder/userjoinexample.yml +++ b/testutils/threadbuilder/userjoinexample.yml @@ -99,7 +99,7 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] -maybeHeads: +orphans: - "A.1.3" updatedChanges: - id: B.1.3 diff --git a/testutils/threadbuilder/userjoinexampleupdate.yml b/testutils/threadbuilder/userjoinexampleupdate.yml index 80f4fcda..daf5d550 100644 --- a/testutils/threadbuilder/userjoinexampleupdate.yml +++ b/testutils/threadbuilder/userjoinexampleupdate.yml @@ -101,7 +101,7 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] -maybeHeads: +orphans: - "A.1.3" updatedChanges: - id: B.1.3 diff --git a/testutils/threadbuilder/userremovebeforeexample.yml b/testutils/threadbuilder/userremovebeforeexample.yml index fe4d2b6e..7569ec04 100644 --- a/testutils/threadbuilder/userremovebeforeexample.yml +++ b/testutils/threadbuilder/userremovebeforeexample.yml @@ -101,6 +101,6 @@ graph: baseSnapshot: A.1.1 aclHeads: [A.1.2] treeHeads: [A.1.2] -maybeHeads: +orphans: - "A.1.3" - "B.1.2" diff --git a/testutils/threadbuilder/userremoveexample.yml b/testutils/threadbuilder/userremoveexample.yml index ab131fb5..36a4a191 100644 --- a/testutils/threadbuilder/userremoveexample.yml +++ b/testutils/threadbuilder/userremoveexample.yml @@ -101,6 +101,6 @@ graph: aclSnapshot: A.1.1 aclHeads: [A.1.3] treeHeads: [A.1.3] -maybeHeads: +orphans: - "A.1.4" - "B.1.2" diff --git a/testutils/threadbuilder/validsnapshotexample.yml b/testutils/threadbuilder/validsnapshotexample.yml index 2c3e49d0..1fef3c35 100644 --- a/testutils/threadbuilder/validsnapshotexample.yml +++ b/testutils/threadbuilder/validsnapshotexample.yml @@ -124,7 +124,7 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] -maybeHeads: +orphans: - "A.1.3" - "B.1.2" diff --git a/testutils/threadbuilder/ymlentities.go b/testutils/threadbuilder/ymlentities.go index 33a9a34e..68c7f3ac 100644 --- a/testutils/threadbuilder/ymlentities.go +++ b/testutils/threadbuilder/ymlentities.go @@ -100,6 +100,6 @@ type YMLThread struct { Graph []*GraphNode `yaml:"graph"` UpdatedGraph []*GraphNode `yaml:"updatedGraph"` - Heads []string `yaml:"heads"` - MaybeHeads []string `yaml:"maybeHeads"` + Heads []string `yaml:"heads"` + Orphans []string `yaml:"orphans"` } From 5b9d403b7df3809a42f1529c3efcb2df9b310aae Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Mon, 11 Jul 2022 11:28:58 +0200 Subject: [PATCH 36/53] Add tree updater to ACL tree --- acltree/acltree.go | 40 +- exampledocument/docstate.go | 9 - exampledocument/document.go | 303 -------------- exampledocument/plaintextdocstate.go | 68 ---- plaintextdocument/document.go | 378 ++++++++++++++++++ .../document_test.go | 8 +- .../documentcontext.go | 2 +- .../documentstatebuilder.go | 2 +- .../documentstatebuilder_test.go | 6 +- plaintextdocument/plaintextdocstate.go | 59 +++ plaintextdocument/plaintextdocument.go | 1 + 11 files changed, 478 insertions(+), 398 deletions(-) delete mode 100644 exampledocument/docstate.go delete mode 100644 exampledocument/document.go delete mode 100644 exampledocument/plaintextdocstate.go create mode 100644 plaintextdocument/document.go rename {exampledocument => plaintextdocument}/document_test.go (87%) rename {exampledocument => plaintextdocument}/documentcontext.go (90%) rename {exampledocument => plaintextdocument}/documentstatebuilder.go (98%) rename {exampledocument => plaintextdocument}/documentstatebuilder_test.go (91%) create mode 100644 plaintextdocument/plaintextdocstate.go create mode 100644 plaintextdocument/plaintextdocument.go diff --git a/acltree/acltree.go b/acltree/acltree.go index 59268b34..276a4054 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -23,19 +23,26 @@ type AddResult struct { Summary AddResultSummary } +type TreeUpdateListener interface { + Update(tree ACLTree) + Rebuild(tree ACLTree) +} + type ACLTree interface { ACLState() *ACLState AddContent(f func(builder ChangeBuilder)) (*Change, error) AddChanges(changes ...*Change) (AddResult, error) Heads() []string + Root() *Change Iterate(func(change *Change) bool) IterateFrom(string, func(change *Change) bool) HasChange(string) bool } type aclTree struct { - thread thread.Thread - accountData *account.AccountData + thread thread.Thread + accountData *account.AccountData + updateListener TreeUpdateListener fullTree *Tree 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 @@ -50,7 +57,10 @@ type aclTree struct { sync.Mutex } -func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) { +func BuildACLTree( + t thread.Thread, + acc *account.AccountData, + listener TreeUpdateListener) (ACLTree, error) { decoder := keys.NewEd25519Decoder() aclTreeBuilder := newACLTreeBuilder(t, decoder) treeBuilder := newTreeBuilder(t, decoder) @@ -68,6 +78,7 @@ func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) { aclStateBuilder: aclStateBuilder, snapshotValidator: snapshotValidator, changeBuilder: changeBuilder, + updateListener: listener, } err := aclTree.rebuildFromThread(false) if err != nil { @@ -183,6 +194,7 @@ func (a *aclTree) AddContent(build func(builder ChangeBuilder)) (*Change, error) } a.thread.SetHeads([]string{ch.Id}) + a.updateListener.Update(a) return ch, nil } @@ -190,8 +202,8 @@ 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 + var mode Mode defer func() { if err != nil { @@ -209,13 +221,17 @@ func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { } } a.thread.RemoveOrphans(toRemove...) + switch mode { + case Append: + a.updateListener.Update(a) + case Rebuild: + a.updateListener.Rebuild(a) + default: + break + } }() for _, ch := range changes { - if ch.IsACLChange() { - aclChanges = append(aclChanges, ch) - break - } err = a.thread.AddChange(ch) if err != nil { return AddResult{}, err @@ -224,7 +240,7 @@ func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { } prevHeads := a.fullTree.Heads() - mode := a.fullTree.Add(changes...) + mode = a.fullTree.Add(changes...) switch mode { case Nothing: return AddResult{ @@ -284,3 +300,9 @@ func (a *aclTree) Heads() []string { defer a.Unlock() return a.fullTree.Heads() } + +func (a *aclTree) Root() *Change { + a.Lock() + defer a.Unlock() + return a.fullTree.Root() +} diff --git a/exampledocument/docstate.go b/exampledocument/docstate.go deleted file mode 100644 index 95bc41b6..00000000 --- a/exampledocument/docstate.go +++ /dev/null @@ -1,9 +0,0 @@ -package exampledocument - -type DocumentState interface { - ApplyChange(change []byte, id string) (DocumentState, error) -} - -type InitialStateProvider interface { - ProvideFromInitialChange(change []byte, id string) (DocumentState, error) -} diff --git a/exampledocument/document.go b/exampledocument/document.go deleted file mode 100644 index d196b111..00000000 --- a/exampledocument/document.go +++ /dev/null @@ -1,303 +0,0 @@ -package exampledocument - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "github.com/gogo/protobuf/proto" -) - -type AccountData struct { - Identity string - SignKey threadmodels.SigningPrivKey - EncKey threadmodels.EncryptionPrivKey -} - -type Document struct { - // TODO: ensure that every operation on Document is synchronized - thread threadmodels.Thread - stateProvider InitialStateProvider - accountData *AccountData - decoder threadmodels.SigningPubKeyDecoder - - treeBuilder *acltree.treeBuilder - aclTreeBuilder *acltree.aclTreeBuilder - aclStateBuilder *acltree.aclStateBuilder - snapshotValidator *acltree.snapshotValidator - docStateBuilder *acltree.documentStateBuilder - - docContext *acltree.documentContext -} - -type UpdateResult int - -const ( - UpdateResultNoAction UpdateResult = iota - UpdateResultAppend - UpdateResultRebuild -) - -type CreateChangePayload struct { - ChangesData proto.Marshaler - ACLData *pb.ACLChangeACLData - Id string // TODO: this is just for testing, because id should be created automatically from content -} - -func NewDocument( - thread threadmodels.Thread, - stateProvider InitialStateProvider, - accountData *AccountData) *Document { - - decoder := threadmodels.NewEd25519Decoder() - return &Document{ - thread: thread, - 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), - docStateBuilder: acltree.newDocumentStateBuilder(stateProvider), - docContext: &acltree.documentContext{}, - } -} - -//sync layer -> Object cache -> document -> Update (..raw changes) -//client layer -> Object cache -> document -> CreateChange(...) -// - -// smartblock -> CreateChange(payload) -// SmartTree iterate etc -func (d *Document) CreateChange(payload *CreateChangePayload) error { - // TODO: add snapshot creation logic - marshalled, err := payload.ChangesData.Marshal() - if err != nil { - return err - } - - encrypted, err := d.docContext.aclState.userReadKeys[d.docContext.aclState.currentReadKeyHash]. - Encrypt(marshalled) - if err != nil { - return err - } - - aclChange := &pb.ACLChange{ - TreeHeadIds: d.docContext.fullTree.Heads(), - AclHeadIds: d.getACLHeads(), - SnapshotBaseId: d.docContext.fullTree.RootId(), - AclData: payload.ACLData, - ChangesData: encrypted, - CurrentReadKeyHash: d.docContext.aclState.currentReadKeyHash, - Timestamp: 0, - Identity: d.accountData.Identity, - } - - // TODO: add CID creation logic based on content - ch := acltree.NewChange(payload.Id, aclChange) - ch.DecryptedDocumentChange = marshalled - - fullMarshalledChange, err := proto.Marshal(aclChange) - if err != nil { - return err - } - signature, err := d.accountData.SignKey.Sign(fullMarshalledChange) - if err != nil { - return err - } - - if aclChange.AclData != nil { - // we can apply change right away without going through builder, because - err = d.docContext.aclState.ApplyChange(payload.Id, aclChange) - if err != nil { - return err - } - } - d.docContext.fullTree.AddFast(ch) - - err = d.thread.AddChange(&thread.RawChange{ - Payload: marshalled, - Signature: signature, - Id: payload.Id, - }) - if err != nil { - return err - } - - d.thread.SetHeads([]string{ch.Id}) - d.thread.SetMaybeHeads([]string{ch.Id}) - - return nil -} - -func (d *Document) Update(changes ...*thread.RawChange) (DocumentState, UpdateResult, error) { - var treeChanges []*acltree.Change - - var foundACLChange bool - for _, ch := range changes { - aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch) - if err != nil { - return nil, UpdateResultNoAction, fmt.Errorf("change with id %s is incorrect: %w", ch.Id, err) - } - - treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) - treeChanges = append(treeChanges, treeChange) - - // this already sets PossibleHeads to include new changes - // TODO: change this behaviour as non-obvious, because it is not evident from the interface - err = d.thread.AddChange(ch) - if err != nil { - return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) - } - if treeChange.IsACLChange() { - foundACLChange = true - } - } - - if foundACLChange { - res, err := d.Build() - return res, UpdateResultRebuild, err - } - - prevHeads := d.docContext.fullTree.Heads() - mode := d.docContext.fullTree.Add(treeChanges...) - switch mode { - case acltree.Nothing: - return d.docContext.docState, UpdateResultNoAction, nil - case acltree.Rebuild: - res, err := d.Build() - return res, UpdateResultRebuild, err - default: - break - } - - // TODO: we should still check if the user making those changes are able to write using "aclState" - // decrypting everything, because we have no new keys - for _, ch := range treeChanges { - if ch.Content.GetChangesData() != nil { - key, exists := d.docContext.aclState.userReadKeys[ch.Content.CurrentReadKeyHash] - if !exists { - err := fmt.Errorf("failed to find key with hash: %d", ch.Content.CurrentReadKeyHash) - return nil, UpdateResultNoAction, err - } - - err := ch.DecryptContents(key) - if err != nil { - err = fmt.Errorf("failed to decrypt contents for hash: %d", ch.Content.CurrentReadKeyHash) - return nil, UpdateResultNoAction, err - } - } - } - - // because for every new change we know it was after any of the previous heads - // each of previous heads must have same "Next" nodes - // so it doesn't matter which one we choose - // so we choose first one - newState, err := d.docStateBuilder.appendFrom(prevHeads[0], d.docContext.docState) - if err != nil { - res, _ := d.Build() - return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") - } - - // setting all heads - d.thread.SetHeads(d.docContext.fullTree.Heads()) - // this should be the entrypoint when we build the document - d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) - - return newState, UpdateResultAppend, nil -} - -func (d *Document) Build() (DocumentState, error) { - return d.build(false) -} - -// TODO: this should not be the responsibility of Document, move it somewhere else after testing -func (d *Document) getACLHeads() []string { - var aclTreeHeads []string - for _, head := range d.docContext.fullTree.Heads() { - if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads - continue - } - precedingHeads := d.getPrecedingACLHeads(head) - - for _, aclHead := range precedingHeads { - if slice.FindPos(aclTreeHeads, aclHead) != -1 { - continue - } - aclTreeHeads = append(aclTreeHeads, aclHead) - } - } - return aclTreeHeads -} - -func (d *Document) getPrecedingACLHeads(head string) []string { - headChange := d.docContext.fullTree.attached[head] - - if headChange.Content.GetAclData() != nil { - return []string{head} - } else { - return headChange.Content.AclHeadIds - } -} - -func (d *Document) build(fromStart bool) (DocumentState, error) { - d.treeBuilder.init() - d.aclTreeBuilder.init() - - var err error - 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() - if err != nil { - return nil, err - } - - if !fromStart { - err = d.snapshotValidator.init(d.docContext.aclTree) - if err != nil { - return nil, err - } - - valid, err := d.snapshotValidator.validateSnapshot(d.docContext.fullTree.root) - if err != nil { - return nil, err - } - if !valid { - return d.build(true) - } - } - err = d.aclStateBuilder.init(d.docContext.fullTree) - if err != nil { - return nil, err - } - - d.docContext.aclState, err = d.aclStateBuilder.build() - if err != nil { - return nil, err - } - - // tree should be exposed - - d.docStateBuilder.init(d.docContext.aclState, d.docContext.fullTree) - d.docContext.docState, err = d.docStateBuilder.build() - if err != nil { - return nil, err - } - - // setting all heads - d.thread.SetHeads(d.docContext.fullTree.Heads()) - // this should be the entrypoint when we build the document - d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) - - return d.docContext.docState, nil -} - -func (d *Document) State() DocumentState { - return nil -} diff --git a/exampledocument/plaintextdocstate.go b/exampledocument/plaintextdocstate.go deleted file mode 100644 index e5296e42..00000000 --- a/exampledocument/plaintextdocstate.go +++ /dev/null @@ -1,68 +0,0 @@ -package exampledocument - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" - "github.com/gogo/protobuf/proto" -) - -// TestDocumentState -> testutils -// ThreadBuilder -> testutils -// move protos to test utils - -type PlainTextDocumentState struct { - LastChangeId string - Text string -} - -func NewPlainTextDocumentState(text string, id string) *PlainTextDocumentState { - return &PlainTextDocumentState{ - LastChangeId: id, - Text: text, - } -} - -func (p *PlainTextDocumentState) ApplyChange(change []byte, id string) (DocumentState, error) { - var changesData pb.PlainTextChangeData - err := proto.Unmarshal(change, &changesData) - if err != nil { - return nil, err - } - - for _, content := range changesData.GetContent() { - err = p.applyChange(content) - if err != nil { - return nil, err - } - } - p.LastChangeId = id - return p, nil -} - -func (p *PlainTextDocumentState) applyChange(ch *pb.PlainTextChangeContent) error { - switch { - case ch.GetTextAppend() != nil: - text := ch.GetTextAppend().GetText() - p.Text += "|" + text - } - return nil -} - -type PlainTextDocumentStateProvider struct{} - -func NewPlainTextDocumentStateProvider() *PlainTextDocumentStateProvider { - return &PlainTextDocumentStateProvider{} -} - -func (p *PlainTextDocumentStateProvider) ProvideFromInitialChange(change []byte, id string) (DocumentState, error) { - var changesData pb.PlainTextChangeData - err := proto.Unmarshal(change, &changesData) - if err != nil { - return nil, err - } - - if changesData.GetSnapshot() == nil { - return nil, fmt.Errorf("could not create state from empty snapshot") - } - return NewPlainTextDocumentState(changesData.GetSnapshot().GetText(), id), nil -} diff --git a/plaintextdocument/document.go b/plaintextdocument/document.go new file mode 100644 index 00000000..3908cb29 --- /dev/null +++ b/plaintextdocument/document.go @@ -0,0 +1,378 @@ +package plaintextdocument + +import ( + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" +) + +type PlainTextDocument struct { + heads []string + aclTree acltree.ACLTree + state *DocumentState +} + +func (p *PlainTextDocument) Update(tree acltree.ACLTree) { + var err error + defer func() { + if err != nil { + fmt.Println("rebuild has returned error:", err) + } + }() + + prevHeads := p.heads + p.heads = tree.Heads() + startId := prevHeads[0] + tree.IterateFrom(startId, func(change *acltree.Change) (isContinue bool) { + if change.Id == startId { + return true + } + if change.DecryptedDocumentChange != nil { + p.state, err = p.state.ApplyChange(change.DecryptedDocumentChange, change.Id) + if err != nil { + return false + } + } + return true + }) +} + +func (p *PlainTextDocument) Rebuild(tree acltree.ACLTree) { + p.heads = tree.Heads() + var startId string + var err error + defer func() { + if err != nil { + fmt.Println("rebuild has returned error:", err) + } + }() + + rootChange := tree.Root() + + if rootChange.DecryptedDocumentChange == nil { + err = fmt.Errorf("root doesn't have decrypted change") + return + } + + state, err := BuildDocumentStateFromChange(rootChange.DecryptedDocumentChange, rootChange.Id) + if err != nil { + return + } + + startId = rootChange.Id + tree.Iterate(func(change *acltree.Change) (isContinue bool) { + if startId == change.Id { + return true + } + if change.DecryptedDocumentChange != nil { + state, err = state.ApplyChange(change.DecryptedDocumentChange, change.Id) + if err != nil { + return false + } + } + return true + }) + if err != nil { + return + } + p.state = state +} + +func NewPlainTextDocument(t thread.Thread, acc *account.AccountData) (*PlainTextDocument, error) { + tree, e +} + +// +//type AccountData struct { +// Identity string +// SignKey threadmodels.SigningPrivKey +// EncKey threadmodels.EncryptionPrivKey +//} +// +//type Document struct { +// // TODO: ensure that every operation on Document is synchronized +// thread threadmodels.Thread +// stateProvider InitialStateProvider +// accountData *AccountData +// decoder threadmodels.SigningPubKeyDecoder +// +// treeBuilder *acltree.treeBuilder +// aclTreeBuilder *acltree.aclTreeBuilder +// aclStateBuilder *acltree.aclStateBuilder +// snapshotValidator *acltree.snapshotValidator +// docStateBuilder *acltree.documentStateBuilder +// +// docContext *acltree.documentContext +//} +// +//type UpdateResult int +// +//const ( +// UpdateResultNoAction UpdateResult = iota +// UpdateResultAppend +// UpdateResultRebuild +//) +// +//type CreateChangePayload struct { +// ChangesData proto.Marshaler +// ACLData *pb.ACLChangeACLData +// Id string // TODO: this is just for testing, because id should be created automatically from content +//} +// +//func NewDocument( +// thread threadmodels.Thread, +// stateProvider InitialStateProvider, +// accountData *AccountData) *Document { +// +// decoder := threadmodels.NewEd25519Decoder() +// return &Document{ +// thread: thread, +// 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), +// docStateBuilder: acltree.newDocumentStateBuilder(stateProvider), +// docContext: &acltree.documentContext{}, +// } +//} +// +////sync layer -> Object cache -> document -> Update (..raw changes) +////client layer -> Object cache -> document -> CreateChange(...) +//// +// +//// smartblock -> CreateChange(payload) +//// SmartTree iterate etc +//func (d *Document) CreateChange(payload *CreateChangePayload) error { +// // TODO: add snapshot creation logic +// marshalled, err := payload.ChangesData.Marshal() +// if err != nil { +// return err +// } +// +// encrypted, err := d.docContext.aclState.userReadKeys[d.docContext.aclState.currentReadKeyHash]. +// Encrypt(marshalled) +// if err != nil { +// return err +// } +// +// aclChange := &pb.ACLChange{ +// TreeHeadIds: d.docContext.fullTree.Heads(), +// AclHeadIds: d.getACLHeads(), +// SnapshotBaseId: d.docContext.fullTree.RootId(), +// AclData: payload.ACLData, +// ChangesData: encrypted, +// CurrentReadKeyHash: d.docContext.aclState.currentReadKeyHash, +// Timestamp: 0, +// Identity: d.accountData.Identity, +// } +// +// // TODO: add CID creation logic based on content +// ch := acltree.NewChange(payload.Id, aclChange) +// ch.DecryptedDocumentChange = marshalled +// +// fullMarshalledChange, err := proto.Marshal(aclChange) +// if err != nil { +// return err +// } +// signature, err := d.accountData.SignKey.Sign(fullMarshalledChange) +// if err != nil { +// return err +// } +// +// if aclChange.AclData != nil { +// // we can apply change right away without going through builder, because +// err = d.docContext.aclState.ApplyChange(payload.Id, aclChange) +// if err != nil { +// return err +// } +// } +// d.docContext.fullTree.AddFast(ch) +// +// err = d.thread.AddChange(&thread.RawChange{ +// Payload: marshalled, +// Signature: signature, +// Id: payload.Id, +// }) +// if err != nil { +// return err +// } +// +// d.thread.SetHeads([]string{ch.Id}) +// d.thread.SetMaybeHeads([]string{ch.Id}) +// +// return nil +//} +// +//func (d *Document) Update(changes ...*thread.RawChange) (DocumentState, UpdateResult, error) { +// var treeChanges []*acltree.Change +// +// var foundACLChange bool +// for _, ch := range changes { +// aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch) +// if err != nil { +// return nil, UpdateResultNoAction, fmt.Errorf("change with id %s is incorrect: %w", ch.Id, err) +// } +// +// treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) +// treeChanges = append(treeChanges, treeChange) +// +// // this already sets PossibleHeads to include new changes +// // TODO: change this behaviour as non-obvious, because it is not evident from the interface +// err = d.thread.AddChange(ch) +// if err != nil { +// return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) +// } +// if treeChange.IsACLChange() { +// foundACLChange = true +// } +// } +// +// if foundACLChange { +// res, err := d.Build() +// return res, UpdateResultRebuild, err +// } +// +// prevHeads := d.docContext.fullTree.Heads() +// mode := d.docContext.fullTree.Add(treeChanges...) +// switch mode { +// case acltree.Nothing: +// return d.docContext.docState, UpdateResultNoAction, nil +// case acltree.Rebuild: +// res, err := d.Build() +// return res, UpdateResultRebuild, err +// default: +// break +// } +// +// // TODO: we should still check if the user making those changes are able to write using "aclState" +// // decrypting everything, because we have no new keys +// for _, ch := range treeChanges { +// if ch.Content.GetChangesData() != nil { +// key, exists := d.docContext.aclState.userReadKeys[ch.Content.CurrentReadKeyHash] +// if !exists { +// err := fmt.Errorf("failed to find key with hash: %d", ch.Content.CurrentReadKeyHash) +// return nil, UpdateResultNoAction, err +// } +// +// err := ch.DecryptContents(key) +// if err != nil { +// err = fmt.Errorf("failed to decrypt contents for hash: %d", ch.Content.CurrentReadKeyHash) +// return nil, UpdateResultNoAction, err +// } +// } +// } +// +// // because for every new change we know it was after any of the previous heads +// // each of previous heads must have same "Next" nodes +// // so it doesn't matter which one we choose +// // so we choose first one +// newState, err := d.docStateBuilder.appendFrom(prevHeads[0], d.docContext.docState) +// if err != nil { +// res, _ := d.Build() +// return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") +// } +// +// // setting all heads +// d.thread.SetHeads(d.docContext.fullTree.Heads()) +// // this should be the entrypoint when we build the document +// d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) +// +// return newState, UpdateResultAppend, nil +//} +// +//func (d *Document) Build() (DocumentState, error) { +// return d.build(false) +//} +// +//// TODO: this should not be the responsibility of Document, move it somewhere else after testing +//func (d *Document) getACLHeads() []string { +// var aclTreeHeads []string +// for _, head := range d.docContext.fullTree.Heads() { +// if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads +// continue +// } +// precedingHeads := d.getPrecedingACLHeads(head) +// +// for _, aclHead := range precedingHeads { +// if slice.FindPos(aclTreeHeads, aclHead) != -1 { +// continue +// } +// aclTreeHeads = append(aclTreeHeads, aclHead) +// } +// } +// return aclTreeHeads +//} +// +//func (d *Document) getPrecedingACLHeads(head string) []string { +// headChange := d.docContext.fullTree.attached[head] +// +// if headChange.Content.GetAclData() != nil { +// return []string{head} +// } else { +// return headChange.Content.AclHeadIds +// } +//} +// +//func (d *Document) build(fromStart bool) (DocumentState, error) { +// d.treeBuilder.init() +// d.aclTreeBuilder.init() +// +// var err error +// 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() +// if err != nil { +// return nil, err +// } +// +// if !fromStart { +// err = d.snapshotValidator.init(d.docContext.aclTree) +// if err != nil { +// return nil, err +// } +// +// valid, err := d.snapshotValidator.validateSnapshot(d.docContext.fullTree.root) +// if err != nil { +// return nil, err +// } +// if !valid { +// return d.build(true) +// } +// } +// err = d.aclStateBuilder.init(d.docContext.fullTree) +// if err != nil { +// return nil, err +// } +// +// d.docContext.aclState, err = d.aclStateBuilder.build() +// if err != nil { +// return nil, err +// } +// +// // tree should be exposed +// +// d.docStateBuilder.init(d.docContext.aclState, d.docContext.fullTree) +// d.docContext.docState, err = d.docStateBuilder.build() +// if err != nil { +// return nil, err +// } +// +// // setting all heads +// d.thread.SetHeads(d.docContext.fullTree.Heads()) +// // this should be the entrypoint when we build the document +// d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) +// +// return d.docContext.docState, nil +//} +// +//func (d *Document) State() DocumentState { +// return nil +//} diff --git a/exampledocument/document_test.go b/plaintextdocument/document_test.go similarity index 87% rename from exampledocument/document_test.go rename to plaintextdocument/document_test.go index 99fea827..a2b6691f 100644 --- a/exampledocument/document_test.go +++ b/plaintextdocument/document_test.go @@ -1,4 +1,4 @@ -package exampledocument +package plaintextdocument import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" @@ -22,7 +22,7 @@ func TestDocument_Build(t *testing.T) { t.Fatal(err) } - st := res.(*PlainTextDocumentState) + st := res.(*DocumentState) assert.Equal(t, st.Text, "some text|first") } @@ -42,11 +42,11 @@ func TestDocument_Update(t *testing.T) { t.Fatal(err) } - st := res.(*PlainTextDocumentState) + st := res.(*DocumentState) assert.Equal(t, st.Text, "some text|first") rawChs := thread.GetUpdatedChanges() res, updateResult, err := doc.Update(rawChs...) assert.Equal(t, updateResult, UpdateResultAppend) - assert.Equal(t, res.(*PlainTextDocumentState).Text, "some text|first|second") + assert.Equal(t, res.(*DocumentState).Text, "some text|first|second") } diff --git a/exampledocument/documentcontext.go b/plaintextdocument/documentcontext.go similarity index 90% rename from exampledocument/documentcontext.go rename to plaintextdocument/documentcontext.go index bb165ab8..2aae6a97 100644 --- a/exampledocument/documentcontext.go +++ b/plaintextdocument/documentcontext.go @@ -1,4 +1,4 @@ -package exampledocument +package plaintextdocument import "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" diff --git a/exampledocument/documentstatebuilder.go b/plaintextdocument/documentstatebuilder.go similarity index 98% rename from exampledocument/documentstatebuilder.go rename to plaintextdocument/documentstatebuilder.go index 4a6ac604..ab23274e 100644 --- a/exampledocument/documentstatebuilder.go +++ b/plaintextdocument/documentstatebuilder.go @@ -1,4 +1,4 @@ -package exampledocument +package plaintextdocument import ( "fmt" diff --git a/exampledocument/documentstatebuilder_test.go b/plaintextdocument/documentstatebuilder_test.go similarity index 91% rename from exampledocument/documentstatebuilder_test.go rename to plaintextdocument/documentstatebuilder_test.go index 3f1d7232..b293d212 100644 --- a/exampledocument/documentstatebuilder_test.go +++ b/plaintextdocument/documentstatebuilder_test.go @@ -1,4 +1,4 @@ -package exampledocument +package plaintextdocument import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" @@ -23,7 +23,7 @@ func TestDocumentStateBuilder_UserJoinBuild(t *testing.T) { t.Fatalf("should build acl aclState without err: %v", err) } - st := ctx.DocState.(*PlainTextDocumentState) + st := ctx.DocState.(*DocumentState) assert.Equal(t, st.Text, "some text|first") } @@ -43,6 +43,6 @@ func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { t.Fatalf("should build acl aclState without err: %v", err) } - st := ctx.DocState.(*PlainTextDocumentState) + st := ctx.DocState.(*DocumentState) assert.Equal(t, st.Text, "some text|first") } diff --git a/plaintextdocument/plaintextdocstate.go b/plaintextdocument/plaintextdocstate.go new file mode 100644 index 00000000..d7447b03 --- /dev/null +++ b/plaintextdocument/plaintextdocstate.go @@ -0,0 +1,59 @@ +package plaintextdocument + +import ( + "fmt" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb" + "github.com/gogo/protobuf/proto" +) + +type DocumentState struct { + LastChangeId string + Text string +} + +func NewDocumentState(text string, id string) *DocumentState { + return &DocumentState{ + LastChangeId: id, + Text: text, + } +} + +func BuildDocumentStateFromChange(change []byte, id string) (*DocumentState, error) { + var changesData pb.PlainTextChangeData + err := proto.Unmarshal(change, &changesData) + if err != nil { + return nil, err + } + + if changesData.GetSnapshot() == nil { + return nil, fmt.Errorf("could not create state from empty snapshot") + } + return NewDocumentState(changesData.GetSnapshot().GetText(), id), nil +} + +func (p *DocumentState) ApplyChange(change []byte, id string) (*DocumentState, error) { + var changesData pb.PlainTextChangeData + err := proto.Unmarshal(change, &changesData) + if err != nil { + return nil, err + } + + for _, content := range changesData.GetContent() { + err = p.applyChange(content) + if err != nil { + return nil, err + } + } + p.LastChangeId = id + return p, nil +} + +func (p *DocumentState) applyChange(ch *pb.PlainTextChangeContent) error { + switch { + case ch.GetTextAppend() != nil: + text := ch.GetTextAppend().GetText() + p.Text += "|" + text + } + return nil +} diff --git a/plaintextdocument/plaintextdocument.go b/plaintextdocument/plaintextdocument.go new file mode 100644 index 00000000..d033fb38 --- /dev/null +++ b/plaintextdocument/plaintextdocument.go @@ -0,0 +1 @@ +package plaintextdocument From 9242f78b88673a423940fe60ce98e9b98fbfbdd5 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Mon, 11 Jul 2022 23:40:44 +0200 Subject: [PATCH 37/53] Add thread header and make snapshot --- Makefile | 11 +- aclchanges/pb/aclchanges.pb.go | 152 +++++++------- acltree/aclstate.go | 27 +++ acltree/changebuilder.go | 6 +- thread/models.go | 3 + thread/pb/protos/thread.proto | 8 + thread/pb/thread.pb.go | 355 +++++++++++++++++++++++++++++++++ 7 files changed, 482 insertions(+), 80 deletions(-) create mode 100644 thread/pb/protos/thread.proto create mode 100644 thread/pb/thread.pb.go diff --git a/Makefile b/Makefile index 31dda6ee..fb87ac1e 100644 --- a/Makefile +++ b/Makefile @@ -17,8 +17,11 @@ protos-go: @echo 'Generating protobuf packages (Go)...' $(eval P_TIMESTAMP := Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types) $(eval P_STRUCT := Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types) - @$(eval P_ACL_CHANGES := Mdata/pb/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb) - @$(eval P_PLAINTEXT_CHANGES := Mdata/pb/protos/plaintextchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb) + @$(eval P_ACL_CHANGES := Mdata/pb/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb) + @$(eval P_THREAD := Mdata/pb/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/thread/pb) + @$(eval P_PLAINTEXT_CHANGES := Mdata/pb/protos/plaintextchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb) - $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT),$$(P_ACL_CHANGES),$$(P_PLAINTEXT_CHANGES)) - GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1 protoc --gogofaster_out=$(PKGMAP):./data/pb data/pb/protos/*.*; mv data/pb/data/pb/protos/*.go data/pb; rm -rf data/pb/data + $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT),$$(P_ACL_CHANGES)) + GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1 protoc --gogofaster_out=$(PKGMAP):./aclchanges/pb aclchanges/pb/protos/*.*; mv aclchanges/pb/aclchanges/pb/protos/*.go aclchanges/pb; rm -rf aclchanges/pb/aclchanges + $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT),$$(P_THREAD)) + GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1 protoc --gogofaster_out=$(PKGMAP):./thread/pb thread/pb/protos/*.*; mv thread/pb/thread/pb/protos/*.go thread/pb; rm -rf thread/pb/thread diff --git a/aclchanges/pb/aclchanges.pb.go b/aclchanges/pb/aclchanges.pb.go index 925d68d4..d872a7bb 100644 --- a/aclchanges/pb/aclchanges.pb.go +++ b/aclchanges/pb/aclchanges.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: data/pb/protos/aclchanges.proto +// source: aclchanges/pb/protos/aclchanges.proto package pb @@ -50,7 +50,7 @@ func (x ACLChangeUserPermissions) String() string { } func (ACLChangeUserPermissions) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 0} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 0} } // the element of change tree used to store and internal apply smartBlock history @@ -70,7 +70,7 @@ func (m *ACLChange) Reset() { *m = ACLChange{} } func (m *ACLChange) String() string { return proto.CompactTextString(m) } func (*ACLChange) ProtoMessage() {} func (*ACLChange) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0} + return fileDescriptor_ffc88c8445fc0f24, []int{0} } func (m *ACLChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -170,7 +170,7 @@ func (m *ACLChangeACLContentValue) Reset() { *m = ACLChangeACLContentVal func (m *ACLChangeACLContentValue) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLContentValue) ProtoMessage() {} func (*ACLChangeACLContentValue) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 0} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 0} } func (m *ACLChangeACLContentValue) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -301,7 +301,7 @@ func (m *ACLChangeACLData) Reset() { *m = ACLChangeACLData{} } func (m *ACLChangeACLData) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLData) ProtoMessage() {} func (*ACLChangeACLData) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 1} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 1} } func (m *ACLChangeACLData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -353,7 +353,7 @@ func (m *ACLChangeACLSnapshot) Reset() { *m = ACLChangeACLSnapshot{} } func (m *ACLChangeACLSnapshot) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLSnapshot) ProtoMessage() {} func (*ACLChangeACLSnapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 2} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 2} } func (m *ACLChangeACLSnapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -399,7 +399,7 @@ func (m *ACLChangeACLState) Reset() { *m = ACLChangeACLState{} } func (m *ACLChangeACLState) String() string { return proto.CompactTextString(m) } func (*ACLChangeACLState) ProtoMessage() {} func (*ACLChangeACLState) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 3} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 3} } func (m *ACLChangeACLState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -461,7 +461,7 @@ func (m *ACLChangeUserState) Reset() { *m = ACLChangeUserState{} } func (m *ACLChangeUserState) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserState) ProtoMessage() {} func (*ACLChangeUserState) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 4} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 4} } func (m *ACLChangeUserState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -537,7 +537,7 @@ func (m *ACLChangeUserAdd) Reset() { *m = ACLChangeUserAdd{} } func (m *ACLChangeUserAdd) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserAdd) ProtoMessage() {} func (*ACLChangeUserAdd) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 5} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 5} } func (m *ACLChangeUserAdd) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -604,7 +604,7 @@ func (m *ACLChangeUserConfirm) Reset() { *m = ACLChangeUserConfirm{} } func (m *ACLChangeUserConfirm) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserConfirm) ProtoMessage() {} func (*ACLChangeUserConfirm) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 6} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 6} } func (m *ACLChangeUserConfirm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -658,7 +658,7 @@ func (m *ACLChangeUserInvite) Reset() { *m = ACLChangeUserInvite{} } func (m *ACLChangeUserInvite) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserInvite) ProtoMessage() {} func (*ACLChangeUserInvite) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 7} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 7} } func (m *ACLChangeUserInvite) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -727,7 +727,7 @@ func (m *ACLChangeUserJoin) Reset() { *m = ACLChangeUserJoin{} } func (m *ACLChangeUserJoin) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserJoin) ProtoMessage() {} func (*ACLChangeUserJoin) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 8} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 8} } func (m *ACLChangeUserJoin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -800,7 +800,7 @@ func (m *ACLChangeUserRemove) Reset() { *m = ACLChangeUserRemove{} } func (m *ACLChangeUserRemove) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserRemove) ProtoMessage() {} func (*ACLChangeUserRemove) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 9} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 9} } func (m *ACLChangeUserRemove) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -853,7 +853,7 @@ func (m *ACLChangeReadKeyReplace) Reset() { *m = ACLChangeReadKeyReplace func (m *ACLChangeReadKeyReplace) String() string { return proto.CompactTextString(m) } func (*ACLChangeReadKeyReplace) ProtoMessage() {} func (*ACLChangeReadKeyReplace) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 10} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 10} } func (m *ACLChangeReadKeyReplace) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -912,7 +912,7 @@ func (m *ACLChangeUserPermissionChange) Reset() { *m = ACLChangeUserPerm func (m *ACLChangeUserPermissionChange) String() string { return proto.CompactTextString(m) } func (*ACLChangeUserPermissionChange) ProtoMessage() {} func (*ACLChangeUserPermissionChange) Descriptor() ([]byte, []int) { - return fileDescriptor_5a15109171a9dc49, []int{0, 11} + return fileDescriptor_ffc88c8445fc0f24, []int{0, 11} } func (m *ACLChangeUserPermissionChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -973,67 +973,69 @@ func init() { proto.RegisterType((*ACLChangeUserPermissionChange)(nil), "anytype.ACLChange.UserPermissionChange") } -func init() { proto.RegisterFile("data/pb/protos/aclchanges.proto", fileDescriptor_5a15109171a9dc49) } +func init() { + proto.RegisterFile("aclchanges/pb/protos/aclchanges.proto", fileDescriptor_ffc88c8445fc0f24) +} -var fileDescriptor_5a15109171a9dc49 = []byte{ - // 898 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4d, 0x6f, 0xe3, 0x44, - 0x18, 0xf6, 0x24, 0x6d, 0x1d, 0xbf, 0x0e, 0x6d, 0x18, 0xf5, 0x60, 0x79, 0x8b, 0x09, 0x15, 0x82, - 0x08, 0xa1, 0x54, 0xca, 0x22, 0xbe, 0x84, 0x04, 0x4d, 0x17, 0x48, 0x28, 0x87, 0xd5, 0xac, 0x00, - 0x81, 0xc4, 0x61, 0x6a, 0x0f, 0x5b, 0x8b, 0xc4, 0xb6, 0x3c, 0x93, 0x0a, 0x5f, 0xf8, 0x07, 0x08, - 0xfe, 0x07, 0x37, 0xfe, 0x00, 0x57, 0x8e, 0x7b, 0x40, 0x88, 0x23, 0x6a, 0xff, 0x08, 0x9a, 0x0f, - 0x3b, 0x4e, 0x6a, 0x17, 0x09, 0x55, 0x42, 0xdc, 0x66, 0x9e, 0x79, 0xde, 0xf1, 0xf3, 0x7e, 0x8e, - 0xe1, 0xc5, 0x88, 0x0a, 0x7a, 0x92, 0x5d, 0x9c, 0x64, 0x79, 0x2a, 0x52, 0x7e, 0x42, 0xc3, 0x45, - 0x78, 0x49, 0x93, 0xa7, 0x8c, 0x8f, 0x15, 0x82, 0x6d, 0x9a, 0x14, 0xa2, 0xc8, 0xd8, 0xf1, 0xcf, - 0x87, 0xe0, 0x9c, 0x9e, 0x7d, 0x7a, 0xa6, 0x4e, 0xf1, 0x10, 0x5c, 0x91, 0x33, 0x36, 0x63, 0x34, - 0x9a, 0x47, 0xdc, 0x43, 0xc3, 0xee, 0xc8, 0x21, 0x75, 0x08, 0x07, 0x00, 0x34, 0x5c, 0x94, 0x84, - 0x8e, 0x22, 0xd4, 0x10, 0xfc, 0x0a, 0xec, 0xf3, 0x84, 0x66, 0xfc, 0x32, 0x15, 0x53, 0xca, 0xd9, - 0x3c, 0xf2, 0xba, 0x43, 0x34, 0x72, 0xc8, 0x16, 0x8a, 0xdf, 0x00, 0x9b, 0x86, 0x8b, 0x47, 0x54, - 0x50, 0x6f, 0x67, 0x88, 0x46, 0xee, 0xc4, 0x1f, 0x1b, 0x49, 0xe3, 0x4a, 0x8e, 0x5c, 0x49, 0x06, - 0x29, 0xa9, 0x52, 0x9f, 0xf1, 0x43, 0x59, 0xee, 0x0e, 0xd1, 0xa8, 0x4f, 0xea, 0x10, 0x1e, 0x03, - 0x0e, 0x57, 0x79, 0xce, 0x12, 0x41, 0x18, 0x8d, 0xce, 0x59, 0x31, 0xa3, 0xfc, 0xd2, 0xdb, 0x1b, - 0xa2, 0xd1, 0x0e, 0x69, 0x38, 0xc1, 0x47, 0xe0, 0x88, 0x78, 0xc9, 0xb8, 0xa0, 0xcb, 0xcc, 0xb3, - 0x87, 0x68, 0xd4, 0x25, 0x6b, 0x00, 0xfb, 0xd0, 0x8b, 0x23, 0x96, 0x88, 0x58, 0x14, 0x5e, 0x4f, - 0xf9, 0x51, 0xed, 0xfd, 0x5f, 0xba, 0x70, 0x20, 0xa5, 0xa6, 0x89, 0x60, 0x89, 0xf8, 0x9c, 0x2e, - 0x56, 0x0c, 0xbf, 0x09, 0xf6, 0x8a, 0xb3, 0xfc, 0x34, 0x8a, 0x3c, 0xd4, 0xea, 0xd5, 0x67, 0x9a, - 0x31, 0xb3, 0x48, 0x49, 0xc6, 0xef, 0x03, 0xc8, 0x25, 0x61, 0xcb, 0xf4, 0x8a, 0x79, 0x1d, 0x65, - 0xfa, 0x42, 0x8b, 0xa9, 0x26, 0xcd, 0x2c, 0x52, 0x33, 0xc1, 0x5f, 0xc3, 0xa1, 0xdc, 0x3d, 0x66, - 0xf9, 0x32, 0xe6, 0x3c, 0x4e, 0x13, 0x6d, 0xa0, 0x82, 0xef, 0x4e, 0x5e, 0x6d, 0xb9, 0x6a, 0x9b, - 0x3e, 0xb3, 0x48, 0xe3, 0x35, 0xa5, 0xbe, 0x79, 0x72, 0x15, 0x0b, 0x66, 0x12, 0xd6, 0xa6, 0x4f, - 0x93, 0x4a, 0x7d, 0x7a, 0x87, 0xdf, 0x81, 0x9e, 0xdc, 0x7d, 0x92, 0xc6, 0x89, 0xca, 0x9a, 0x3b, - 0x79, 0xd0, 0x62, 0x2e, 0x29, 0x33, 0x8b, 0x54, 0x74, 0x3c, 0x05, 0x57, 0xae, 0xcf, 0xd2, 0xe4, - 0x9b, 0x38, 0x5f, 0xaa, 0x54, 0xba, 0x93, 0xa0, 0xc5, 0xda, 0xb0, 0x66, 0x16, 0xa9, 0x1b, 0x4d, - 0x6d, 0xd8, 0xbd, 0x92, 0x09, 0xf2, 0x7f, 0x44, 0x60, 0x9b, 0xaa, 0xc2, 0x1f, 0x80, 0x4b, 0xc3, - 0xc5, 0x13, 0x53, 0x97, 0x26, 0x61, 0x41, 0x73, 0x19, 0x96, 0x2c, 0x52, 0x37, 0xc1, 0x53, 0xd5, - 0x0c, 0xa6, 0x02, 0x54, 0x33, 0xb8, 0x93, 0xe3, 0xe6, 0x0b, 0xea, 0x65, 0x42, 0x6a, 0x56, 0xfe, - 0x47, 0xe0, 0xd6, 0xee, 0xc7, 0x6f, 0x41, 0x4f, 0x7e, 0x41, 0x50, 0xc1, 0x8c, 0xa2, 0x07, 0x2d, - 0x8a, 0x24, 0x85, 0x54, 0x64, 0xff, 0x87, 0x0e, 0xf4, 0x4a, 0x18, 0xbf, 0x0c, 0xcf, 0xe5, 0xeb, - 0x22, 0x67, 0xba, 0x93, 0x77, 0xc8, 0x26, 0x88, 0xdf, 0xd3, 0x59, 0x55, 0x26, 0xdc, 0xc8, 0x3f, - 0x6a, 0x09, 0xac, 0xfe, 0x5c, 0x8d, 0x8f, 0xa7, 0x60, 0xc7, 0x2a, 0xb9, 0xdc, 0xeb, 0x2a, 0xd3, - 0xd1, 0x1d, 0x42, 0xc7, 0xba, 0x0e, 0xf8, 0x87, 0x89, 0xc8, 0x0b, 0x52, 0x1a, 0xfa, 0x5f, 0x42, - 0xbf, 0x7e, 0x80, 0x07, 0xd0, 0xfd, 0x96, 0x15, 0xca, 0x71, 0x87, 0xc8, 0x25, 0x7e, 0x68, 0x32, - 0xf7, 0x0f, 0x4d, 0xa1, 0x6f, 0x21, 0x9a, 0xfb, 0x6e, 0xe7, 0x6d, 0xe4, 0xdf, 0x20, 0x70, 0x2a, - 0xe1, 0x1b, 0x8d, 0x8c, 0x36, 0x1b, 0x59, 0x06, 0x8b, 0x25, 0x61, 0x5e, 0x64, 0x22, 0x4e, 0x93, - 0x73, 0x56, 0xa8, 0x4f, 0xf5, 0xc9, 0x26, 0x88, 0x5f, 0x87, 0xe7, 0x0d, 0xc0, 0x22, 0x33, 0x40, - 0xb4, 0xe3, 0x7d, 0x72, 0xfb, 0x00, 0x3f, 0x02, 0x37, 0xab, 0x9a, 0x88, 0xab, 0x8e, 0xd9, 0x6f, - 0x2c, 0x8d, 0xcd, 0x36, 0xe4, 0xa4, 0x6e, 0x26, 0xc7, 0xdd, 0x9c, 0x9b, 0x1a, 0x66, 0x91, 0x6a, - 0x9c, 0x1e, 0xa9, 0x43, 0xfe, 0xaf, 0x08, 0x6c, 0x33, 0x4f, 0xfe, 0x9f, 0x3e, 0xfa, 0x1f, 0x83, - 0x5b, 0x6b, 0xdc, 0x3b, 0x9d, 0x38, 0x02, 0xc7, 0x0c, 0xcc, 0x79, 0xa4, 0x1c, 0x70, 0xc8, 0x1a, - 0xf0, 0xff, 0x40, 0x00, 0xeb, 0x52, 0xc0, 0x23, 0x38, 0xa0, 0x61, 0xc8, 0x32, 0xf1, 0x78, 0x75, - 0xb1, 0x88, 0xc3, 0x73, 0x53, 0x56, 0x7d, 0xb2, 0x0d, 0xe3, 0xd7, 0x60, 0x60, 0x9c, 0x5b, 0x53, - 0x75, 0x78, 0x6e, 0xe1, 0xff, 0x49, 0x84, 0x7e, 0x47, 0xd0, 0x2b, 0x27, 0xe3, 0x3d, 0x24, 0xb9, - 0x0a, 0xcc, 0x93, 0xf8, 0x69, 0x42, 0xc5, 0x2a, 0xd7, 0xaf, 0x44, 0x15, 0x98, 0x0a, 0x96, 0x6f, - 0xe9, 0x7a, 0x84, 0x6b, 0xa5, 0xf3, 0x48, 0x79, 0xe1, 0x90, 0x86, 0x93, 0xe6, 0xe0, 0xec, 0xb6, - 0x04, 0xc7, 0x5f, 0xe9, 0x74, 0x99, 0x07, 0xec, 0x2e, 0xbf, 0xce, 0xe1, 0xc0, 0x0c, 0x2e, 0xc2, - 0xb2, 0x05, 0x0d, 0xab, 0x89, 0xf3, 0x52, 0x43, 0x28, 0xc9, 0x06, 0x93, 0x6c, 0x5b, 0xfa, 0xdf, - 0xc3, 0xfe, 0x26, 0xe5, 0x1e, 0x42, 0xba, 0xae, 0xa0, 0xca, 0x3f, 0x13, 0xd3, 0x5b, 0xb8, 0xff, - 0x1d, 0x1c, 0x36, 0x3d, 0xbd, 0x77, 0xaa, 0xd8, 0xaa, 0xa3, 0xce, 0xbf, 0xaa, 0xa3, 0xe3, 0x53, - 0x38, 0xd8, 0x3a, 0xc7, 0x0e, 0xec, 0x9e, 0x46, 0xcb, 0x38, 0x19, 0x58, 0x18, 0x60, 0xef, 0x8b, - 0x3c, 0x16, 0x2c, 0x1f, 0x20, 0xb9, 0x96, 0x72, 0x59, 0x3e, 0xe8, 0x60, 0x17, 0x6c, 0x9d, 0xa2, - 0x68, 0xd0, 0x9d, 0x1e, 0xfd, 0x76, 0x1d, 0xa0, 0x67, 0xd7, 0x01, 0xfa, 0xeb, 0x3a, 0x40, 0x3f, - 0xdd, 0x04, 0xd6, 0xb3, 0x9b, 0xc0, 0xfa, 0xf3, 0x26, 0xb0, 0xbe, 0xea, 0x64, 0x17, 0x17, 0x7b, - 0xea, 0xdf, 0xf2, 0xe1, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x2f, 0x29, 0xef, 0x7e, 0x0a, - 0x00, 0x00, +var fileDescriptor_ffc88c8445fc0f24 = []byte{ + // 897 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0xcb, 0x6e, 0xeb, 0x44, + 0x18, 0xf6, 0x24, 0x6d, 0x1d, 0xff, 0x0e, 0x6d, 0x18, 0x75, 0x61, 0xf9, 0x14, 0x2b, 0x54, 0x5c, + 0x22, 0x84, 0x52, 0x29, 0x07, 0x71, 0x13, 0x12, 0x34, 0x3d, 0x40, 0x42, 0x59, 0x1c, 0xcd, 0x11, + 0x20, 0x90, 0x58, 0x4c, 0xed, 0xe1, 0xd4, 0x22, 0xb1, 0x2d, 0xcf, 0xa4, 0x22, 0x1b, 0xde, 0x00, + 0xc1, 0x7b, 0xb0, 0xe3, 0x05, 0xd8, 0xb2, 0x3c, 0x0b, 0x84, 0x58, 0xa2, 0xf6, 0x45, 0xd0, 0x5c, + 0x7c, 0x49, 0x6a, 0x17, 0x09, 0x55, 0x42, 0xec, 0x66, 0xbe, 0xf9, 0xfe, 0xf1, 0xf7, 0x5f, 0xc7, + 0xf0, 0x32, 0x0d, 0x17, 0xe1, 0x25, 0x4d, 0x9e, 0x32, 0x7e, 0x92, 0x5d, 0x9c, 0x64, 0x79, 0x2a, + 0x52, 0x7e, 0x52, 0x81, 0x63, 0x85, 0x60, 0x9b, 0x26, 0x6b, 0xb1, 0xce, 0xd8, 0xf1, 0xcf, 0x87, + 0xe0, 0x9c, 0x9e, 0x7d, 0x7a, 0xa6, 0x4e, 0xf1, 0x10, 0x5c, 0x91, 0x33, 0x36, 0x63, 0x34, 0x9a, + 0x47, 0xdc, 0x43, 0xc3, 0xee, 0xc8, 0x21, 0x75, 0x08, 0x07, 0x00, 0x34, 0x5c, 0x14, 0x84, 0x8e, + 0x22, 0xd4, 0x10, 0xfc, 0x0a, 0xec, 0xf3, 0x84, 0x66, 0xfc, 0x32, 0x15, 0x53, 0xca, 0xd9, 0x3c, + 0xf2, 0xba, 0x43, 0x34, 0x72, 0xc8, 0x16, 0x8a, 0xdf, 0x00, 0x9b, 0x86, 0x8b, 0x47, 0x54, 0x50, + 0x6f, 0x67, 0x88, 0x46, 0xee, 0xc4, 0x1f, 0x1b, 0x49, 0xe3, 0x52, 0x8e, 0x5c, 0x49, 0x06, 0x29, + 0xa8, 0x52, 0x9f, 0xf1, 0x43, 0x59, 0xee, 0x0e, 0xd1, 0xa8, 0x4f, 0xea, 0x10, 0x1e, 0x03, 0x0e, + 0x57, 0x79, 0xce, 0x12, 0x41, 0x18, 0x8d, 0xce, 0xd9, 0x7a, 0x46, 0xf9, 0xa5, 0xb7, 0x37, 0x44, + 0xa3, 0x1d, 0xd2, 0x70, 0x82, 0x8f, 0xc0, 0x11, 0xf1, 0x92, 0x71, 0x41, 0x97, 0x99, 0x67, 0x0f, + 0xd1, 0xa8, 0x4b, 0x2a, 0x00, 0xfb, 0xd0, 0x8b, 0x23, 0x96, 0x88, 0x58, 0xac, 0xbd, 0x9e, 0xf2, + 0xa3, 0xdc, 0xfb, 0xbf, 0x74, 0xe1, 0x40, 0x4a, 0x4d, 0x13, 0xc1, 0x12, 0xf1, 0x39, 0x5d, 0xac, + 0x18, 0x7e, 0x13, 0xec, 0x15, 0x67, 0xf9, 0x69, 0x14, 0x79, 0xa8, 0xd5, 0xab, 0xcf, 0x34, 0x63, + 0x66, 0x91, 0x82, 0x8c, 0xdf, 0x07, 0x90, 0x4b, 0xc2, 0x96, 0xe9, 0x15, 0xf3, 0x3a, 0xca, 0xf4, + 0x85, 0x16, 0x53, 0x4d, 0x9a, 0x59, 0xa4, 0x66, 0x82, 0xbf, 0x86, 0x43, 0xb9, 0x7b, 0xcc, 0xf2, + 0x65, 0xcc, 0x79, 0x9c, 0x26, 0xda, 0x40, 0x05, 0xdf, 0x9d, 0xbc, 0xda, 0x72, 0xd5, 0x36, 0x7d, + 0x66, 0x91, 0xc6, 0x6b, 0x0a, 0x7d, 0xf3, 0xe4, 0x2a, 0x16, 0xcc, 0x24, 0xac, 0x4d, 0x9f, 0x26, + 0x15, 0xfa, 0xf4, 0x0e, 0xbf, 0x03, 0x3d, 0xb9, 0xfb, 0x24, 0x8d, 0x13, 0x95, 0x35, 0x77, 0xf2, + 0xa0, 0xc5, 0x5c, 0x52, 0x66, 0x16, 0x29, 0xe9, 0x78, 0x0a, 0xae, 0x5c, 0x9f, 0xa5, 0xc9, 0x37, + 0x71, 0xbe, 0x54, 0xa9, 0x74, 0x27, 0x41, 0x8b, 0xb5, 0x61, 0xcd, 0x2c, 0x52, 0x37, 0x9a, 0xda, + 0xb0, 0x7b, 0x25, 0x13, 0xe4, 0xff, 0x88, 0xc0, 0x36, 0x55, 0x85, 0x3f, 0x00, 0x97, 0x86, 0x8b, + 0x27, 0xa6, 0x2e, 0x4d, 0xc2, 0x82, 0xe6, 0x32, 0x2c, 0x58, 0xa4, 0x6e, 0x82, 0xa7, 0xaa, 0x19, + 0x4c, 0x05, 0xa8, 0x66, 0x70, 0x27, 0xc7, 0xcd, 0x17, 0xd4, 0xcb, 0x84, 0xd4, 0xac, 0xfc, 0x8f, + 0xc0, 0xad, 0xdd, 0x8f, 0xdf, 0x82, 0x9e, 0xfc, 0x82, 0xa0, 0x82, 0x19, 0x45, 0x0f, 0x5a, 0x14, + 0x49, 0x0a, 0x29, 0xc9, 0xfe, 0x0f, 0x1d, 0xe8, 0x15, 0x30, 0x7e, 0x09, 0x9e, 0xcb, 0xab, 0x22, + 0x67, 0xba, 0x93, 0x77, 0xc8, 0x26, 0x88, 0xdf, 0xd3, 0x59, 0x55, 0x26, 0xdc, 0xc8, 0x3f, 0x6a, + 0x09, 0xac, 0xfe, 0x5c, 0x8d, 0x8f, 0xa7, 0x60, 0xc7, 0x2a, 0xb9, 0xdc, 0xeb, 0x2a, 0xd3, 0xd1, + 0x1d, 0x42, 0xc7, 0xba, 0x0e, 0xf8, 0x87, 0x89, 0xc8, 0xd7, 0xa4, 0x30, 0xf4, 0xbf, 0x84, 0x7e, + 0xfd, 0x00, 0x0f, 0xa0, 0xfb, 0x2d, 0x5b, 0x2b, 0xc7, 0x1d, 0x22, 0x97, 0xf8, 0xa1, 0xc9, 0xdc, + 0x3f, 0x34, 0x85, 0xbe, 0x85, 0x68, 0xee, 0xbb, 0x9d, 0xb7, 0x91, 0x7f, 0x83, 0xc0, 0x29, 0x85, + 0x6f, 0x34, 0x32, 0xda, 0x6c, 0x64, 0x19, 0x2c, 0x96, 0x84, 0xf9, 0x3a, 0x13, 0x71, 0x9a, 0x9c, + 0xb3, 0xb5, 0xfa, 0x54, 0x9f, 0x6c, 0x82, 0xf8, 0x75, 0x78, 0xde, 0x00, 0x2c, 0x32, 0x03, 0x44, + 0x3b, 0xde, 0x27, 0xb7, 0x0f, 0xf0, 0x23, 0x70, 0xb3, 0xb2, 0x89, 0xb8, 0xea, 0x98, 0xfd, 0xc6, + 0xd2, 0xd8, 0x6c, 0x43, 0x4e, 0xea, 0x66, 0x72, 0xdc, 0xcd, 0xb9, 0xa9, 0x61, 0x16, 0xa9, 0xc6, + 0xe9, 0x91, 0x3a, 0xe4, 0xff, 0x8a, 0xc0, 0x36, 0xf3, 0xe4, 0xff, 0xe9, 0xa3, 0xff, 0x31, 0xb8, + 0xb5, 0xc6, 0xbd, 0xd3, 0x89, 0x23, 0x70, 0xcc, 0xc0, 0x9c, 0x47, 0xca, 0x01, 0x87, 0x54, 0x80, + 0xff, 0x07, 0x02, 0xa8, 0x4a, 0x01, 0x8f, 0xe0, 0x80, 0x86, 0x21, 0xcb, 0xc4, 0xe3, 0xd5, 0xc5, + 0x22, 0x0e, 0xcf, 0x4d, 0x59, 0xf5, 0xc9, 0x36, 0x8c, 0x5f, 0x83, 0x81, 0x71, 0xae, 0xa2, 0xea, + 0xf0, 0xdc, 0xc2, 0xff, 0x93, 0x08, 0xfd, 0x8e, 0xa0, 0x57, 0x4c, 0xc6, 0x7b, 0x48, 0x72, 0x19, + 0x98, 0x27, 0xf1, 0xd3, 0x84, 0x8a, 0x55, 0xae, 0x5f, 0x89, 0x32, 0x30, 0x25, 0x2c, 0xdf, 0xd2, + 0x6a, 0x84, 0x6b, 0xa5, 0xf3, 0x48, 0x79, 0xe1, 0x90, 0x86, 0x93, 0xe6, 0xe0, 0xec, 0xb6, 0x04, + 0xc7, 0x5f, 0xe9, 0x74, 0x99, 0x07, 0xec, 0x2e, 0xbf, 0xce, 0xe1, 0xc0, 0x0c, 0x2e, 0xc2, 0xb2, + 0x05, 0x0d, 0xcb, 0x89, 0xf3, 0x62, 0x43, 0x28, 0xc9, 0x06, 0x93, 0x6c, 0x5b, 0xfa, 0xdf, 0xc3, + 0xfe, 0x26, 0xe5, 0x1e, 0x42, 0x5a, 0x55, 0x50, 0xe9, 0x9f, 0x89, 0xe9, 0x2d, 0xdc, 0xff, 0x0e, + 0x0e, 0x9b, 0x9e, 0xde, 0x3b, 0x55, 0x6c, 0xd5, 0x51, 0xe7, 0x5f, 0xd5, 0xd1, 0xf1, 0x29, 0x1c, + 0x6c, 0x9d, 0x63, 0x07, 0x76, 0x4f, 0xa3, 0x65, 0x9c, 0x0c, 0x2c, 0x0c, 0xb0, 0xf7, 0x45, 0x1e, + 0x0b, 0x96, 0x0f, 0x90, 0x5c, 0x4b, 0xb9, 0x2c, 0x1f, 0x74, 0xb0, 0x0b, 0xb6, 0x4e, 0x51, 0x34, + 0xe8, 0x4e, 0x8f, 0x7e, 0xbb, 0x0e, 0xd0, 0xb3, 0xeb, 0x00, 0xfd, 0x75, 0x1d, 0xa0, 0x9f, 0x6e, + 0x02, 0xeb, 0xd9, 0x4d, 0x60, 0xfd, 0x79, 0x13, 0x58, 0x5f, 0x75, 0xb2, 0x8b, 0x8b, 0x3d, 0xf5, + 0x6f, 0xf9, 0xf0, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x54, 0xc0, 0x2c, 0x4a, 0x84, 0x0a, 0x00, + 0x00, } func (m *ACLChange) Marshal() (dAtA []byte, err error) { diff --git a/acltree/aclstate.go b/acltree/aclstate.go index f2eaad3e..fd92cca6 100644 --- a/acltree/aclstate.go +++ b/acltree/aclstate.go @@ -27,6 +27,20 @@ type ACLState struct { identity string } +func newACLState( + identity string, + encryptionKey keys.EncryptionPrivKey, + signingPubKeyDecoder keys.SigningPubKeyDecoder) *ACLState { + return &ACLState{ + identity: identity, + encryptionKey: encryptionKey, + userReadKeys: make(map[uint64]*symmetric.Key), + userStates: make(map[string]*pb.ACLChangeUserState), + userInvites: make(map[string]*pb.ACLChangeUserInvite), + signingPubKeyDecoder: signingPubKeyDecoder, + } +} + func newACLStateFromSnapshot( snapshot *pb.ACLChangeACLSnapshot, identity string, @@ -74,6 +88,19 @@ func (st *ACLState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) erro return nil } +func (st *ACLState) makeSnapshot() *pb.ACLChangeACLSnapshot { + var userStates []*pb.ACLChangeUserState + for _, st := range st.userStates { + userStates = append(userStates, st) + } + + return &pb.ACLChangeACLSnapshot{AclState: &pb.ACLChangeACLState{ + ReadKeyHashes: nil, + UserStates: userStates, // TODO: make states and invites in same format + Invites: st.userInvites, + }} +} + 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) { diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go index 458bc85b..423bbca4 100644 --- a/acltree/changebuilder.go +++ b/acltree/changebuilder.go @@ -68,10 +68,14 @@ func (c *changeBuilder) Build() (*Change, []byte, error) { return nil, nil, err } + if c.makeSnapshot { + c.aclData.AclSnapshot = c.aclState.makeSnapshot() + } + aclChange := &pb.ACLChange{ TreeHeadIds: c.tree.Heads(), AclHeadIds: c.tree.ACLHeads(), - SnapshotBaseId: c.tree.RootId(), // TODO: add logic for ACL snapshot + SnapshotBaseId: c.tree.RootId(), AclData: c.aclData, ChangesData: encrypted, CurrentReadKeyHash: c.aclState.currentReadKeyHash, diff --git a/thread/models.go b/thread/models.go index 1e57beb7..7a10ff67 100644 --- a/thread/models.go +++ b/thread/models.go @@ -2,6 +2,7 @@ package thread import ( "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges" ) @@ -10,6 +11,7 @@ import ( type Thread interface { ID() string + Header() *pb.ThreadHeader Heads() []string Orphans() []string SetHeads(heads []string) @@ -18,6 +20,7 @@ type Thread interface { AddRawChange(change *RawChange) error AddChange(change aclchanges.Change) error + // TODO: have methods with raw changes also GetChange(ctx context.Context, recordID string) (*RawChange, error) } diff --git a/thread/pb/protos/thread.proto b/thread/pb/protos/thread.proto new file mode 100644 index 00000000..d3930e4c --- /dev/null +++ b/thread/pb/protos/thread.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package anytype; +option go_package = "pb"; + +message ThreadHeader { + string firstRecordId = 1; + bool isWorkspace = 2; +} \ No newline at end of file diff --git a/thread/pb/thread.pb.go b/thread/pb/thread.pb.go new file mode 100644 index 00000000..f6b0ef5c --- /dev/null +++ b/thread/pb/thread.pb.go @@ -0,0 +1,355 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: thread/pb/protos/thread.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type ThreadHeader struct { + FirstRecordId string `protobuf:"bytes,1,opt,name=firstRecordId,proto3" json:"firstRecordId,omitempty"` + IsWorkspace bool `protobuf:"varint,2,opt,name=isWorkspace,proto3" json:"isWorkspace,omitempty"` +} + +func (m *ThreadHeader) Reset() { *m = ThreadHeader{} } +func (m *ThreadHeader) String() string { return proto.CompactTextString(m) } +func (*ThreadHeader) ProtoMessage() {} +func (*ThreadHeader) Descriptor() ([]byte, []int) { + return fileDescriptor_b228ffbfd554b168, []int{0} +} +func (m *ThreadHeader) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ThreadHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ThreadHeader.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ThreadHeader) XXX_Merge(src proto.Message) { + xxx_messageInfo_ThreadHeader.Merge(m, src) +} +func (m *ThreadHeader) XXX_Size() int { + return m.Size() +} +func (m *ThreadHeader) XXX_DiscardUnknown() { + xxx_messageInfo_ThreadHeader.DiscardUnknown(m) +} + +var xxx_messageInfo_ThreadHeader proto.InternalMessageInfo + +func (m *ThreadHeader) GetFirstRecordId() string { + if m != nil { + return m.FirstRecordId + } + return "" +} + +func (m *ThreadHeader) GetIsWorkspace() bool { + if m != nil { + return m.IsWorkspace + } + return false +} + +func init() { + proto.RegisterType((*ThreadHeader)(nil), "anytype.ThreadHeader") +} + +func init() { proto.RegisterFile("thread/pb/protos/thread.proto", fileDescriptor_b228ffbfd554b168) } + +var fileDescriptor_b228ffbfd554b168 = []byte{ + // 156 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2d, 0xc9, 0x28, 0x4a, + 0x4d, 0x4c, 0xd1, 0x2f, 0x48, 0xd2, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x2f, 0xd6, 0x87, 0x08, 0xe8, + 0x81, 0x79, 0x42, 0xec, 0x89, 0x79, 0x95, 0x25, 0x95, 0x05, 0xa9, 0x4a, 0x61, 0x5c, 0x3c, 0x21, + 0x60, 0x09, 0x8f, 0xd4, 0xc4, 0x94, 0xd4, 0x22, 0x21, 0x15, 0x2e, 0xde, 0xb4, 0xcc, 0xa2, 0xe2, + 0x92, 0xa0, 0xd4, 0xe4, 0xfc, 0xa2, 0x14, 0xcf, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, + 0x54, 0x41, 0x21, 0x05, 0x2e, 0xee, 0xcc, 0xe2, 0xf0, 0xfc, 0xa2, 0xec, 0xe2, 0x82, 0xc4, 0xe4, + 0x54, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x8e, 0x20, 0x64, 0x21, 0x27, 0x99, 0x13, 0x8f, 0xe4, 0x18, + 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, + 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x62, 0x2a, 0x48, 0x4a, 0x62, 0x03, 0xbb, 0xc2, 0x18, 0x10, + 0x00, 0x00, 0xff, 0xff, 0x05, 0x80, 0xf4, 0xa3, 0xa6, 0x00, 0x00, 0x00, +} + +func (m *ThreadHeader) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ThreadHeader) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ThreadHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsWorkspace { + i-- + if m.IsWorkspace { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.FirstRecordId) > 0 { + i -= len(m.FirstRecordId) + copy(dAtA[i:], m.FirstRecordId) + i = encodeVarintThread(dAtA, i, uint64(len(m.FirstRecordId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintThread(dAtA []byte, offset int, v uint64) int { + offset -= sovThread(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ThreadHeader) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FirstRecordId) + if l > 0 { + n += 1 + l + sovThread(uint64(l)) + } + if m.IsWorkspace { + n += 2 + } + return n +} + +func sovThread(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozThread(x uint64) (n int) { + return sovThread(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ThreadHeader) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowThread + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ThreadHeader: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ThreadHeader: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FirstRecordId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowThread + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthThread + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthThread + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FirstRecordId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsWorkspace", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowThread + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsWorkspace = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipThread(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthThread + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipThread(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowThread + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowThread + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowThread + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthThread + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupThread + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthThread + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthThread = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowThread = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupThread = fmt.Errorf("proto: unexpected end of group") +) From 1fb82a6d7fa1b6aa2f308f48bb25aa2cf99bd3c8 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Mon, 11 Jul 2022 23:56:13 +0200 Subject: [PATCH 38/53] Update changebuilder to include CID creation --- acltree/changebuilder.go | 11 +++++++---- go.mod | 5 +++++ util/cid/cid.go | 22 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 util/cid/cid.go diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go index 423bbca4..8e2334bc 100644 --- a/acltree/changebuilder.go +++ b/acltree/changebuilder.go @@ -3,6 +3,7 @@ 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/cid" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/gogo/protobuf/proto" ) @@ -83,10 +84,6 @@ func (c *changeBuilder) Build() (*Change, []byte, error) { 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 @@ -95,6 +92,12 @@ func (c *changeBuilder) Build() (*Change, []byte, error) { if err != nil { return nil, nil, err } + id, err := cid.NewCIDFromBytes(fullMarshalledChange) + if err != nil { + return nil, nil, err + } + ch := NewChange(id, aclChange) + ch.DecryptedDocumentChange = marshalled ch.Sign = signature return ch, fullMarshalledChange, nil diff --git a/go.mod b/go.mod index 0c846236..01f7d96e 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,11 @@ require ( github.com/stretchr/testify v1.7.0 github.com/textileio/go-threads v1.0.2-0.20210304072541-d0f91da84404 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c + github.com/multiformats/go-base32 v0.0.3 + github.com/multiformats/go-multiaddr v0.3.3 + github.com/multiformats/go-multiaddr-dns v0.3.1 + github.com/multiformats/go-multibase v0.0.3 + github.com/multiformats/go-multihash v0.0.15 ) require ( diff --git a/util/cid/cid.go b/util/cid/cid.go new file mode 100644 index 00000000..437d23f9 --- /dev/null +++ b/util/cid/cid.go @@ -0,0 +1,22 @@ +package cid + +import ( + "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" +) + +func NewCIDFromBytes(data []byte) (string, error) { + hash, err := mh.Sum(data, mh.SHA2_256, -1) + if err != nil { + return "", err + } + return cid.NewCidV1(cid.DagCBOR, hash).String(), nil +} + +func VerifyCID(data []byte, id string) bool { + hash, err := mh.Sum(data, mh.SHA2_256, -1) + if err != nil { + return false + } + return cid.NewCidV1(cid.DagCBOR, hash).String() == id +} From 9c72f05fbd913c1b2a53f41c812ff4972d83d362 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 00:30:24 +0200 Subject: [PATCH 39/53] Add inmemory thread and build logic --- acltree/threadutility.go | 32 ++++++++++ thread/inmemory.go | 112 ++++++++++++++++++++++++++++++++++ thread/pb/protos/thread.proto | 2 +- thread/pb/thread.pb.go | 24 ++++---- 4 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 acltree/threadutility.go create mode 100644 thread/inmemory.go diff --git a/acltree/threadutility.go b/acltree/threadutility.go new file mode 100644 index 00000000..da35c74c --- /dev/null +++ b/acltree/threadutility.go @@ -0,0 +1,32 @@ +package acltree + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" +) + +func BuildThreadWithACL( + acc *account.AccountData, + build func(builder ChangeBuilder), + create func(change *thread.RawChange) (thread.Thread, error)) (thread.Thread, error) { + bld := newChangeBuilder() + bld.Init( + newACLState(acc.Identity, acc.EncKey, keys.NewEd25519Decoder()), + &Tree{}, + acc) + build(bld) + bld.SetMakeSnapshot(true) + + change, payload, err := bld.Build() + if err != nil { + return nil, err + } + + rawChange := &thread.RawChange{ + Payload: payload, + Signature: change.Signature(), + Id: change.CID(), + } + return create(rawChange) +} diff --git a/thread/inmemory.go b/thread/inmemory.go new file mode 100644 index 00000000..d5fa7478 --- /dev/null +++ b/thread/inmemory.go @@ -0,0 +1,112 @@ +package thread + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" + "github.com/gogo/protobuf/proto" + "sync" +) + +type inMemoryThread struct { + id string + header *pb.ThreadHeader + heads []string + orphans []string + changes map[string]*RawChange + + sync.Mutex +} + +func NewInMemoryThread(firstChange *RawChange) (Thread, error) { + header := &pb.ThreadHeader{ + FirstChangeId: firstChange.Id, + IsWorkspace: false, + } + marshalledHeader, err := proto.Marshal(header) + if err != nil { + return nil, err + } + threadId, err := cid.NewCIDFromBytes(marshalledHeader) + if err != nil { + return nil, err + } + + changes := make(map[string]*RawChange) + changes[firstChange.Id] = firstChange + + return &inMemoryThread{ + id: threadId, + header: header, + heads: []string{firstChange.Id}, + orphans: nil, + changes: changes, + Mutex: sync.Mutex{}, + }, nil +} + +func (t *inMemoryThread) ID() string { + return t.id +} + +func (t *inMemoryThread) Header() *pb.ThreadHeader { + return t.header +} + +func (t *inMemoryThread) Heads() []string { + return t.heads +} + +func (t *inMemoryThread) Orphans() []string { + return t.orphans +} + +func (t *inMemoryThread) SetHeads(heads []string) { + t.heads = t.heads[:0] + + for _, h := range heads { + t.heads = append(t.heads, h) + } +} + +func (t *inMemoryThread) RemoveOrphans(orphans ...string) { + t.orphans = slice.Difference(t.orphans, orphans) +} + +func (t *inMemoryThread) AddOrphans(orphans ...string) { + t.orphans = append(t.orphans, orphans...) +} + +func (t *inMemoryThread) AddRawChange(change *RawChange) error { + // TODO: better to do deep copy + t.changes[change.Id] = change + return nil +} + +func (t *inMemoryThread) AddChange(change aclchanges.Change) error { + signature := change.Signature() + id := change.CID() + aclChange := change.ProtoChange() + + fullMarshalledChange, err := proto.Marshal(aclChange) + if err != nil { + return err + } + rawChange := &RawChange{ + Payload: fullMarshalledChange, + Signature: signature, + Id: id, + } + t.changes[id] = rawChange + return nil +} + +func (t *inMemoryThread) GetChange(ctx context.Context, changeId string) (*RawChange, error) { + if res, exists := t.changes[changeId]; exists { + return res, nil + } + return nil, fmt.Errorf("could not get change with id: %s", changeId) +} diff --git a/thread/pb/protos/thread.proto b/thread/pb/protos/thread.proto index d3930e4c..fb2b15e4 100644 --- a/thread/pb/protos/thread.proto +++ b/thread/pb/protos/thread.proto @@ -3,6 +3,6 @@ package anytype; option go_package = "pb"; message ThreadHeader { - string firstRecordId = 1; + string firstChangeId = 1; bool isWorkspace = 2; } \ No newline at end of file diff --git a/thread/pb/thread.pb.go b/thread/pb/thread.pb.go index f6b0ef5c..e941b634 100644 --- a/thread/pb/thread.pb.go +++ b/thread/pb/thread.pb.go @@ -23,7 +23,7 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type ThreadHeader struct { - FirstRecordId string `protobuf:"bytes,1,opt,name=firstRecordId,proto3" json:"firstRecordId,omitempty"` + FirstChangeId string `protobuf:"bytes,1,opt,name=firstChangeId,proto3" json:"firstChangeId,omitempty"` IsWorkspace bool `protobuf:"varint,2,opt,name=isWorkspace,proto3" json:"isWorkspace,omitempty"` } @@ -60,9 +60,9 @@ func (m *ThreadHeader) XXX_DiscardUnknown() { var xxx_messageInfo_ThreadHeader proto.InternalMessageInfo -func (m *ThreadHeader) GetFirstRecordId() string { +func (m *ThreadHeader) GetFirstChangeId() string { if m != nil { - return m.FirstRecordId + return m.FirstChangeId } return "" } @@ -86,12 +86,12 @@ var fileDescriptor_b228ffbfd554b168 = []byte{ 0x4d, 0x4c, 0xd1, 0x2f, 0x48, 0xd2, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x2f, 0xd6, 0x87, 0x08, 0xe8, 0x81, 0x79, 0x42, 0xec, 0x89, 0x79, 0x95, 0x25, 0x95, 0x05, 0xa9, 0x4a, 0x61, 0x5c, 0x3c, 0x21, 0x60, 0x09, 0x8f, 0xd4, 0xc4, 0x94, 0xd4, 0x22, 0x21, 0x15, 0x2e, 0xde, 0xb4, 0xcc, 0xa2, 0xe2, - 0x92, 0xa0, 0xd4, 0xe4, 0xfc, 0xa2, 0x14, 0xcf, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, + 0x12, 0xe7, 0x8c, 0xc4, 0xbc, 0xf4, 0x54, 0xcf, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x54, 0x41, 0x21, 0x05, 0x2e, 0xee, 0xcc, 0xe2, 0xf0, 0xfc, 0xa2, 0xec, 0xe2, 0x82, 0xc4, 0xe4, 0x54, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x8e, 0x20, 0x64, 0x21, 0x27, 0x99, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x62, 0x2a, 0x48, 0x4a, 0x62, 0x03, 0xbb, 0xc2, 0x18, 0x10, - 0x00, 0x00, 0xff, 0xff, 0x05, 0x80, 0xf4, 0xa3, 0xa6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x2a, 0xae, 0x9d, 0xc2, 0xa6, 0x00, 0x00, 0x00, } func (m *ThreadHeader) Marshal() (dAtA []byte, err error) { @@ -124,10 +124,10 @@ func (m *ThreadHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x10 } - if len(m.FirstRecordId) > 0 { - i -= len(m.FirstRecordId) - copy(dAtA[i:], m.FirstRecordId) - i = encodeVarintThread(dAtA, i, uint64(len(m.FirstRecordId))) + if len(m.FirstChangeId) > 0 { + i -= len(m.FirstChangeId) + copy(dAtA[i:], m.FirstChangeId) + i = encodeVarintThread(dAtA, i, uint64(len(m.FirstChangeId))) i-- dAtA[i] = 0xa } @@ -151,7 +151,7 @@ func (m *ThreadHeader) Size() (n int) { } var l int _ = l - l = len(m.FirstRecordId) + l = len(m.FirstChangeId) if l > 0 { n += 1 + l + sovThread(uint64(l)) } @@ -198,7 +198,7 @@ func (m *ThreadHeader) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FirstRecordId", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field FirstChangeId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -226,7 +226,7 @@ func (m *ThreadHeader) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.FirstRecordId = string(dAtA[iNdEx:postIndex]) + m.FirstChangeId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { From 4502ca7bf471c04fde7ced58f57f6204a95c01ab Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 08:45:13 +0200 Subject: [PATCH 40/53] WIP plain text document --- plaintextdocument/document.go | 375 ++++-------------- plaintextdocument/document_test.go | 101 ++--- plaintextdocument/documentcontext.go | 10 - plaintextdocument/documentstatebuilder.go | 87 ---- .../documentstatebuilder_test.go | 48 --- 5 files changed, 128 insertions(+), 493 deletions(-) delete mode 100644 plaintextdocument/documentcontext.go delete mode 100644 plaintextdocument/documentstatebuilder.go delete mode 100644 plaintextdocument/documentstatebuilder_test.go diff --git a/plaintextdocument/document.go b/plaintextdocument/document.go index 3908cb29..3dc693a3 100644 --- a/plaintextdocument/document.go +++ b/plaintextdocument/document.go @@ -4,16 +4,40 @@ import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/gogo/protobuf/proto" ) -type PlainTextDocument struct { +type PlainTextDocument interface { + Text() string + AddText(text string) error +} + +type plainTextDocument struct { heads []string aclTree acltree.ACLTree state *DocumentState } -func (p *PlainTextDocument) Update(tree acltree.ACLTree) { +func (p *plainTextDocument) Text() string { + return p.state.Text +} + +func (p *plainTextDocument) AddText(text string) error { + _, err := p.aclTree.AddContent(func(builder acltree.ChangeBuilder) { + builder.AddChangeContent( + &pb.PlainTextChangeData{ + Content: []*pb.PlainTextChangeContent{ + createAppendTextChangeContent(text), + }, + }) + }) + return err +} + +func (p *plainTextDocument) Update(tree acltree.ACLTree) { + p.aclTree = tree var err error defer func() { if err != nil { @@ -38,7 +62,8 @@ func (p *PlainTextDocument) Update(tree acltree.ACLTree) { }) } -func (p *PlainTextDocument) Rebuild(tree acltree.ACLTree) { +func (p *plainTextDocument) Rebuild(tree acltree.ACLTree) { + p.aclTree = tree p.heads = tree.Heads() var startId string var err error @@ -79,300 +104,54 @@ func (p *PlainTextDocument) Rebuild(tree acltree.ACLTree) { p.state = state } -func NewPlainTextDocument(t thread.Thread, acc *account.AccountData) (*PlainTextDocument, error) { - tree, e +func NewInMemoryPlainTextDocument(acc *account.AccountData, text string) (PlainTextDocument, error) { + return NewPlainTextDocument(acc, thread.NewInMemoryThread, text) } -// -//type AccountData struct { -// Identity string -// SignKey threadmodels.SigningPrivKey -// EncKey threadmodels.EncryptionPrivKey -//} -// -//type Document struct { -// // TODO: ensure that every operation on Document is synchronized -// thread threadmodels.Thread -// stateProvider InitialStateProvider -// accountData *AccountData -// decoder threadmodels.SigningPubKeyDecoder -// -// treeBuilder *acltree.treeBuilder -// aclTreeBuilder *acltree.aclTreeBuilder -// aclStateBuilder *acltree.aclStateBuilder -// snapshotValidator *acltree.snapshotValidator -// docStateBuilder *acltree.documentStateBuilder -// -// docContext *acltree.documentContext -//} -// -//type UpdateResult int -// -//const ( -// UpdateResultNoAction UpdateResult = iota -// UpdateResultAppend -// UpdateResultRebuild -//) -// -//type CreateChangePayload struct { -// ChangesData proto.Marshaler -// ACLData *pb.ACLChangeACLData -// Id string // TODO: this is just for testing, because id should be created automatically from content -//} -// -//func NewDocument( -// thread threadmodels.Thread, -// stateProvider InitialStateProvider, -// accountData *AccountData) *Document { -// -// decoder := threadmodels.NewEd25519Decoder() -// return &Document{ -// thread: thread, -// 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), -// docStateBuilder: acltree.newDocumentStateBuilder(stateProvider), -// docContext: &acltree.documentContext{}, -// } -//} -// -////sync layer -> Object cache -> document -> Update (..raw changes) -////client layer -> Object cache -> document -> CreateChange(...) -//// -// -//// smartblock -> CreateChange(payload) -//// SmartTree iterate etc -//func (d *Document) CreateChange(payload *CreateChangePayload) error { -// // TODO: add snapshot creation logic -// marshalled, err := payload.ChangesData.Marshal() -// if err != nil { -// return err -// } -// -// encrypted, err := d.docContext.aclState.userReadKeys[d.docContext.aclState.currentReadKeyHash]. -// Encrypt(marshalled) -// if err != nil { -// return err -// } -// -// aclChange := &pb.ACLChange{ -// TreeHeadIds: d.docContext.fullTree.Heads(), -// AclHeadIds: d.getACLHeads(), -// SnapshotBaseId: d.docContext.fullTree.RootId(), -// AclData: payload.ACLData, -// ChangesData: encrypted, -// CurrentReadKeyHash: d.docContext.aclState.currentReadKeyHash, -// Timestamp: 0, -// Identity: d.accountData.Identity, -// } -// -// // TODO: add CID creation logic based on content -// ch := acltree.NewChange(payload.Id, aclChange) -// ch.DecryptedDocumentChange = marshalled -// -// fullMarshalledChange, err := proto.Marshal(aclChange) -// if err != nil { -// return err -// } -// signature, err := d.accountData.SignKey.Sign(fullMarshalledChange) -// if err != nil { -// return err -// } -// -// if aclChange.AclData != nil { -// // we can apply change right away without going through builder, because -// err = d.docContext.aclState.ApplyChange(payload.Id, aclChange) -// if err != nil { -// return err -// } -// } -// d.docContext.fullTree.AddFast(ch) -// -// err = d.thread.AddChange(&thread.RawChange{ -// Payload: marshalled, -// Signature: signature, -// Id: payload.Id, -// }) -// if err != nil { -// return err -// } -// -// d.thread.SetHeads([]string{ch.Id}) -// d.thread.SetMaybeHeads([]string{ch.Id}) -// -// return nil -//} -// -//func (d *Document) Update(changes ...*thread.RawChange) (DocumentState, UpdateResult, error) { -// var treeChanges []*acltree.Change -// -// var foundACLChange bool -// for _, ch := range changes { -// aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch) -// if err != nil { -// return nil, UpdateResultNoAction, fmt.Errorf("change with id %s is incorrect: %w", ch.Id, err) -// } -// -// treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) -// treeChanges = append(treeChanges, treeChange) -// -// // this already sets PossibleHeads to include new changes -// // TODO: change this behaviour as non-obvious, because it is not evident from the interface -// err = d.thread.AddChange(ch) -// if err != nil { -// return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) -// } -// if treeChange.IsACLChange() { -// foundACLChange = true -// } -// } -// -// if foundACLChange { -// res, err := d.Build() -// return res, UpdateResultRebuild, err -// } -// -// prevHeads := d.docContext.fullTree.Heads() -// mode := d.docContext.fullTree.Add(treeChanges...) -// switch mode { -// case acltree.Nothing: -// return d.docContext.docState, UpdateResultNoAction, nil -// case acltree.Rebuild: -// res, err := d.Build() -// return res, UpdateResultRebuild, err -// default: -// break -// } -// -// // TODO: we should still check if the user making those changes are able to write using "aclState" -// // decrypting everything, because we have no new keys -// for _, ch := range treeChanges { -// if ch.Content.GetChangesData() != nil { -// key, exists := d.docContext.aclState.userReadKeys[ch.Content.CurrentReadKeyHash] -// if !exists { -// err := fmt.Errorf("failed to find key with hash: %d", ch.Content.CurrentReadKeyHash) -// return nil, UpdateResultNoAction, err -// } -// -// err := ch.DecryptContents(key) -// if err != nil { -// err = fmt.Errorf("failed to decrypt contents for hash: %d", ch.Content.CurrentReadKeyHash) -// return nil, UpdateResultNoAction, err -// } -// } -// } -// -// // because for every new change we know it was after any of the previous heads -// // each of previous heads must have same "Next" nodes -// // so it doesn't matter which one we choose -// // so we choose first one -// newState, err := d.docStateBuilder.appendFrom(prevHeads[0], d.docContext.docState) -// if err != nil { -// res, _ := d.Build() -// return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") -// } -// -// // setting all heads -// d.thread.SetHeads(d.docContext.fullTree.Heads()) -// // this should be the entrypoint when we build the document -// d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) -// -// return newState, UpdateResultAppend, nil -//} -// -//func (d *Document) Build() (DocumentState, error) { -// return d.build(false) -//} -// -//// TODO: this should not be the responsibility of Document, move it somewhere else after testing -//func (d *Document) getACLHeads() []string { -// var aclTreeHeads []string -// for _, head := range d.docContext.fullTree.Heads() { -// if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads -// continue -// } -// precedingHeads := d.getPrecedingACLHeads(head) -// -// for _, aclHead := range precedingHeads { -// if slice.FindPos(aclTreeHeads, aclHead) != -1 { -// continue -// } -// aclTreeHeads = append(aclTreeHeads, aclHead) -// } -// } -// return aclTreeHeads -//} -// -//func (d *Document) getPrecedingACLHeads(head string) []string { -// headChange := d.docContext.fullTree.attached[head] -// -// if headChange.Content.GetAclData() != nil { -// return []string{head} -// } else { -// return headChange.Content.AclHeadIds -// } -//} -// -//func (d *Document) build(fromStart bool) (DocumentState, error) { -// d.treeBuilder.init() -// d.aclTreeBuilder.init() -// -// var err error -// 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() -// if err != nil { -// return nil, err -// } -// -// if !fromStart { -// err = d.snapshotValidator.init(d.docContext.aclTree) -// if err != nil { -// return nil, err -// } -// -// valid, err := d.snapshotValidator.validateSnapshot(d.docContext.fullTree.root) -// if err != nil { -// return nil, err -// } -// if !valid { -// return d.build(true) -// } -// } -// err = d.aclStateBuilder.init(d.docContext.fullTree) -// if err != nil { -// return nil, err -// } -// -// d.docContext.aclState, err = d.aclStateBuilder.build() -// if err != nil { -// return nil, err -// } -// -// // tree should be exposed -// -// d.docStateBuilder.init(d.docContext.aclState, d.docContext.fullTree) -// d.docContext.docState, err = d.docStateBuilder.build() -// if err != nil { -// return nil, err -// } -// -// // setting all heads -// d.thread.SetHeads(d.docContext.fullTree.Heads()) -// // this should be the entrypoint when we build the document -// d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) -// -// return d.docContext.docState, nil -//} -// -//func (d *Document) State() DocumentState { -// return nil -//} +func NewPlainTextDocument( + acc *account.AccountData, + create func(change *thread.RawChange) (thread.Thread, error), + text string) (PlainTextDocument, error) { + changeBuilder := func(builder acltree.ChangeBuilder) { + builder.UserAdd(acc.Identity, acc.EncKey.GetPublic()) + builder.AddChangeContent(createInitialChangeContent(text)) + } + t, err := acltree.BuildThreadWithACL( + acc, + changeBuilder, + create) + if err != nil { + return nil, err + } + + doc := &plainTextDocument{ + heads: nil, + aclTree: nil, + state: nil, + } + tree, err := acltree.BuildACLTree(t, acc, doc) + if err != nil { + return nil, err + } + doc.aclTree = tree + return doc, nil +} + +func createInitialChangeContent(text string) proto.Marshaler { + return &pb.PlainTextChangeData{ + Content: []*pb.PlainTextChangeContent{ + createAppendTextChangeContent(text), + }, + Snapshot: &pb.PlainTextChangeSnapshot{Text: text}, + } +} + +func createAppendTextChangeContent(text string) *pb.PlainTextChangeContent { + return &pb.PlainTextChangeContent{ + Value: &pb.PlainTextChangeContentValueOfTextAppend{ + TextAppend: &pb.PlainTextChangeTextAppend{ + Text: text, + }, + }, + } +} diff --git a/plaintextdocument/document_test.go b/plaintextdocument/document_test.go index a2b6691f..b90f1f9c 100644 --- a/plaintextdocument/document_test.go +++ b/plaintextdocument/document_test.go @@ -1,52 +1,53 @@ package plaintextdocument -import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestDocument_Build(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - accountData := &AccountData{ - Identity: keychain.GetIdentity("A"), - EncKey: keychain.EncryptionKeys["A"], - } - doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) - res, err := doc.Build() - if err != nil { - t.Fatal(err) - } - - st := res.(*DocumentState) - assert.Equal(t, st.Text, "some text|first") -} - -func TestDocument_Update(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - accountData := &AccountData{ - Identity: keychain.GetIdentity("A"), - EncKey: keychain.EncryptionKeys["A"], - } - doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) - res, err := doc.Build() - if err != nil { - t.Fatal(err) - } - - st := res.(*DocumentState) - assert.Equal(t, st.Text, "some text|first") - - rawChs := thread.GetUpdatedChanges() - res, updateResult, err := doc.Update(rawChs...) - assert.Equal(t, updateResult, UpdateResultAppend) - assert.Equal(t, res.(*DocumentState).Text, "some text|first|second") -} +// +//import ( +// "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" +// "github.com/stretchr/testify/assert" +// "testing" +//) +// +//func TestDocument_Build(t *testing.T) { +// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// if err != nil { +// t.Fatal(err) +// } +// keychain := thread.GetKeychain() +// accountData := &AccountData{ +// Identity: keychain.GetIdentity("A"), +// EncKey: keychain.EncryptionKeys["A"], +// } +// doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) +// res, err := doc.Build() +// if err != nil { +// t.Fatal(err) +// } +// +// st := res.(*DocumentState) +// assert.Equal(t, st.Text, "some text|first") +//} +// +//func TestDocument_Update(t *testing.T) { +// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// if err != nil { +// t.Fatal(err) +// } +// keychain := thread.GetKeychain() +// accountData := &AccountData{ +// Identity: keychain.GetIdentity("A"), +// EncKey: keychain.EncryptionKeys["A"], +// } +// doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) +// res, err := doc.Build() +// if err != nil { +// t.Fatal(err) +// } +// +// st := res.(*DocumentState) +// assert.Equal(t, st.Text, "some text|first") +// +// rawChs := thread.GetUpdatedChanges() +// res, updateResult, err := doc.Update(rawChs...) +// assert.Equal(t, updateResult, UpdateResultAppend) +// assert.Equal(t, res.(*DocumentState).Text, "some text|first|second") +//} diff --git a/plaintextdocument/documentcontext.go b/plaintextdocument/documentcontext.go deleted file mode 100644 index 2aae6a97..00000000 --- a/plaintextdocument/documentcontext.go +++ /dev/null @@ -1,10 +0,0 @@ -package plaintextdocument - -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 - aclState *acltree.aclState - docState DocumentState -} diff --git a/plaintextdocument/documentstatebuilder.go b/plaintextdocument/documentstatebuilder.go deleted file mode 100644 index ab23274e..00000000 --- a/plaintextdocument/documentstatebuilder.go +++ /dev/null @@ -1,87 +0,0 @@ -package plaintextdocument - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" -) - -// example -> - -type documentStateBuilder struct { - tree *acltree.tree - aclState *acltree.aclState // TODO: decide if this is needed or not - stateProvider InitialStateProvider -} - -func newDocumentStateBuilder(stateProvider InitialStateProvider) *documentStateBuilder { - return &documentStateBuilder{ - stateProvider: stateProvider, - } -} - -func (d *documentStateBuilder) init(aclState *acltree.aclState, tree *acltree.tree) { - d.tree = tree - d.aclState = aclState -} - -// TODO: we should probably merge the two builders into one -func (d *documentStateBuilder) build() (s DocumentState, err error) { - var ( - startId string - count int - ) - rootChange := d.tree.Root() - - if rootChange.DecryptedDocumentChange == nil { - err = fmt.Errorf("root doesn't have decrypted change") - return - } - - s, err = d.stateProvider.ProvideFromInitialChange(rootChange.DecryptedDocumentChange, rootChange.Id) - if err != nil { - return - } - - t := d.tree - startId = rootChange.Id - - t.Iterate(startId, func(c *acltree.Change) (isContinue bool) { - count++ - if startId == c.Id { - return true - } - if c.DecryptedDocumentChange != nil { - s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id) - if err != nil { - return false - } - } - return true - }) - if err != nil { - return - } - return s, err -} - -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 - d.tree.Iterate(fromId, func(c *acltree.Change) (isContinue bool) { - if c.Id == fromId { - return true - } - if c.DecryptedDocumentChange != nil { - s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id) - if err != nil { - return false - } - } - return true - }) - if err != nil { - return - } - return s, err -} diff --git a/plaintextdocument/documentstatebuilder_test.go b/plaintextdocument/documentstatebuilder_test.go deleted file mode 100644 index b293d212..00000000 --- a/plaintextdocument/documentstatebuilder_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package plaintextdocument - -import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestDocumentStateBuilder_UserJoinBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := acltree.createDocumentStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - NewPlainTextDocumentStateProvider(), - threadmodels.NewEd25519Decoder()) - if err != nil { - t.Fatalf("should build acl aclState without err: %v", err) - } - - st := ctx.DocState.(*DocumentState) - assert.Equal(t, st.Text, "some text|first") -} - -func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := acltree.createDocumentStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - NewPlainTextDocumentStateProvider(), - threadmodels.NewEd25519Decoder()) - if err != nil { - t.Fatalf("should build acl aclState without err: %v", err) - } - - st := ctx.DocState.(*DocumentState) - assert.Equal(t, st.Text, "some text|first") -} From c242ef49a6305531610c2d98f398263475dc9667 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 08:55:30 +0200 Subject: [PATCH 41/53] WIP plaintextdocument --- acltree/changebuilder.go | 2 +- plaintextdocument/document.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go index 8e2334bc..a23985bd 100644 --- a/acltree/changebuilder.go +++ b/acltree/changebuilder.go @@ -11,7 +11,7 @@ import ( type MarshalledChange = []byte type ACLChangeBuilder interface { - UserAdd(identity string, encryptionKey keys.EncryptionPubKey) + UserAdd(identity string, encryptionKey keys.EncryptionPubKey, permissions pb.ACLChangeUserPermissions) AddId(id string) // TODO: this is only for testing SetMakeSnapshot(bool) // TODO: who should decide this? probably ACLTree so we can delete it } diff --git a/plaintextdocument/document.go b/plaintextdocument/document.go index 3dc693a3..2b030d6c 100644 --- a/plaintextdocument/document.go +++ b/plaintextdocument/document.go @@ -2,7 +2,9 @@ package plaintextdocument import ( "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + aclpb "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree" "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" @@ -113,7 +115,7 @@ func NewPlainTextDocument( create func(change *thread.RawChange) (thread.Thread, error), text string) (PlainTextDocument, error) { changeBuilder := func(builder acltree.ChangeBuilder) { - builder.UserAdd(acc.Identity, acc.EncKey.GetPublic()) + builder.UserAdd(acc.Identity, acc.EncKey.GetPublic(), aclpb.ACLChange_Admin) builder.AddChangeContent(createInitialChangeContent(text)) } t, err := acltree.BuildThreadWithACL( From 04c55cd6020377fe66419af942bc6aa1f902e0f1 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 09:01:06 +0200 Subject: [PATCH 42/53] Add synchronization to inmemory store --- acltree/changebuilder.go | 2 +- thread/inmemory.go | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go index a23985bd..294d4c6d 100644 --- a/acltree/changebuilder.go +++ b/acltree/changebuilder.go @@ -52,7 +52,7 @@ func (c *changeBuilder) SetMakeSnapshot(b bool) { c.makeSnapshot = b } -func (c *changeBuilder) UserAdd(identity string, encryptionKey keys.EncryptionPubKey) { +func (c *changeBuilder) UserAdd(identity string, encryptionKey keys.EncryptionPubKey, permissions pb.ACLChangeUserPermissions) { //TODO implement me panic("implement me") } diff --git a/thread/inmemory.go b/thread/inmemory.go index d5fa7478..484428ee 100644 --- a/thread/inmemory.go +++ b/thread/inmemory.go @@ -18,7 +18,7 @@ type inMemoryThread struct { orphans []string changes map[string]*RawChange - sync.Mutex + sync.RWMutex } func NewInMemoryThread(firstChange *RawChange) (Thread, error) { @@ -44,27 +44,37 @@ func NewInMemoryThread(firstChange *RawChange) (Thread, error) { heads: []string{firstChange.Id}, orphans: nil, changes: changes, - Mutex: sync.Mutex{}, + RWMutex: sync.RWMutex{}, }, nil } func (t *inMemoryThread) ID() string { + t.RLock() + defer t.RUnlock() return t.id } func (t *inMemoryThread) Header() *pb.ThreadHeader { + t.RLock() + defer t.RUnlock() return t.header } func (t *inMemoryThread) Heads() []string { + t.RLock() + defer t.RUnlock() return t.heads } func (t *inMemoryThread) Orphans() []string { + t.RLock() + defer t.RUnlock() return t.orphans } func (t *inMemoryThread) SetHeads(heads []string) { + t.Lock() + defer t.Unlock() t.heads = t.heads[:0] for _, h := range heads { @@ -73,20 +83,28 @@ func (t *inMemoryThread) SetHeads(heads []string) { } func (t *inMemoryThread) RemoveOrphans(orphans ...string) { + t.Lock() + defer t.Unlock() t.orphans = slice.Difference(t.orphans, orphans) } func (t *inMemoryThread) AddOrphans(orphans ...string) { + t.Lock() + defer t.Unlock() t.orphans = append(t.orphans, orphans...) } func (t *inMemoryThread) AddRawChange(change *RawChange) error { + t.Lock() + defer t.Unlock() // TODO: better to do deep copy t.changes[change.Id] = change return nil } func (t *inMemoryThread) AddChange(change aclchanges.Change) error { + t.Lock() + defer t.Unlock() signature := change.Signature() id := change.CID() aclChange := change.ProtoChange() @@ -105,6 +123,8 @@ func (t *inMemoryThread) AddChange(change aclchanges.Change) error { } func (t *inMemoryThread) GetChange(ctx context.Context, changeId string) (*RawChange, error) { + t.RLock() + defer t.RUnlock() if res, exists := t.changes[changeId]; exists { return res, nil } From 4b6ec6f3106da6c53bb0c2521f5dfb283e1ac0dc Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 10:27:02 +0200 Subject: [PATCH 43/53] Change first thread entry handling --- acltree/acltree_test.go | 21 ++++++--- acltree/acltreebuilder.go | 38 ++++------------ acltree/treebuilder.go | 44 +++++-------------- .../threadbuilder/invalidsnapshotexample.yml | 3 ++ testutils/threadbuilder/threadbuilder.go | 13 ++++++ testutils/threadbuilder/userjoinexample.yml | 3 ++ .../threadbuilder/userjoinexampleupdate.yml | 3 ++ .../threadbuilder/userremovebeforeexample.yml | 3 ++ testutils/threadbuilder/userremoveexample.yml | 3 ++ .../threadbuilder/validsnapshotexample.yml | 3 ++ testutils/threadbuilder/ymlentities.go | 6 +++ 11 files changed, 72 insertions(+), 68 deletions(-) diff --git a/acltree/acltree_test.go b/acltree/acltree_test.go index ebf3e4c2..8f5665ac 100644 --- a/acltree/acltree_test.go +++ b/acltree/acltree_test.go @@ -10,6 +10,12 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" ) +type mockListener struct{} + +func (m *mockListener) Update(tree ACLTree) {} + +func (m *mockListener) Rebuild(tree ACLTree) {} + func TestACLTree_UserJoinBuild(t *testing.T) { thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") if err != nil { @@ -21,7 +27,8 @@ func TestACLTree_UserJoinBuild(t *testing.T) { SignKey: keychain.SigningKeys["A"], EncKey: keychain.EncryptionKeys["A"], } - tree, err := BuildACLTree(thr, accountData) + listener := &mockListener{} + tree, err := BuildACLTree(thr, accountData, listener) if err != nil { t.Fatalf("should Build acl ACLState without err: %v", err) } @@ -55,7 +62,8 @@ func TestACLTree_UserRemoveBuild(t *testing.T) { SignKey: keychain.SigningKeys["A"], EncKey: keychain.EncryptionKeys["A"], } - tree, err := BuildACLTree(thr, accountData) + listener := &mockListener{} + tree, err := BuildACLTree(thr, accountData, listener) if err != nil { t.Fatalf("should Build acl ACLState without err: %v", err) } @@ -85,7 +93,8 @@ func TestACLTree_UserRemoveBeforeBuild(t *testing.T) { SignKey: keychain.SigningKeys["A"], EncKey: keychain.EncryptionKeys["A"], } - tree, err := BuildACLTree(thr, accountData) + listener := &mockListener{} + tree, err := BuildACLTree(thr, accountData, listener) if err != nil { t.Fatalf("should Build acl ACLState without err: %v", err) } @@ -116,7 +125,8 @@ func TestACLTree_InvalidSnapshotBuild(t *testing.T) { SignKey: keychain.SigningKeys["A"], EncKey: keychain.EncryptionKeys["A"], } - tree, err := BuildACLTree(thr, accountData) + listener := &mockListener{} + tree, err := BuildACLTree(thr, accountData, listener) if err != nil { t.Fatalf("should Build acl ACLState without err: %v", err) } @@ -146,7 +156,8 @@ func TestACLTree_ValidSnapshotBuild(t *testing.T) { SignKey: keychain.SigningKeys["A"], EncKey: keychain.EncryptionKeys["A"], } - tree, err := BuildACLTree(thr, accountData) + listener := &mockListener{} + tree, err := BuildACLTree(thr, accountData, listener) if err != nil { t.Fatalf("should Build acl ACLState without err: %v", err) } diff --git a/acltree/acltreebuilder.go b/acltree/acltreebuilder.go index 7c552407..dfb855fe 100644 --- a/acltree/acltreebuilder.go +++ b/acltree/acltreebuilder.go @@ -6,7 +6,6 @@ import ( "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/slice" - gothread "github.com/textileio/go-threads/core/thread" ) type aclTreeBuilder struct { @@ -56,11 +55,7 @@ func (tb *aclTreeBuilder) Build() (*Tree, 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") - } - root, err := tb.getRoot(possibleRoots) + changes, root, err := tb.dfsFromStart(heads) if err != nil { return err } @@ -70,7 +65,8 @@ 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, root *Change, err error) { + var possibleRoots []*Change stack := make([]string, len(heads), len(heads)*2) copy(stack, heads) @@ -98,32 +94,14 @@ func (tb *aclTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleR possibleRoots = append(possibleRoots, ch) } } - return buf, possibleRoots, nil -} - -func (tb *aclTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { - threadId, err := gothread.Decode(tb.thread.ID()) - if err != nil { - return nil, err - } - + header := tb.thread.Header() for _, r := range possibleRoots { - id := r.Content.Identity - sk, err := tb.signingPubKeyDecoder.DecodeFromString(id) - if err != nil { - continue - } - - res, err := thread.VerifyACLThreadID(sk, threadId) - if err != nil { - continue - } - - if res { - return r, nil + if r.Id == header.FirstChangeId { + return buf, r, nil } } - return nil, fmt.Errorf("could not find any root") + + return nil, nil, fmt.Errorf("could not find root change") } func (tb *aclTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, err error) { diff --git a/acltree/treebuilder.go b/acltree/treebuilder.go index 0e3bc79d..944a44e1 100644 --- a/acltree/treebuilder.go +++ b/acltree/treebuilder.go @@ -9,7 +9,6 @@ import ( //"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/lib/logging" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "github.com/prometheus/common/log" - gothread "github.com/textileio/go-threads/core/thread" ) var ( @@ -71,11 +70,7 @@ func (tb *treeBuilder) Build(fromStart bool) (*Tree, 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") - } - root, err := tb.getRoot(possibleRoots) + changes, root, err := tb.dfsFromStart(heads) if err != nil { return err } @@ -85,7 +80,8 @@ 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, root *Change, err error) { + var possibleRoots []*Change stack := make([]string, len(heads), len(heads)*2) copy(stack, heads) @@ -113,7 +109,14 @@ func (tb *treeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoot possibleRoots = append(possibleRoots, ch) } } - return buf, possibleRoots, nil + header := tb.thread.Header() + for _, r := range possibleRoots { + if r.Id == header.FirstChangeId { + return buf, r, nil + } + } + + return nil, nil, fmt.Errorf("could not find root change") } func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error) { @@ -287,28 +290,3 @@ func (tb *treeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e return s2, nil } - -func (tb *treeBuilder) getRoot(possibleRoots []*Change) (*Change, error) { - threadId, err := gothread.Decode(tb.thread.ID()) - if err != nil { - return nil, err - } - - for _, r := range possibleRoots { - id := r.Content.Identity - sk, err := tb.signingPubKeyDecoder.DecodeFromString(id) - if err != nil { - continue - } - - res, err := thread.VerifyACLThreadID(sk, threadId) - if err != nil { - continue - } - - if res { - return r, nil - } - } - return nil, fmt.Errorf("could not find any root") -} diff --git a/testutils/threadbuilder/invalidsnapshotexample.yml b/testutils/threadbuilder/invalidsnapshotexample.yml index e3877b47..e592f7d6 100644 --- a/testutils/threadbuilder/invalidsnapshotexample.yml +++ b/testutils/threadbuilder/invalidsnapshotexample.yml @@ -117,6 +117,9 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] +header: + firstChangeId: A.1.1 + isWorkspace: false orphans: - A.1.3 - B.1.2 diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 72784452..9cfd25f8 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -13,6 +13,7 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" testpb "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + threadpb "github.com/anytypeio/go-anytype-infrastructure-experiments/thread/pb" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" ) @@ -34,6 +35,7 @@ type ThreadBuilder struct { heads []string orphans []string keychain *Keychain + header *threadpb.ThreadHeader } func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { @@ -167,6 +169,10 @@ func (t *ThreadBuilder) GetUpdatedChanges() []*thread.RawChange { return res } +func (t *ThreadBuilder) Header() *threadpb.ThreadHeader { + return t.header +} + func (t *ThreadBuilder) getChange(changeId string, m map[string]*threadChange) *thread.RawChange { rec := m[changeId] @@ -510,3 +516,10 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) { func (t *ThreadBuilder) parseOrphans(thread *YMLThread) { t.orphans = thread.Orphans } + +func (t *ThreadBuilder) parseHeader(thread *YMLThread) { + t.header = &threadpb.ThreadHeader{ + FirstChangeId: thread.Header.FirstChangeId, + IsWorkspace: thread.Header.IsWorkspace, + } +} diff --git a/testutils/threadbuilder/userjoinexample.yml b/testutils/threadbuilder/userjoinexample.yml index c2a0b11b..88f9ed92 100644 --- a/testutils/threadbuilder/userjoinexample.yml +++ b/testutils/threadbuilder/userjoinexample.yml @@ -99,6 +99,9 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] +header: + firstChangeId: A.1.1 + isWorkspace: false orphans: - "A.1.3" updatedChanges: diff --git a/testutils/threadbuilder/userjoinexampleupdate.yml b/testutils/threadbuilder/userjoinexampleupdate.yml index daf5d550..8e489521 100644 --- a/testutils/threadbuilder/userjoinexampleupdate.yml +++ b/testutils/threadbuilder/userjoinexampleupdate.yml @@ -101,6 +101,9 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] +header: + firstChangeId: A.1.1 + isWorkspace: false orphans: - "A.1.3" updatedChanges: diff --git a/testutils/threadbuilder/userremovebeforeexample.yml b/testutils/threadbuilder/userremovebeforeexample.yml index 7569ec04..c19ccaa6 100644 --- a/testutils/threadbuilder/userremovebeforeexample.yml +++ b/testutils/threadbuilder/userremovebeforeexample.yml @@ -104,3 +104,6 @@ graph: orphans: - "A.1.3" - "B.1.2" +header: + firstChangeId: A.1.1 + isWorkspace: false diff --git a/testutils/threadbuilder/userremoveexample.yml b/testutils/threadbuilder/userremoveexample.yml index 36a4a191..614a0687 100644 --- a/testutils/threadbuilder/userremoveexample.yml +++ b/testutils/threadbuilder/userremoveexample.yml @@ -104,3 +104,6 @@ graph: orphans: - "A.1.4" - "B.1.2" +header: + firstChangeId: A.1.1 + isWorkspace: false diff --git a/testutils/threadbuilder/validsnapshotexample.yml b/testutils/threadbuilder/validsnapshotexample.yml index 1fef3c35..9d5821ec 100644 --- a/testutils/threadbuilder/validsnapshotexample.yml +++ b/testutils/threadbuilder/validsnapshotexample.yml @@ -127,4 +127,7 @@ graph: orphans: - "A.1.3" - "B.1.2" +header: + firstChangeId: A.1.1 + isWorkspace: false diff --git a/testutils/threadbuilder/ymlentities.go b/testutils/threadbuilder/ymlentities.go index 68c7f3ac..c488ce0e 100644 --- a/testutils/threadbuilder/ymlentities.go +++ b/testutils/threadbuilder/ymlentities.go @@ -90,6 +90,11 @@ type Change struct { ReadKey string `yaml:"readKey"` } +type Header struct { + FirstChangeId string `yaml:"firstChangeId"` + IsWorkspace bool `yaml:"isWorkspace"` +} + type YMLThread struct { Description *ThreadDescription `yaml:"thread"` Changes []*Change `yaml:"changes"` @@ -102,4 +107,5 @@ type YMLThread struct { Heads []string `yaml:"heads"` Orphans []string `yaml:"orphans"` + Header *Header `yaml:"header"` } From e17e8e15c77a9e3de2295ca60414b2266cbe2572 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 10:55:09 +0200 Subject: [PATCH 44/53] Simplify paths to yaml tests --- acltree/acltree_test.go | 10 +++++----- acltree/treebuilder_test.go | 6 +++--- plaintextdocument/document_test.go | 4 ++-- testutils/threadbuilder/threadbuilder.go | 7 +++++++ testutils/threadbuilder/ymlentities_test.go | 2 +- .../invalidsnapshotexample.yml | 0 testutils/yamltests/path.go | 15 +++++++++++++++ .../userjoinexample.yml | 0 .../userjoinexampleupdate.yml | 0 .../userremovebeforeexample.yml | 0 .../userremoveexample.yml | 0 .../validsnapshotexample.yml | 0 12 files changed, 33 insertions(+), 11 deletions(-) rename testutils/{threadbuilder => yamltests}/invalidsnapshotexample.yml (100%) create mode 100644 testutils/yamltests/path.go rename testutils/{threadbuilder => yamltests}/userjoinexample.yml (100%) rename testutils/{threadbuilder => yamltests}/userjoinexampleupdate.yml (100%) rename testutils/{threadbuilder => yamltests}/userremovebeforeexample.yml (100%) rename testutils/{threadbuilder => yamltests}/userremoveexample.yml (100%) rename testutils/{threadbuilder => yamltests}/validsnapshotexample.yml (100%) diff --git a/acltree/acltree_test.go b/acltree/acltree_test.go index 8f5665ac..7ca67c5c 100644 --- a/acltree/acltree_test.go +++ b/acltree/acltree_test.go @@ -17,7 +17,7 @@ func (m *mockListener) Update(tree ACLTree) {} func (m *mockListener) Rebuild(tree ACLTree) {} func TestACLTree_UserJoinBuild(t *testing.T) { - thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") + thr, err := threadbuilder.NewThreadBuilderWithTestName("userjoinexample.yml") if err != nil { t.Fatal(err) } @@ -52,7 +52,7 @@ func TestACLTree_UserJoinBuild(t *testing.T) { } func TestACLTree_UserRemoveBuild(t *testing.T) { - thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") + thr, err := threadbuilder.NewThreadBuilderWithTestName("userremoveexample.yml") if err != nil { t.Fatal(err) } @@ -83,7 +83,7 @@ func TestACLTree_UserRemoveBuild(t *testing.T) { } func TestACLTree_UserRemoveBeforeBuild(t *testing.T) { - thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremovebeforeexample.yml") + thr, err := threadbuilder.NewThreadBuilderWithTestName("userremovebeforeexample.yml") if err != nil { t.Fatal(err) } @@ -115,7 +115,7 @@ func TestACLTree_UserRemoveBeforeBuild(t *testing.T) { } func TestACLTree_InvalidSnapshotBuild(t *testing.T) { - thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/invalidsnapshotexample.yml") + thr, err := threadbuilder.NewThreadBuilderWithTestName("invalidsnapshotexample.yml") if err != nil { t.Fatal(err) } @@ -146,7 +146,7 @@ func TestACLTree_InvalidSnapshotBuild(t *testing.T) { } func TestACLTree_ValidSnapshotBuild(t *testing.T) { - thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/validsnapshotexample.yml") + thr, err := threadbuilder.NewThreadBuilderWithTestName("validsnapshotexample.yml") if err != nil { t.Fatal(err) } diff --git a/acltree/treebuilder_test.go b/acltree/treebuilder_test.go index 02d0a317..cc586148 100644 --- a/acltree/treebuilder_test.go +++ b/acltree/treebuilder_test.go @@ -7,7 +7,7 @@ package acltree //} // //func TestACLTreeBuilder_UserJoinCorrectHeadsAndLen(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") // if err != nil { // t.Fatal(err) // } @@ -22,7 +22,7 @@ package acltree //} // //func TestTreeBuilder_UserJoinTestTreeIterate(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") // if err != nil { // t.Fatal(err) // } @@ -43,7 +43,7 @@ package acltree //} // //func TestTreeBuilder_UserRemoveTestTreeIterate(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") +// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userremoveexample.yml") // if err != nil { // t.Fatal(err) // } diff --git a/plaintextdocument/document_test.go b/plaintextdocument/document_test.go index b90f1f9c..0532240e 100644 --- a/plaintextdocument/document_test.go +++ b/plaintextdocument/document_test.go @@ -8,7 +8,7 @@ package plaintextdocument //) // //func TestDocument_Build(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") // if err != nil { // t.Fatal(err) // } @@ -28,7 +28,7 @@ package plaintextdocument //} // //func TestDocument_Update(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") +// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") // if err != nil { // t.Fatal(err) // } diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 9cfd25f8..e5af56a3 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -4,8 +4,10 @@ import ( "context" "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/yamltests" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "io/ioutil" + "path" "github.com/gogo/protobuf/proto" "gopkg.in/yaml.v3" @@ -46,6 +48,11 @@ func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { } } +func NewThreadBuilderWithTestName(name string) (*ThreadBuilder, error) { + filePath := path.Join(yamltests.Path(), name) + return NewThreadBuilderFromFile(filePath) +} + func NewThreadBuilderFromFile(file string) (*ThreadBuilder, error) { content, err := ioutil.ReadFile(file) if err != nil { diff --git a/testutils/threadbuilder/ymlentities_test.go b/testutils/threadbuilder/ymlentities_test.go index d2420eae..30c8f453 100644 --- a/testutils/threadbuilder/ymlentities_test.go +++ b/testutils/threadbuilder/ymlentities_test.go @@ -6,7 +6,7 @@ import ( ) func Test_YamlParse(t *testing.T) { - tb, _ := NewThreadBuilderFromFile("userjoinexample.yml") + tb, _ := NewThreadBuilderWithTestName("userjoinexample.yml") gr, _ := tb.Graph() fmt.Println(gr) } diff --git a/testutils/threadbuilder/invalidsnapshotexample.yml b/testutils/yamltests/invalidsnapshotexample.yml similarity index 100% rename from testutils/threadbuilder/invalidsnapshotexample.yml rename to testutils/yamltests/invalidsnapshotexample.yml diff --git a/testutils/yamltests/path.go b/testutils/yamltests/path.go new file mode 100644 index 00000000..c2dd2712 --- /dev/null +++ b/testutils/yamltests/path.go @@ -0,0 +1,15 @@ +package yamltests + +import ( + "path/filepath" + "runtime" +) + +var ( + _, b, _, _ = runtime.Caller(0) + basepath = filepath.Dir(b) +) + +func Path() string { + return basepath +} diff --git a/testutils/threadbuilder/userjoinexample.yml b/testutils/yamltests/userjoinexample.yml similarity index 100% rename from testutils/threadbuilder/userjoinexample.yml rename to testutils/yamltests/userjoinexample.yml diff --git a/testutils/threadbuilder/userjoinexampleupdate.yml b/testutils/yamltests/userjoinexampleupdate.yml similarity index 100% rename from testutils/threadbuilder/userjoinexampleupdate.yml rename to testutils/yamltests/userjoinexampleupdate.yml diff --git a/testutils/threadbuilder/userremovebeforeexample.yml b/testutils/yamltests/userremovebeforeexample.yml similarity index 100% rename from testutils/threadbuilder/userremovebeforeexample.yml rename to testutils/yamltests/userremovebeforeexample.yml diff --git a/testutils/threadbuilder/userremoveexample.yml b/testutils/yamltests/userremoveexample.yml similarity index 100% rename from testutils/threadbuilder/userremoveexample.yml rename to testutils/yamltests/userremoveexample.yml diff --git a/testutils/threadbuilder/validsnapshotexample.yml b/testutils/yamltests/validsnapshotexample.yml similarity index 100% rename from testutils/threadbuilder/validsnapshotexample.yml rename to testutils/yamltests/validsnapshotexample.yml From 27ec72cdb982b6985a4cdfe295b411b6410a9808 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 13:26:06 +0200 Subject: [PATCH 45/53] Fix header parse and add todos --- testutils/threadbuilder/threadbuilder.go | 6 +----- thread/pb/protos/thread.proto | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index e5af56a3..8258adbf 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -79,11 +79,6 @@ func (t *ThreadBuilder) GetKeychain() *Keychain { return t.keychain } -// writer can create docs -> id can create writer permissions -// by id we can check who created -// at the same time this guy can add some random folks which are not in space -// but we should compare this against space in the future - func (t *ThreadBuilder) Heads() []string { return t.heads } @@ -228,6 +223,7 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { t.parseGraph(thread) t.parseOrphans(thread) + t.parseHeader(thread) } func (t *ThreadBuilder) parseChange(ch *Change) *threadChange { diff --git a/thread/pb/protos/thread.proto b/thread/pb/protos/thread.proto index fb2b15e4..97557b25 100644 --- a/thread/pb/protos/thread.proto +++ b/thread/pb/protos/thread.proto @@ -5,4 +5,5 @@ option go_package = "pb"; message ThreadHeader { string firstChangeId = 1; bool isWorkspace = 2; + // TODO: add user identity, signature and nano timestamp } \ No newline at end of file From 277d40761bf6bd6cd9315be1963c04d6b05b1b3a Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 16:33:22 +0200 Subject: [PATCH 46/53] Change updates format in yaml --- acltree/acltree.go | 4 ++ acltree/change.go | 13 ++++ testutils/threadbuilder/threadbuilder.go | 64 +++++++++++-------- testutils/threadbuilder/ymlentities.go | 15 +++-- testutils/threadbuilder/ymlentities_test.go | 2 +- testutils/yamltests/userjoinexample.yml | 12 ---- testutils/yamltests/userjoinexampleupdate.yml | 56 ++++++++-------- 7 files changed, 96 insertions(+), 70 deletions(-) diff --git a/acltree/acltree.go b/acltree/acltree.go index 276a4054..2a671d53 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -148,6 +148,10 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error { return a.rebuildFromThread(true) } } + // TODO: there is a question how we can validate not only that the full tree is built correctly + // but also that the ACL prev ids are not messed up. I think we should probably compare the resulting + // acl state with the acl state which is built in aclTreeFromStart + err = a.aclStateBuilder.Init(a.fullTree) if err != nil { return err diff --git a/acltree/change.go b/acltree/change.go index 40a168d0..5f3121b3 100644 --- a/acltree/change.go +++ b/acltree/change.go @@ -2,6 +2,7 @@ package acltree import ( "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" "github.com/gogo/protobuf/proto" "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" @@ -46,6 +47,18 @@ func (ch *Change) IsACLChange() bool { return ch.Content.GetAclData() != nil } +func NewFromRawChange(rawChange *thread.RawChange) (*Change, error) { + unmarshalled := &pb.ACLChange{} + err := proto.Unmarshal(rawChange.Payload, unmarshalled) + if err != nil { + return nil, err + } + + ch := NewChange(rawChange.Id, unmarshalled) + ch.Sign = rawChange.Signature + return ch, nil +} + func NewChange(id string, ch *pb.ACLChange) *Change { return &Change{ Next: nil, diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 8258adbf..313f209c 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -30,21 +30,25 @@ type threadChange struct { changesDataDecrypted []byte } +type updateUseCase struct { + changes map[string]*threadChange +} + type ThreadBuilder struct { - threadId string - allChanges map[string]*threadChange - updatedChanges map[string]*threadChange - heads []string - orphans []string - keychain *Keychain - header *threadpb.ThreadHeader + threadId string + allChanges map[string]*threadChange + updates map[string]*updateUseCase + heads []string + orphans []string + keychain *Keychain + header *threadpb.ThreadHeader } func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { return &ThreadBuilder{ - allChanges: make(map[string]*threadChange), - updatedChanges: make(map[string]*threadChange), - keychain: keychain, + allChanges: make(map[string]*threadChange), + updates: make(map[string]*updateUseCase), + keychain: keychain, } } @@ -162,10 +166,11 @@ func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*thread return t.getChange(recordID, t.allChanges), nil } -func (t *ThreadBuilder) GetUpdatedChanges() []*thread.RawChange { +func (t *ThreadBuilder) GetUpdates(useCase string) []*thread.RawChange { var res []*thread.RawChange - for _, ch := range t.updatedChanges { - rawCh := t.getChange(ch.id, t.updatedChanges) + update := t.updates[useCase] + for _, ch := range update.changes { + rawCh := t.getChange(ch.id, update.changes) res = append(res, rawCh) } return res @@ -216,14 +221,10 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { t.allChanges[newChange.id] = newChange } - for _, ch := range thread.UpdatedChanges { - newChange := t.parseChange(ch) - t.updatedChanges[newChange.id] = newChange - } - t.parseGraph(thread) t.parseOrphans(thread) t.parseHeader(thread) + t.parseUpdates(thread.Updates) } func (t *ThreadBuilder) parseChange(ch *Change) *threadChange { @@ -500,6 +501,26 @@ func (t *ThreadBuilder) traverseFromHeads(f func(t *threadChange) error) error { return nil } +func (t *ThreadBuilder) parseUpdates(updates []*Update) { + for _, update := range updates { + useCase := &updateUseCase{ + changes: map[string]*threadChange{}, + } + for _, ch := range update.Changes { + newChange := t.parseChange(ch) + useCase.changes[newChange.id] = newChange + } + for _, node := range update.Graph { + rec := useCase.changes[node.Id] + rec.AclHeadIds = node.ACLHeads + rec.TreeHeadIds = node.TreeHeads + rec.SnapshotBaseId = node.BaseSnapshot + } + + t.updates[update.UseCase] = useCase + } +} + func (t *ThreadBuilder) parseGraph(thread *YMLThread) { for _, node := range thread.Graph { rec := t.allChanges[node.Id] @@ -507,13 +528,6 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) { rec.TreeHeadIds = node.TreeHeads rec.SnapshotBaseId = node.BaseSnapshot } - - for _, node := range thread.UpdatedGraph { - rec := t.updatedChanges[node.Id] - rec.AclHeadIds = node.ACLHeads - rec.TreeHeadIds = node.TreeHeads - rec.SnapshotBaseId = node.BaseSnapshot - } } func (t *ThreadBuilder) parseOrphans(thread *YMLThread) { diff --git a/testutils/threadbuilder/ymlentities.go b/testutils/threadbuilder/ymlentities.go index c488ce0e..37e983b7 100644 --- a/testutils/threadbuilder/ymlentities.go +++ b/testutils/threadbuilder/ymlentities.go @@ -95,15 +95,20 @@ type Header struct { IsWorkspace bool `yaml:"isWorkspace"` } +type Update struct { + UseCase string `yaml:"useCase"` + Changes []*Change `yaml:"changes"` + Graph []*GraphNode `yaml:"graph"` +} + type YMLThread struct { - Description *ThreadDescription `yaml:"thread"` - Changes []*Change `yaml:"changes"` - UpdatedChanges []*Change `yaml:"updatedChanges"` + Description *ThreadDescription `yaml:"thread"` + Changes []*Change `yaml:"changes"` + Updates []*Update `yaml:"updates"` Keys Keys `yaml:"keys"` - Graph []*GraphNode `yaml:"graph"` - UpdatedGraph []*GraphNode `yaml:"updatedGraph"` + Graph []*GraphNode `yaml:"graph"` Heads []string `yaml:"heads"` Orphans []string `yaml:"orphans"` diff --git a/testutils/threadbuilder/ymlentities_test.go b/testutils/threadbuilder/ymlentities_test.go index 30c8f453..a702152b 100644 --- a/testutils/threadbuilder/ymlentities_test.go +++ b/testutils/threadbuilder/ymlentities_test.go @@ -6,7 +6,7 @@ import ( ) func Test_YamlParse(t *testing.T) { - tb, _ := NewThreadBuilderWithTestName("userjoinexample.yml") + tb, _ := NewThreadBuilderWithTestName("userjoinexampleupdate.yml") gr, _ := tb.Graph() fmt.Println(gr) } diff --git a/testutils/yamltests/userjoinexample.yml b/testutils/yamltests/userjoinexample.yml index 88f9ed92..60080ccc 100644 --- a/testutils/yamltests/userjoinexample.yml +++ b/testutils/yamltests/userjoinexample.yml @@ -104,15 +104,3 @@ header: isWorkspace: false orphans: - "A.1.3" -updatedChanges: - - id: B.1.3 - identity: B - changes: - - textAppend: - text: "second" - readKey: key.Read.1 -updatedGraph: - - id: B.1.3 - baseSnapshot: A.1.1 - aclHeads: [ B.1.1 ] - treeHeads: [ B.1.2 ] diff --git a/testutils/yamltests/userjoinexampleupdate.yml b/testutils/yamltests/userjoinexampleupdate.yml index 8e489521..cebfaf4a 100644 --- a/testutils/yamltests/userjoinexampleupdate.yml +++ b/testutils/yamltests/userjoinexampleupdate.yml @@ -106,31 +106,33 @@ header: isWorkspace: false orphans: - "A.1.3" -updatedChanges: - - id: B.1.3 - identity: B +updates: + - useCase: append changes: - - textAppend: - text: "second" - readKey: key.Read.1 - - id: A.1.4 - identity: A - aclChanges: - - userAdd: - identity: D - permission: writer - encryptionKey: key.Enc.D - encryptedReadKeys: [ key.Read.1 ] - changes: - - textAppend: - text: "second" - readKey: key.Read.1 -updatedGraph: - - id: B.1.3 - baseSnapshot: A.1.1 - aclHeads: [ B.1.1 ] - treeHeads: [ B.1.2 ] - - id: A.1.4 - baseSnapshot: A.1.1 - aclHeads: [ B.1.1 ] - treeHeads: [ B.1.3 ] + - id: B.1.3 + identity: B + changes: + - textAppend: + text: "second" + readKey: key.Read.1 + - id: A.1.4 + identity: A + aclChanges: + - userAdd: + identity: D + permission: writer + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + changes: + - textAppend: + text: "second" + readKey: key.Read.1 + graph: + - id: B.1.3 + baseSnapshot: A.1.1 + aclHeads: [ B.1.1 ] + treeHeads: [ B.1.2 ] + - id: A.1.4 + baseSnapshot: A.1.1 + aclHeads: [ B.1.1 ] + treeHeads: [ B.1.3 ] From 11d3d936930c51ff544933101d796e395f0d77f8 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 16:57:02 +0200 Subject: [PATCH 47/53] Add first update test --- acltree/acltree.go | 78 ++++++++++--------- acltree/acltree_test.go | 54 +++++++++++-- testutils/yamltests/userjoinexampleupdate.yml | 3 - 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/acltree/acltree.go b/acltree/acltree.go index 2a671d53..1e0e3e00 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -84,38 +84,54 @@ func BuildACLTree( if err != nil { return nil, err } + aclTree.removeOrphans() 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.aclTreeFromStart) - if err != nil { - return err - } +//func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) { +// if validateSnapshot { +// err = a.snapshotValidator.Init(a.aclTreeFromStart) +// if err != nil { +// return err +// } +// +// valid, err := a.snapshotValidator.ValidateSnapshot(a.fullTree.root) +// if err != nil { +// return err +// } +// if !valid { +// return a.rebuildFromThread(true) +// } +// } +// +// err = a.aclStateBuilder.Init(a.fullTree) +// if err != nil { +// return err +// } +// +// a.aclState, err = a.aclStateBuilder.Build() +// if err != nil { +// return err +// } +// +// return nil +//} - valid, err := a.snapshotValidator.ValidateSnapshot(a.fullTree.root) - if err != nil { - return err +func (a *aclTree) removeOrphans() { + // 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 !valid { - return a.rebuildFromThread(true) + if _, exists := a.fullTree.invalidChanges[orphan]; exists { + toRemove = append(toRemove, orphan) } } - - err = a.aclStateBuilder.Init(a.fullTree) - if err != nil { - return err - } - - a.aclState, err = a.aclStateBuilder.Build() - if err != nil { - return err - } - - return nil + a.thread.RemoveOrphans(toRemove...) } func (a *aclTree) rebuildFromThread(fromStart bool) error { @@ -213,18 +229,8 @@ func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { 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...) + a.removeOrphans() + a.thread.SetHeads(a.fullTree.Heads()) switch mode { case Append: a.updateListener.Update(a) @@ -265,6 +271,8 @@ func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { Summary: AddResultSummaryRebuild, }, nil default: + // just rebuilding the state from start without reloading everything from thread + // as an optimization we could've started from current heads, but I didn't implement that a.aclState, err = a.aclStateBuilder.Build() if err != nil { return AddResult{}, err diff --git a/acltree/acltree_test.go b/acltree/acltree_test.go index 7ca67c5c..163c47e2 100644 --- a/acltree/acltree_test.go +++ b/acltree/acltree_test.go @@ -33,7 +33,6 @@ func TestACLTree_UserJoinBuild(t *testing.T) { t.Fatalf("should Build acl ACLState without err: %v", err) } aclState := tree.ACLState() - //fmt.Println(ctx.Tree.Graph()) aId := keychain.GeneratedIdentities["A"] bId := keychain.GeneratedIdentities["B"] cId := keychain.GeneratedIdentities["C"] @@ -51,6 +50,55 @@ func TestACLTree_UserJoinBuild(t *testing.T) { assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "B.1.2"}) } +func TestACLTree_UserJoinUpdate_Append(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderWithTestName("userjoinexampleupdate.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + listener := &mockListener{} + tree, err := BuildACLTree(thr, accountData, listener) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + rawChanges := thr.GetUpdates("append") + var changes []*Change + for _, ch := range rawChanges { + newCh, err := NewFromRawChange(ch) + if err != nil { + t.Fatalf("should be able to create change from raw: %v", err) + } + changes = append(changes, newCh) + } + + res, err := tree.AddChanges(changes...) + assert.Equal(t, res.Summary, AddResultSummaryAppend) + + aclState := tree.ACLState() + aId := keychain.GeneratedIdentities["A"] + bId := keychain.GeneratedIdentities["B"] + cId := keychain.GeneratedIdentities["C"] + dId := keychain.GeneratedIdentities["D"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + assert.Equal(t, aclState.userStates[bId].Permissions, pb.ACLChange_Writer) + assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) + assert.Equal(t, aclState.userStates[dId].Permissions, pb.ACLChange_Writer) + + var changeIds []string + tree.Iterate(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", "B.1.2", "B.1.3", "A.1.4"}) +} + func TestACLTree_UserRemoveBuild(t *testing.T) { thr, err := threadbuilder.NewThreadBuilderWithTestName("userremoveexample.yml") if err != nil { @@ -68,7 +116,6 @@ func TestACLTree_UserRemoveBuild(t *testing.T) { t.Fatalf("should Build acl ACLState without err: %v", err) } aclState := tree.ACLState() - //fmt.Println(ctx.Tree.Graph()) aId := keychain.GeneratedIdentities["A"] assert.Equal(t, aclState.identity, aId) @@ -99,7 +146,6 @@ func TestACLTree_UserRemoveBeforeBuild(t *testing.T) { t.Fatalf("should Build acl ACLState without err: %v", err) } aclState := tree.ACLState() - //fmt.Println(ctx.Tree.Graph()) for _, s := range []string{"A", "C", "E"} { assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) } @@ -131,7 +177,6 @@ func TestACLTree_InvalidSnapshotBuild(t *testing.T) { t.Fatalf("should Build acl ACLState without err: %v", err) } aclState := tree.ACLState() - //fmt.Println(ctx.Tree.Graph()) for _, s := range []string{"A", "B", "C", "D", "E", "F"} { assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) } @@ -162,7 +207,6 @@ func TestACLTree_ValidSnapshotBuild(t *testing.T) { t.Fatalf("should Build acl ACLState without err: %v", err) } aclState := tree.ACLState() - //fmt.Println(ctx.Tree.Graph()) for _, s := range []string{"A", "B", "C", "D", "E", "F"} { assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) } diff --git a/testutils/yamltests/userjoinexampleupdate.yml b/testutils/yamltests/userjoinexampleupdate.yml index cebfaf4a..0dca174a 100644 --- a/testutils/yamltests/userjoinexampleupdate.yml +++ b/testutils/yamltests/userjoinexampleupdate.yml @@ -123,9 +123,6 @@ updates: permission: writer encryptionKey: key.Enc.D encryptedReadKeys: [ key.Read.1 ] - changes: - - textAppend: - text: "second" readKey: key.Read.1 graph: - id: B.1.3 From 7496c79ae5501134afbd08f9f7d0fec5e5f5a482 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 17:46:08 +0200 Subject: [PATCH 48/53] Add test for rebuild useCase --- acltree/acltree.go | 1 + acltree/acltree_test.go | 50 +++++++++++++++++++ acltree/treeiterator.go | 10 +++- testutils/yamltests/userjoinexampleupdate.yml | 16 ++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/acltree/acltree.go b/acltree/acltree.go index 1e0e3e00..b7a677a3 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -85,6 +85,7 @@ func BuildACLTree( return nil, err } aclTree.removeOrphans() + t.SetHeads(aclTree.Heads()) return aclTree, nil } diff --git a/acltree/acltree_test.go b/acltree/acltree_test.go index 163c47e2..8039e0b8 100644 --- a/acltree/acltree_test.go +++ b/acltree/acltree_test.go @@ -99,6 +99,56 @@ func TestACLTree_UserJoinUpdate_Append(t *testing.T) { assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "B.1.2", "B.1.3", "A.1.4"}) } +func TestACLTree_UserJoinUpdate_Rebuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderWithTestName("userjoinexampleupdate.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + listener := &mockListener{} + tree, err := BuildACLTree(thr, accountData, listener) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + rawChanges := thr.GetUpdates("rebuild") + var changes []*Change + for _, ch := range rawChanges { + newCh, err := NewFromRawChange(ch) + if err != nil { + t.Fatalf("should be able to create change from raw: %v", err) + } + changes = append(changes, newCh) + } + + res, err := tree.AddChanges(changes...) + assert.Equal(t, res.Summary, AddResultSummaryRebuild) + + aclState := tree.ACLState() + aId := keychain.GeneratedIdentities["A"] + bId := keychain.GeneratedIdentities["B"] + cId := keychain.GeneratedIdentities["C"] + dId := keychain.GeneratedIdentities["D"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + assert.Equal(t, aclState.userStates[bId].Permissions, pb.ACLChange_Writer) + assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) + assert.Equal(t, aclState.userStates[dId].Permissions, pb.ACLChange_Writer) + + var changeIds []string + + tree.Iterate(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", "B.1.2", "A.1.4"}) +} + func TestACLTree_UserRemoveBuild(t *testing.T) { thr, err := threadbuilder.NewThreadBuilderWithTestName("userremoveexample.yml") if err != nil { diff --git a/acltree/treeiterator.go b/acltree/treeiterator.go index addcf307..19a20095 100644 --- a/acltree/treeiterator.go +++ b/acltree/treeiterator.go @@ -91,7 +91,15 @@ func (i *iterator) iterateLin(c *Change) bool { break } } - i.breakpoint = c + if len(c.Next) == 0 && len(c.PreviousIds) <= 1 { + if !i.f(c) { + return false + } + i.doneMap[c] = struct{}{} + } else { + i.breakpoint = c + } + return true } diff --git a/testutils/yamltests/userjoinexampleupdate.yml b/testutils/yamltests/userjoinexampleupdate.yml index 0dca174a..c3916cc6 100644 --- a/testutils/yamltests/userjoinexampleupdate.yml +++ b/testutils/yamltests/userjoinexampleupdate.yml @@ -133,3 +133,19 @@ updates: baseSnapshot: A.1.1 aclHeads: [ B.1.1 ] treeHeads: [ B.1.3 ] + - useCase: rebuild + changes: + - id: A.1.4 + identity: A + aclChanges: + - userAdd: + identity: D + permission: writer + encryptionKey: key.Enc.D + encryptedReadKeys: [ key.Read.1 ] + readKey: key.Read.1 + graph: + - id: A.1.4 + baseSnapshot: A.1.1 + aclHeads: [ A.1.1 ] + treeHeads: [ A.1.1 ] From c90af47f02e6014e4e99ca04a3b76b71e2026f62 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 20:25:13 +0200 Subject: [PATCH 49/53] Change InviteId logic and add UserAdd to change builder --- aclchanges/pb/aclchanges.pb.go | 195 +++++++++++------- aclchanges/pb/protos/aclchanges.proto | 3 +- acltree/aclstate.go | 58 ++++-- acltree/aclstatebuilder.go | 7 +- acltree/acltree.go | 16 +- acltree/changebuilder.go | 69 ++++++- acltree/snapshotvalidator.go | 2 +- acltree/threadutility.go | 9 +- plaintextdocument/document.go | 11 +- testutils/threadbuilder/threadbuilder.go | 11 +- testutils/threadbuilder/ymlentities.go | 1 + testutils/yamltests/userjoinexample.yml | 1 + testutils/yamltests/userjoinexampleupdate.yml | 1 + testutils/yamltests/userremoveexample.yml | 1 + 14 files changed, 258 insertions(+), 127 deletions(-) diff --git a/aclchanges/pb/aclchanges.pb.go b/aclchanges/pb/aclchanges.pb.go index d872a7bb..39c15341 100644 --- a/aclchanges/pb/aclchanges.pb.go +++ b/aclchanges/pb/aclchanges.pb.go @@ -652,6 +652,7 @@ type ACLChangeUserInvite struct { EncryptPublicKey []byte `protobuf:"bytes,2,opt,name=encryptPublicKey,proto3" json:"encryptPublicKey,omitempty"` EncryptedReadKeys [][]byte `protobuf:"bytes,3,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` Permissions ACLChangeUserPermissions `protobuf:"varint,4,opt,name=permissions,proto3,enum=anytype.ACLChangeUserPermissions" json:"permissions,omitempty"` + InviteId string `protobuf:"bytes,5,opt,name=InviteId,proto3" json:"InviteId,omitempty"` } func (m *ACLChangeUserInvite) Reset() { *m = ACLChangeUserInvite{} } @@ -715,12 +716,19 @@ func (m *ACLChangeUserInvite) GetPermissions() ACLChangeUserPermissions { return ACLChange_Admin } +func (m *ACLChangeUserInvite) GetInviteId() string { + if m != nil { + return m.InviteId + } + return "" +} + type ACLChangeUserJoin struct { - Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - EncryptionKey []byte `protobuf:"bytes,2,opt,name=encryptionKey,proto3" json:"encryptionKey,omitempty"` - AcceptSignature []byte `protobuf:"bytes,3,opt,name=acceptSignature,proto3" json:"acceptSignature,omitempty"` - UserInviteChangeId string `protobuf:"bytes,4,opt,name=userInviteChangeId,proto3" json:"userInviteChangeId,omitempty"` - EncryptedReadKeys [][]byte `protobuf:"bytes,5,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` + Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + EncryptionKey []byte `protobuf:"bytes,2,opt,name=encryptionKey,proto3" json:"encryptionKey,omitempty"` + AcceptSignature []byte `protobuf:"bytes,3,opt,name=acceptSignature,proto3" json:"acceptSignature,omitempty"` + UserInviteId string `protobuf:"bytes,4,opt,name=userInviteId,proto3" json:"userInviteId,omitempty"` + EncryptedReadKeys [][]byte `protobuf:"bytes,5,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` } func (m *ACLChangeUserJoin) Reset() { *m = ACLChangeUserJoin{} } @@ -777,9 +785,9 @@ func (m *ACLChangeUserJoin) GetAcceptSignature() []byte { return nil } -func (m *ACLChangeUserJoin) GetUserInviteChangeId() string { +func (m *ACLChangeUserJoin) GetUserInviteId() string { if m != nil { - return m.UserInviteChangeId + return m.UserInviteId } return "" } @@ -978,64 +986,64 @@ func init() { } var fileDescriptor_ffc88c8445fc0f24 = []byte{ - // 897 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0xcb, 0x6e, 0xeb, 0x44, - 0x18, 0xf6, 0x24, 0x6d, 0x1d, 0xff, 0x0e, 0x6d, 0x18, 0x75, 0x61, 0xf9, 0x14, 0x2b, 0x54, 0x5c, - 0x22, 0x84, 0x52, 0x29, 0x07, 0x71, 0x13, 0x12, 0x34, 0x3d, 0x40, 0x42, 0x59, 0x1c, 0xcd, 0x11, - 0x20, 0x90, 0x58, 0x4c, 0xed, 0xe1, 0xd4, 0x22, 0xb1, 0x2d, 0xcf, 0xa4, 0x22, 0x1b, 0xde, 0x00, - 0xc1, 0x7b, 0xb0, 0xe3, 0x05, 0xd8, 0xb2, 0x3c, 0x0b, 0x84, 0x58, 0xa2, 0xf6, 0x45, 0xd0, 0x5c, - 0x7c, 0x49, 0x6a, 0x17, 0x09, 0x55, 0x42, 0xec, 0x66, 0xbe, 0xf9, 0xfe, 0xf1, 0xf7, 0x5f, 0xc7, - 0xf0, 0x32, 0x0d, 0x17, 0xe1, 0x25, 0x4d, 0x9e, 0x32, 0x7e, 0x92, 0x5d, 0x9c, 0x64, 0x79, 0x2a, - 0x52, 0x7e, 0x52, 0x81, 0x63, 0x85, 0x60, 0x9b, 0x26, 0x6b, 0xb1, 0xce, 0xd8, 0xf1, 0xcf, 0x87, - 0xe0, 0x9c, 0x9e, 0x7d, 0x7a, 0xa6, 0x4e, 0xf1, 0x10, 0x5c, 0x91, 0x33, 0x36, 0x63, 0x34, 0x9a, - 0x47, 0xdc, 0x43, 0xc3, 0xee, 0xc8, 0x21, 0x75, 0x08, 0x07, 0x00, 0x34, 0x5c, 0x14, 0x84, 0x8e, - 0x22, 0xd4, 0x10, 0xfc, 0x0a, 0xec, 0xf3, 0x84, 0x66, 0xfc, 0x32, 0x15, 0x53, 0xca, 0xd9, 0x3c, - 0xf2, 0xba, 0x43, 0x34, 0x72, 0xc8, 0x16, 0x8a, 0xdf, 0x00, 0x9b, 0x86, 0x8b, 0x47, 0x54, 0x50, - 0x6f, 0x67, 0x88, 0x46, 0xee, 0xc4, 0x1f, 0x1b, 0x49, 0xe3, 0x52, 0x8e, 0x5c, 0x49, 0x06, 0x29, - 0xa8, 0x52, 0x9f, 0xf1, 0x43, 0x59, 0xee, 0x0e, 0xd1, 0xa8, 0x4f, 0xea, 0x10, 0x1e, 0x03, 0x0e, - 0x57, 0x79, 0xce, 0x12, 0x41, 0x18, 0x8d, 0xce, 0xd9, 0x7a, 0x46, 0xf9, 0xa5, 0xb7, 0x37, 0x44, - 0xa3, 0x1d, 0xd2, 0x70, 0x82, 0x8f, 0xc0, 0x11, 0xf1, 0x92, 0x71, 0x41, 0x97, 0x99, 0x67, 0x0f, - 0xd1, 0xa8, 0x4b, 0x2a, 0x00, 0xfb, 0xd0, 0x8b, 0x23, 0x96, 0x88, 0x58, 0xac, 0xbd, 0x9e, 0xf2, - 0xa3, 0xdc, 0xfb, 0xbf, 0x74, 0xe1, 0x40, 0x4a, 0x4d, 0x13, 0xc1, 0x12, 0xf1, 0x39, 0x5d, 0xac, - 0x18, 0x7e, 0x13, 0xec, 0x15, 0x67, 0xf9, 0x69, 0x14, 0x79, 0xa8, 0xd5, 0xab, 0xcf, 0x34, 0x63, - 0x66, 0x91, 0x82, 0x8c, 0xdf, 0x07, 0x90, 0x4b, 0xc2, 0x96, 0xe9, 0x15, 0xf3, 0x3a, 0xca, 0xf4, - 0x85, 0x16, 0x53, 0x4d, 0x9a, 0x59, 0xa4, 0x66, 0x82, 0xbf, 0x86, 0x43, 0xb9, 0x7b, 0xcc, 0xf2, - 0x65, 0xcc, 0x79, 0x9c, 0x26, 0xda, 0x40, 0x05, 0xdf, 0x9d, 0xbc, 0xda, 0x72, 0xd5, 0x36, 0x7d, - 0x66, 0x91, 0xc6, 0x6b, 0x0a, 0x7d, 0xf3, 0xe4, 0x2a, 0x16, 0xcc, 0x24, 0xac, 0x4d, 0x9f, 0x26, - 0x15, 0xfa, 0xf4, 0x0e, 0xbf, 0x03, 0x3d, 0xb9, 0xfb, 0x24, 0x8d, 0x13, 0x95, 0x35, 0x77, 0xf2, - 0xa0, 0xc5, 0x5c, 0x52, 0x66, 0x16, 0x29, 0xe9, 0x78, 0x0a, 0xae, 0x5c, 0x9f, 0xa5, 0xc9, 0x37, - 0x71, 0xbe, 0x54, 0xa9, 0x74, 0x27, 0x41, 0x8b, 0xb5, 0x61, 0xcd, 0x2c, 0x52, 0x37, 0x9a, 0xda, - 0xb0, 0x7b, 0x25, 0x13, 0xe4, 0xff, 0x88, 0xc0, 0x36, 0x55, 0x85, 0x3f, 0x00, 0x97, 0x86, 0x8b, - 0x27, 0xa6, 0x2e, 0x4d, 0xc2, 0x82, 0xe6, 0x32, 0x2c, 0x58, 0xa4, 0x6e, 0x82, 0xa7, 0xaa, 0x19, - 0x4c, 0x05, 0xa8, 0x66, 0x70, 0x27, 0xc7, 0xcd, 0x17, 0xd4, 0xcb, 0x84, 0xd4, 0xac, 0xfc, 0x8f, - 0xc0, 0xad, 0xdd, 0x8f, 0xdf, 0x82, 0x9e, 0xfc, 0x82, 0xa0, 0x82, 0x19, 0x45, 0x0f, 0x5a, 0x14, - 0x49, 0x0a, 0x29, 0xc9, 0xfe, 0x0f, 0x1d, 0xe8, 0x15, 0x30, 0x7e, 0x09, 0x9e, 0xcb, 0xab, 0x22, - 0x67, 0xba, 0x93, 0x77, 0xc8, 0x26, 0x88, 0xdf, 0xd3, 0x59, 0x55, 0x26, 0xdc, 0xc8, 0x3f, 0x6a, - 0x09, 0xac, 0xfe, 0x5c, 0x8d, 0x8f, 0xa7, 0x60, 0xc7, 0x2a, 0xb9, 0xdc, 0xeb, 0x2a, 0xd3, 0xd1, - 0x1d, 0x42, 0xc7, 0xba, 0x0e, 0xf8, 0x87, 0x89, 0xc8, 0xd7, 0xa4, 0x30, 0xf4, 0xbf, 0x84, 0x7e, - 0xfd, 0x00, 0x0f, 0xa0, 0xfb, 0x2d, 0x5b, 0x2b, 0xc7, 0x1d, 0x22, 0x97, 0xf8, 0xa1, 0xc9, 0xdc, - 0x3f, 0x34, 0x85, 0xbe, 0x85, 0x68, 0xee, 0xbb, 0x9d, 0xb7, 0x91, 0x7f, 0x83, 0xc0, 0x29, 0x85, - 0x6f, 0x34, 0x32, 0xda, 0x6c, 0x64, 0x19, 0x2c, 0x96, 0x84, 0xf9, 0x3a, 0x13, 0x71, 0x9a, 0x9c, - 0xb3, 0xb5, 0xfa, 0x54, 0x9f, 0x6c, 0x82, 0xf8, 0x75, 0x78, 0xde, 0x00, 0x2c, 0x32, 0x03, 0x44, - 0x3b, 0xde, 0x27, 0xb7, 0x0f, 0xf0, 0x23, 0x70, 0xb3, 0xb2, 0x89, 0xb8, 0xea, 0x98, 0xfd, 0xc6, - 0xd2, 0xd8, 0x6c, 0x43, 0x4e, 0xea, 0x66, 0x72, 0xdc, 0xcd, 0xb9, 0xa9, 0x61, 0x16, 0xa9, 0xc6, - 0xe9, 0x91, 0x3a, 0xe4, 0xff, 0x8a, 0xc0, 0x36, 0xf3, 0xe4, 0xff, 0xe9, 0xa3, 0xff, 0x31, 0xb8, - 0xb5, 0xc6, 0xbd, 0xd3, 0x89, 0x23, 0x70, 0xcc, 0xc0, 0x9c, 0x47, 0xca, 0x01, 0x87, 0x54, 0x80, - 0xff, 0x07, 0x02, 0xa8, 0x4a, 0x01, 0x8f, 0xe0, 0x80, 0x86, 0x21, 0xcb, 0xc4, 0xe3, 0xd5, 0xc5, - 0x22, 0x0e, 0xcf, 0x4d, 0x59, 0xf5, 0xc9, 0x36, 0x8c, 0x5f, 0x83, 0x81, 0x71, 0xae, 0xa2, 0xea, - 0xf0, 0xdc, 0xc2, 0xff, 0x93, 0x08, 0xfd, 0x8e, 0xa0, 0x57, 0x4c, 0xc6, 0x7b, 0x48, 0x72, 0x19, - 0x98, 0x27, 0xf1, 0xd3, 0x84, 0x8a, 0x55, 0xae, 0x5f, 0x89, 0x32, 0x30, 0x25, 0x2c, 0xdf, 0xd2, - 0x6a, 0x84, 0x6b, 0xa5, 0xf3, 0x48, 0x79, 0xe1, 0x90, 0x86, 0x93, 0xe6, 0xe0, 0xec, 0xb6, 0x04, - 0xc7, 0x5f, 0xe9, 0x74, 0x99, 0x07, 0xec, 0x2e, 0xbf, 0xce, 0xe1, 0xc0, 0x0c, 0x2e, 0xc2, 0xb2, - 0x05, 0x0d, 0xcb, 0x89, 0xf3, 0x62, 0x43, 0x28, 0xc9, 0x06, 0x93, 0x6c, 0x5b, 0xfa, 0xdf, 0xc3, - 0xfe, 0x26, 0xe5, 0x1e, 0x42, 0x5a, 0x55, 0x50, 0xe9, 0x9f, 0x89, 0xe9, 0x2d, 0xdc, 0xff, 0x0e, - 0x0e, 0x9b, 0x9e, 0xde, 0x3b, 0x55, 0x6c, 0xd5, 0x51, 0xe7, 0x5f, 0xd5, 0xd1, 0xf1, 0x29, 0x1c, - 0x6c, 0x9d, 0x63, 0x07, 0x76, 0x4f, 0xa3, 0x65, 0x9c, 0x0c, 0x2c, 0x0c, 0xb0, 0xf7, 0x45, 0x1e, - 0x0b, 0x96, 0x0f, 0x90, 0x5c, 0x4b, 0xb9, 0x2c, 0x1f, 0x74, 0xb0, 0x0b, 0xb6, 0x4e, 0x51, 0x34, - 0xe8, 0x4e, 0x8f, 0x7e, 0xbb, 0x0e, 0xd0, 0xb3, 0xeb, 0x00, 0xfd, 0x75, 0x1d, 0xa0, 0x9f, 0x6e, - 0x02, 0xeb, 0xd9, 0x4d, 0x60, 0xfd, 0x79, 0x13, 0x58, 0x5f, 0x75, 0xb2, 0x8b, 0x8b, 0x3d, 0xf5, - 0x6f, 0xf9, 0xf0, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x54, 0xc0, 0x2c, 0x4a, 0x84, 0x0a, 0x00, - 0x00, + // 907 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4d, 0x6f, 0xe3, 0x44, + 0x18, 0xf6, 0x24, 0x6d, 0x1d, 0xbf, 0x0e, 0x6d, 0x18, 0xf5, 0x60, 0x79, 0x8b, 0x15, 0x2a, 0x3e, + 0x22, 0x84, 0x52, 0x29, 0x8b, 0xf8, 0x12, 0x12, 0x34, 0x5d, 0x20, 0xa1, 0x1c, 0x56, 0xb3, 0x02, + 0x04, 0x12, 0x87, 0xa9, 0x3d, 0x6c, 0x2d, 0x92, 0xb1, 0xe5, 0x99, 0x54, 0xe4, 0xc2, 0x3f, 0x40, + 0xf0, 0x5b, 0xb8, 0x72, 0xe0, 0xba, 0xc7, 0x3d, 0x72, 0x44, 0xed, 0x3f, 0xe0, 0x17, 0xa0, 0xf9, + 0xb0, 0xe3, 0xa4, 0x71, 0x91, 0x50, 0x25, 0xc4, 0x6d, 0xe6, 0x99, 0xe7, 0x99, 0xbc, 0x1f, 0xcf, + 0x3b, 0x0e, 0xbc, 0x4a, 0xe3, 0x59, 0x7c, 0x49, 0xf9, 0x53, 0x26, 0x4e, 0xf2, 0x8b, 0x93, 0xbc, + 0xc8, 0x64, 0x26, 0x4e, 0x56, 0xe0, 0x50, 0x23, 0xd8, 0xa5, 0x7c, 0x29, 0x97, 0x39, 0x3b, 0xfe, + 0xed, 0x10, 0xbc, 0xd3, 0xb3, 0xcf, 0xcf, 0xf4, 0x29, 0xee, 0x83, 0x2f, 0x0b, 0xc6, 0x26, 0x8c, + 0x26, 0xd3, 0x44, 0x04, 0xa8, 0xdf, 0x1e, 0x78, 0xa4, 0x0e, 0xe1, 0x08, 0x80, 0xc6, 0xb3, 0x92, + 0xd0, 0xd2, 0x84, 0x1a, 0x82, 0x5f, 0x83, 0x7d, 0xc1, 0x69, 0x2e, 0x2e, 0x33, 0x39, 0xa6, 0x82, + 0x4d, 0x93, 0xa0, 0xdd, 0x47, 0x03, 0x8f, 0x6c, 0xa0, 0xf8, 0x2d, 0x70, 0x69, 0x3c, 0x7b, 0x44, + 0x25, 0x0d, 0x76, 0xfa, 0x68, 0xe0, 0x8f, 0xc2, 0xa1, 0x0d, 0x69, 0x58, 0x85, 0xa3, 0x56, 0x8a, + 0x41, 0x4a, 0xaa, 0x8a, 0xcf, 0xe6, 0xa1, 0x95, 0xbb, 0x7d, 0x34, 0xe8, 0x92, 0x3a, 0x84, 0x87, + 0x80, 0xe3, 0x45, 0x51, 0x30, 0x2e, 0x09, 0xa3, 0xc9, 0x39, 0x5b, 0x4e, 0xa8, 0xb8, 0x0c, 0xf6, + 0xfa, 0x68, 0xb0, 0x43, 0xb6, 0x9c, 0xe0, 0x23, 0xf0, 0x64, 0x3a, 0x67, 0x42, 0xd2, 0x79, 0x1e, + 0xb8, 0x7d, 0x34, 0x68, 0x93, 0x15, 0x80, 0x43, 0xe8, 0xa4, 0x09, 0xe3, 0x32, 0x95, 0xcb, 0xa0, + 0xa3, 0xf3, 0xa8, 0xf6, 0xe1, 0xaf, 0x6d, 0x38, 0x50, 0xa1, 0x66, 0x5c, 0x32, 0x2e, 0xbf, 0xa4, + 0xb3, 0x05, 0xc3, 0x6f, 0x83, 0xbb, 0x10, 0xac, 0x38, 0x4d, 0x92, 0x00, 0x35, 0x66, 0xf5, 0x85, + 0x61, 0x4c, 0x1c, 0x52, 0x92, 0xf1, 0x87, 0x00, 0x6a, 0x49, 0xd8, 0x3c, 0xbb, 0x62, 0x41, 0x4b, + 0x4b, 0x5f, 0x6a, 0x90, 0x1a, 0xd2, 0xc4, 0x21, 0x35, 0x09, 0xfe, 0x16, 0x0e, 0xd5, 0xee, 0x31, + 0x2b, 0xe6, 0xa9, 0x10, 0x69, 0xc6, 0x8d, 0x40, 0x17, 0xdf, 0x1f, 0xbd, 0xde, 0x70, 0xd5, 0x26, + 0x7d, 0xe2, 0x90, 0xad, 0xd7, 0x94, 0xf1, 0x4d, 0xf9, 0x55, 0x2a, 0x99, 0x6d, 0x58, 0x53, 0x7c, + 0x86, 0x54, 0xc6, 0x67, 0x76, 0xf8, 0x3d, 0xe8, 0xa8, 0xdd, 0x67, 0x59, 0xca, 0x75, 0xd7, 0xfc, + 0xd1, 0x83, 0x06, 0xb9, 0xa2, 0x4c, 0x1c, 0x52, 0xd1, 0xf1, 0x18, 0x7c, 0xb5, 0x3e, 0xcb, 0xf8, + 0x77, 0x69, 0x31, 0xd7, 0xad, 0xf4, 0x47, 0x51, 0x83, 0xda, 0xb2, 0x26, 0x0e, 0xa9, 0x8b, 0xc6, + 0x2e, 0xec, 0x5e, 0xa9, 0x06, 0x85, 0x3f, 0x23, 0x70, 0xad, 0xab, 0xf0, 0x47, 0xe0, 0xd3, 0x78, + 0xf6, 0xc4, 0xfa, 0xd2, 0x36, 0x2c, 0xda, 0x6e, 0xc3, 0x92, 0x45, 0xea, 0x12, 0x3c, 0xd6, 0xc3, + 0x60, 0x1d, 0xa0, 0x87, 0xc1, 0x1f, 0x1d, 0x6f, 0xbf, 0xa0, 0x6e, 0x13, 0x52, 0x53, 0x85, 0x9f, + 0x80, 0x5f, 0xbb, 0x1f, 0xbf, 0x03, 0x1d, 0xf5, 0x0b, 0x92, 0x4a, 0x66, 0x23, 0x7a, 0xd0, 0x10, + 0x91, 0xa2, 0x90, 0x8a, 0x1c, 0xfe, 0xd4, 0x82, 0x4e, 0x09, 0xe3, 0x57, 0xe0, 0x85, 0x62, 0x65, + 0x72, 0x66, 0x26, 0x79, 0x87, 0xac, 0x83, 0xf8, 0x03, 0xd3, 0x55, 0x2d, 0x11, 0x36, 0xfc, 0xa3, + 0x86, 0xc2, 0x9a, 0x9f, 0xab, 0xf1, 0xf1, 0x18, 0xdc, 0x54, 0x37, 0x57, 0x04, 0x6d, 0x2d, 0x1d, + 0xdc, 0x11, 0xe8, 0xd0, 0xf8, 0x40, 0x7c, 0xcc, 0x65, 0xb1, 0x24, 0xa5, 0x30, 0xfc, 0x1a, 0xba, + 0xf5, 0x03, 0xdc, 0x83, 0xf6, 0xf7, 0x6c, 0xa9, 0x13, 0xf7, 0x88, 0x5a, 0xe2, 0x87, 0xb6, 0x73, + 0xff, 0x30, 0x14, 0xe6, 0x16, 0x62, 0xb8, 0xef, 0xb7, 0xde, 0x45, 0xe1, 0x0d, 0x02, 0xaf, 0x0a, + 0x7c, 0x6d, 0x90, 0xd1, 0xfa, 0x20, 0xab, 0x62, 0x31, 0x1e, 0x17, 0xcb, 0x5c, 0xa6, 0x19, 0x3f, + 0x67, 0x4b, 0xfd, 0x53, 0x5d, 0xb2, 0x0e, 0xe2, 0x37, 0xe1, 0x45, 0x0b, 0xb0, 0xc4, 0x3e, 0x20, + 0x26, 0xf1, 0x2e, 0xb9, 0x7d, 0x80, 0x1f, 0x81, 0x9f, 0x57, 0x43, 0x24, 0xf4, 0xc4, 0xec, 0x6f, + 0xb5, 0xc6, 0xfa, 0x18, 0x0a, 0x52, 0x97, 0xa9, 0xe7, 0x6e, 0x2a, 0xac, 0x87, 0x59, 0xa2, 0x07, + 0xa7, 0x43, 0xea, 0x50, 0xf8, 0x3b, 0x02, 0xd7, 0xbe, 0x27, 0xff, 0xcf, 0x1c, 0xc3, 0x4f, 0xc1, + 0xaf, 0x0d, 0xee, 0x9d, 0x49, 0x1c, 0x81, 0x67, 0x1f, 0xcc, 0x69, 0xa2, 0x13, 0xf0, 0xc8, 0x0a, + 0x08, 0xff, 0x42, 0x00, 0x2b, 0x2b, 0xe0, 0x01, 0x1c, 0xd0, 0x38, 0x66, 0xb9, 0x7c, 0xbc, 0xb8, + 0x98, 0xa5, 0xf1, 0xb9, 0xb5, 0x55, 0x97, 0x6c, 0xc2, 0xf8, 0x0d, 0xe8, 0xd9, 0xe4, 0x56, 0x54, + 0x53, 0x9e, 0x5b, 0xf8, 0x7f, 0xe2, 0x82, 0x10, 0x3a, 0x26, 0xa7, 0xa9, 0xb1, 0x80, 0x47, 0xaa, + 0x7d, 0xf8, 0x0c, 0x41, 0xa7, 0x7c, 0x35, 0xef, 0xc1, 0x00, 0x55, 0xd1, 0x9e, 0xa4, 0x4f, 0x39, + 0x95, 0x8b, 0xc2, 0x7c, 0x41, 0xaa, 0xa2, 0x55, 0x30, 0x3e, 0x86, 0xee, 0xea, 0x79, 0x9f, 0x26, + 0x3a, 0x37, 0x8f, 0xac, 0x61, 0xdb, 0x8b, 0xb5, 0xdb, 0x50, 0xac, 0x70, 0x61, 0xda, 0x67, 0x3f, + 0x68, 0x77, 0xe5, 0x72, 0x0e, 0x07, 0xf6, 0x21, 0x23, 0x2c, 0x9f, 0xd1, 0xb8, 0x7a, 0x81, 0x5e, + 0xde, 0x52, 0x5a, 0xb2, 0xc6, 0x24, 0x9b, 0xca, 0xf0, 0x47, 0xd8, 0x5f, 0xa7, 0xdc, 0x43, 0x19, + 0x57, 0x8e, 0xaa, 0xf2, 0xb3, 0x75, 0xbc, 0x85, 0x87, 0x3f, 0xc0, 0xe1, 0xb6, 0x4f, 0xf1, 0x9d, + 0x51, 0x6c, 0xf8, 0xaa, 0xf5, 0xaf, 0x7c, 0x75, 0x7c, 0x0a, 0x07, 0x1b, 0xe7, 0xd8, 0x83, 0xdd, + 0xd3, 0x64, 0x9e, 0xf2, 0x9e, 0x83, 0x01, 0xf6, 0xbe, 0x2a, 0x52, 0xc9, 0x8a, 0x1e, 0x52, 0x6b, + 0x15, 0x2e, 0x2b, 0x7a, 0x2d, 0xec, 0x83, 0x6b, 0x5a, 0x94, 0xf4, 0xda, 0xe3, 0xa3, 0x67, 0xd7, + 0x11, 0x7a, 0x7e, 0x1d, 0xa1, 0x3f, 0xaf, 0x23, 0xf4, 0xcb, 0x4d, 0xe4, 0x3c, 0xbf, 0x89, 0x9c, + 0x3f, 0x6e, 0x22, 0xe7, 0x9b, 0x56, 0x7e, 0x71, 0xb1, 0xa7, 0xff, 0x6b, 0x3e, 0xfc, 0x3b, 0x00, + 0x00, 0xff, 0xff, 0xd3, 0xdf, 0xf9, 0x1b, 0x94, 0x0a, 0x00, 0x00, } func (m *ACLChange) Marshal() (dAtA []byte, err error) { @@ -1614,6 +1622,13 @@ func (m *ACLChangeUserInvite) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.InviteId) > 0 { + i -= len(m.InviteId) + copy(dAtA[i:], m.InviteId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.InviteId))) + i-- + dAtA[i] = 0x2a + } if m.Permissions != 0 { i = encodeVarintAclchanges(dAtA, i, uint64(m.Permissions)) i-- @@ -1674,10 +1689,10 @@ func (m *ACLChangeUserJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x2a } } - if len(m.UserInviteChangeId) > 0 { - i -= len(m.UserInviteChangeId) - copy(dAtA[i:], m.UserInviteChangeId) - i = encodeVarintAclchanges(dAtA, i, uint64(len(m.UserInviteChangeId))) + if len(m.UserInviteId) > 0 { + i -= len(m.UserInviteId) + copy(dAtA[i:], m.UserInviteId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.UserInviteId))) i-- dAtA[i] = 0x22 } @@ -2128,6 +2143,10 @@ func (m *ACLChangeUserInvite) Size() (n int) { if m.Permissions != 0 { n += 1 + sovAclchanges(uint64(m.Permissions)) } + l = len(m.InviteId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } return n } @@ -2149,7 +2168,7 @@ func (m *ACLChangeUserJoin) Size() (n int) { if l > 0 { n += 1 + l + sovAclchanges(uint64(l)) } - l = len(m.UserInviteChangeId) + l = len(m.UserInviteId) if l > 0 { n += 1 + l + sovAclchanges(uint64(l)) } @@ -3881,6 +3900,38 @@ func (m *ACLChangeUserInvite) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InviteId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InviteId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAclchanges(dAtA[iNdEx:]) @@ -4033,7 +4084,7 @@ func (m *ACLChangeUserJoin) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserInviteChangeId", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field UserInviteId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -4061,7 +4112,7 @@ func (m *ACLChangeUserJoin) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.UserInviteChangeId = string(dAtA[iNdEx:postIndex]) + m.UserInviteId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { diff --git a/aclchanges/pb/protos/aclchanges.proto b/aclchanges/pb/protos/aclchanges.proto index 5b101b04..d62b5088 100644 --- a/aclchanges/pb/protos/aclchanges.proto +++ b/aclchanges/pb/protos/aclchanges.proto @@ -69,13 +69,14 @@ message ACLChange { bytes encryptPublicKey = 2; repeated bytes encryptedReadKeys = 3; // all read keys that we know for the user UserPermissions permissions = 4; + string InviteId = 5; } message UserJoin { string identity = 1; bytes encryptionKey = 2; bytes acceptSignature = 3; // sign acceptPublicKey - string userInviteChangeId = 4; + string userInviteId = 4; repeated bytes encryptedReadKeys = 5; // the idea is that user should itself reencrypt the keys with the pub key } diff --git a/acltree/aclstate.go b/acltree/aclstate.go index fd92cca6..b56142b6 100644 --- a/acltree/aclstate.go +++ b/acltree/aclstate.go @@ -41,8 +41,8 @@ func newACLState( } } -func newACLStateFromSnapshot( - snapshot *pb.ACLChangeACLSnapshot, +func newACLStateFromSnapshotChange( + snapshotChange *pb.ACLChange, identity string, encryptionKey keys.EncryptionPrivKey, signingPubKeyDecoder keys.SigningPubKeyDecoder) (*ACLState, error) { @@ -54,14 +54,18 @@ func newACLStateFromSnapshot( userInvites: make(map[string]*pb.ACLChangeUserInvite), signingPubKeyDecoder: signingPubKeyDecoder, } - err := st.recreateFromSnapshot(snapshot) + err := st.recreateFromSnapshotChange(snapshotChange) if err != nil { return nil, err } return st, nil } -func (st *ACLState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) error { +func (st *ACLState) recreateFromSnapshotChange(snapshotChange *pb.ACLChange) error { + snapshot := snapshotChange.GetAclData().GetAclSnapshot() + if snapshot == nil { + return fmt.Errorf("could not create state from snapshot, because it is nil") + } state := snapshot.AclState for _, userState := range state.UserStates { st.userStates[userState.Identity] = userState @@ -71,7 +75,6 @@ func (st *ACLState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) erro if !exists { return ErrNoSuchUser } - var lastKeyHash uint64 for _, key := range userState.EncryptedReadKeys { key, hash, err := st.decryptReadKeyAndHash(key) if err != nil { @@ -79,9 +82,8 @@ func (st *ACLState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) erro } st.userReadKeys[hash] = key - lastKeyHash = hash } - st.currentReadKeyHash = lastKeyHash + st.currentReadKeyHash = snapshotChange.CurrentReadKeyHash if snapshot.GetAclState().GetInvites() != nil { st.userInvites = snapshot.GetAclState().GetInvites() } @@ -101,21 +103,29 @@ func (st *ACLState) makeSnapshot() *pb.ACLChangeACLSnapshot { }} } -func (st *ACLState) applyChange(changeId string, change *pb.ACLChange) error { +func (st *ACLState) applyChange(change *pb.ACLChange) (err error) { + defer func() { + if err != nil { + return + } + st.currentReadKeyHash = change.CurrentReadKeyHash + }() // 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 if _, exists := st.userStates[change.Identity]; !exists { - return ErrNoSuchUser + err = ErrNoSuchUser + return } if !st.hasPermission(change.Identity, pb.ACLChange_Admin) { - return fmt.Errorf("user %s must have admin permissions", change.Identity) + err = fmt.Errorf("user %s must have admin permissions", change.Identity) + return } } for _, ch := range change.GetAclData().GetAclContent() { - if err := st.applyChangeContent(changeId, ch); err != nil { + if err = st.applyChangeContent(ch); err != nil { //log.Infof("error while applying changes: %v; ignore", err) return err } @@ -125,7 +135,7 @@ func (st *ACLState) applyChange(changeId string, change *pb.ACLChange) error { } // TODO: remove changeId, because it is not needed -func (st *ACLState) applyChangeContent(changeId string, ch *pb.ACLChangeACLContentValue) error { +func (st *ACLState) applyChangeContent(ch *pb.ACLChangeACLContentValue) error { switch { case ch.GetUserPermissionChange() != nil: return st.applyUserPermissionChange(ch.GetUserPermissionChange()) @@ -134,7 +144,7 @@ func (st *ACLState) applyChangeContent(changeId string, ch *pb.ACLChangeACLConte case ch.GetUserRemove() != nil: return st.applyUserRemove(ch.GetUserRemove()) case ch.GetUserInvite() != nil: - return st.applyUserInvite(changeId, ch.GetUserInvite()) + return st.applyUserInvite(ch.GetUserInvite()) case ch.GetUserJoin() != nil: return st.applyUserJoin(ch.GetUserJoin()) case ch.GetUserConfirm() != nil: @@ -153,15 +163,15 @@ func (st *ACLState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChan return nil } -func (st *ACLState) applyUserInvite(changeId string, ch *pb.ACLChangeUserInvite) error { - st.userInvites[changeId] = ch +func (st *ACLState) applyUserInvite(ch *pb.ACLChangeUserInvite) error { + st.userInvites[ch.InviteId] = ch return nil } func (st *ACLState) applyUserJoin(ch *pb.ACLChangeUserJoin) error { - invite, exists := st.userInvites[ch.UserInviteChangeId] + invite, exists := st.userInvites[ch.UserInviteId] if !exists { - return fmt.Errorf("no such invite with id %s", ch.UserInviteChangeId) + return fmt.Errorf("no such invite with id %s", ch.UserInviteId) } if _, exists = st.userStates[ch.Identity]; exists { @@ -190,7 +200,6 @@ func (st *ACLState) applyUserJoin(ch *pb.ACLChangeUserJoin) error { // if ourselves -> we need to decrypt the read keys if st.identity == ch.Identity { - var lastKeyHash uint64 for _, key := range ch.EncryptedReadKeys { key, hash, err := st.decryptReadKeyAndHash(key) if err != nil { @@ -198,9 +207,7 @@ func (st *ACLState) applyUserJoin(ch *pb.ACLChangeUserJoin) error { } st.userReadKeys[hash] = key - lastKeyHash = hash } - st.currentReadKeyHash = lastKeyHash } // adding user to the list @@ -227,6 +234,17 @@ func (st *ACLState) applyUserAdd(ch *pb.ACLChangeUserAdd) error { EncryptedReadKeys: ch.EncryptedReadKeys, } + if ch.Identity == st.identity { + for _, key := range ch.EncryptedReadKeys { + key, hash, err := st.decryptReadKeyAndHash(key) + if err != nil { + return ErrFailedToDecrypt + } + + st.userReadKeys[hash] = key + } + } + return nil } diff --git a/acltree/aclstatebuilder.go b/acltree/aclstatebuilder.go index 609ab793..7774d9c5 100644 --- a/acltree/aclstatebuilder.go +++ b/acltree/aclstatebuilder.go @@ -52,9 +52,8 @@ func (sb *aclStateBuilder) BuildBefore(beforeId string) (*ACLState, bool, error) return nil, false, fmt.Errorf("root should always be a snapshot") } - snapshot := root.Content.GetAclData().GetAclSnapshot() - state, err := newACLStateFromSnapshot( - snapshot, + state, err := newACLStateFromSnapshotChange( + root.Content, sb.identity, sb.key, sb.decoder) @@ -99,7 +98,7 @@ 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 = state.applyChange(c.Id, c.Content) + err = state.applyChange(c.Content) if err != nil { return false } diff --git a/acltree/acltree.go b/acltree/acltree.go index b7a677a3..46191d4c 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -30,7 +30,7 @@ type TreeUpdateListener interface { type ACLTree interface { ACLState() *ACLState - AddContent(f func(builder ChangeBuilder)) (*Change, error) + AddContent(f func(builder ChangeBuilder) error) (*Change, error) AddChanges(changes ...*Change) (AddResult, error) Heads() []string Root() *Change @@ -186,23 +186,21 @@ func (a *aclTree) ACLState() *ACLState { return a.aclState } -func (a *aclTree) AddContent(build func(builder ChangeBuilder)) (*Change, error) { +func (a *aclTree) AddContent(build func(builder ChangeBuilder) error) (*Change, error) { // TODO: add snapshot creation logic a.Lock() defer a.Unlock() 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) + err := build(a.changeBuilder) if err != nil { return nil, err } + ch, marshalled, err := a.changeBuilder.BuildAndApply() + if err != nil { + return nil, err + } a.fullTree.AddFast(ch) err = a.thread.AddRawChange(&thread.RawChange{ diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go index 294d4c6d..4a31250e 100644 --- a/acltree/changebuilder.go +++ b/acltree/changebuilder.go @@ -6,12 +6,14 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/gogo/protobuf/proto" + "github.com/textileio/go-threads/crypto/symmetric" + "hash/fnv" ) type MarshalledChange = []byte type ACLChangeBuilder interface { - UserAdd(identity string, encryptionKey keys.EncryptionPubKey, permissions pb.ACLChangeUserPermissions) + UserAdd(identity string, encryptionKey keys.EncryptionPubKey, permissions pb.ACLChangeUserPermissions) error AddId(id string) // TODO: this is only for testing SetMakeSnapshot(bool) // TODO: who should decide this? probably ACLTree so we can delete it } @@ -30,6 +32,8 @@ type changeBuilder struct { changeContent proto.Marshaler id string makeSnapshot bool + readKey *symmetric.Key + readKeyHash uint64 } func newChangeBuilder() *changeBuilder { @@ -42,6 +46,17 @@ func (c *changeBuilder) Init(state *ACLState, tree *Tree, acc *account.AccountDa c.acc = acc c.aclData = &pb.ACLChangeACLData{} + // setting read key for further encryption etc + if state.currentReadKeyHash == 0 { + c.readKey, _ = symmetric.NewRandom() + + hasher := fnv.New64() + hasher.Write(c.readKey.Bytes()) + c.readKeyHash = hasher.Sum64() + } else { + c.readKey = c.aclState.userReadKeys[c.aclState.currentReadKeyHash] + c.readKeyHash = c.aclState.currentReadKeyHash + } } func (c *changeBuilder) AddId(id string) { @@ -52,12 +67,44 @@ func (c *changeBuilder) SetMakeSnapshot(b bool) { c.makeSnapshot = b } -func (c *changeBuilder) UserAdd(identity string, encryptionKey keys.EncryptionPubKey, permissions pb.ACLChangeUserPermissions) { - //TODO implement me - panic("implement me") +func (c *changeBuilder) UserAdd(identity string, encryptionKey keys.EncryptionPubKey, permissions pb.ACLChangeUserPermissions) error { + var allKeys []*symmetric.Key + if c.aclState.currentReadKeyHash != 0 { + for _, key := range c.aclState.userReadKeys { + allKeys = append(allKeys, key) + } + } else { + allKeys = append(allKeys, c.readKey) + } + + var encryptedKeys [][]byte + for _, k := range allKeys { + res, err := encryptionKey.Encrypt(k.Bytes()) + if err != nil { + return err + } + + encryptedKeys = append(encryptedKeys, res) + } + rawKey, err := encryptionKey.Raw() + if err != nil { + return err + } + ch := &pb.ACLChangeACLContentValue{ + Value: &pb.ACLChangeACLContentValueValueOfUserAdd{ + UserAdd: &pb.ACLChangeUserAdd{ + Identity: identity, + EncryptionKey: rawKey, + EncryptedReadKeys: encryptedKeys, + Permissions: permissions, + }, + }, + } + c.aclData.AclContent = append(c.aclData.AclContent, ch) + return nil } -func (c *changeBuilder) Build() (*Change, []byte, error) { +func (c *changeBuilder) BuildAndApply() (*Change, []byte, error) { marshalled, err := c.changeContent.Marshal() if err != nil { return nil, nil, err @@ -69,10 +116,6 @@ func (c *changeBuilder) Build() (*Change, []byte, error) { return nil, nil, err } - if c.makeSnapshot { - c.aclData.AclSnapshot = c.aclState.makeSnapshot() - } - aclChange := &pb.ACLChange{ TreeHeadIds: c.tree.Heads(), AclHeadIds: c.tree.ACLHeads(), @@ -83,6 +126,14 @@ func (c *changeBuilder) Build() (*Change, []byte, error) { Timestamp: 0, Identity: c.acc.Identity, } + err = c.aclState.applyChange(aclChange) + if err != nil { + return nil, nil, err + } + + if c.makeSnapshot { + c.aclData.AclSnapshot = c.aclState.makeSnapshot() + } fullMarshalledChange, err := proto.Marshal(aclChange) if err != nil { diff --git a/acltree/snapshotvalidator.go b/acltree/snapshotvalidator.go index 8bf19451..f4479859 100644 --- a/acltree/snapshotvalidator.go +++ b/acltree/snapshotvalidator.go @@ -41,7 +41,7 @@ func (s *snapshotValidator) ValidateSnapshot(ch *Change) (bool, error) { 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 := newACLStateFromSnapshotChange(ch.Content, s.identity, s.key, s.decoder) if err != nil { return false, err } diff --git a/acltree/threadutility.go b/acltree/threadutility.go index da35c74c..8b847bfe 100644 --- a/acltree/threadutility.go +++ b/acltree/threadutility.go @@ -8,17 +8,20 @@ import ( func BuildThreadWithACL( acc *account.AccountData, - build func(builder ChangeBuilder), + build func(builder ChangeBuilder) error, create func(change *thread.RawChange) (thread.Thread, error)) (thread.Thread, error) { bld := newChangeBuilder() bld.Init( newACLState(acc.Identity, acc.EncKey, keys.NewEd25519Decoder()), &Tree{}, acc) - build(bld) + err := build(bld) + if err != nil { + return nil, err + } bld.SetMakeSnapshot(true) - change, payload, err := bld.Build() + change, payload, err := bld.BuildAndApply() if err != nil { return nil, err } diff --git a/plaintextdocument/document.go b/plaintextdocument/document.go index 2b030d6c..653388f9 100644 --- a/plaintextdocument/document.go +++ b/plaintextdocument/document.go @@ -27,13 +27,14 @@ func (p *plainTextDocument) Text() string { } func (p *plainTextDocument) AddText(text string) error { - _, err := p.aclTree.AddContent(func(builder acltree.ChangeBuilder) { + _, err := p.aclTree.AddContent(func(builder acltree.ChangeBuilder) error { builder.AddChangeContent( &pb.PlainTextChangeData{ Content: []*pb.PlainTextChangeContent{ createAppendTextChangeContent(text), }, }) + return nil }) return err } @@ -114,9 +115,13 @@ func NewPlainTextDocument( acc *account.AccountData, create func(change *thread.RawChange) (thread.Thread, error), text string) (PlainTextDocument, error) { - changeBuilder := func(builder acltree.ChangeBuilder) { - builder.UserAdd(acc.Identity, acc.EncKey.GetPublic(), aclpb.ACLChange_Admin) + changeBuilder := func(builder acltree.ChangeBuilder) error { + err := builder.UserAdd(acc.Identity, acc.EncKey.GetPublic(), aclpb.ACLChange_Admin) + if err != nil { + return err + } builder.AddChangeContent(createInitialChangeContent(text)) + return nil } t, err := acltree.BuildThreadWithACL( acc, diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 313f209c..9778a607 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -367,11 +367,11 @@ func (t *ThreadBuilder) parseACLChange(ch *ACLChange) (convCh *pb.ACLChangeACLCo convCh = &pb.ACLChangeACLContentValue{ Value: &pb.ACLChangeACLContentValueValueOfUserJoin{ UserJoin: &pb.ACLChangeUserJoin{ - Identity: t.keychain.GetIdentity(join.Identity), - EncryptionKey: rawKey, - AcceptSignature: signature, - UserInviteChangeId: join.InviteId, - EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), + Identity: t.keychain.GetIdentity(join.Identity), + EncryptionKey: rawKey, + AcceptSignature: signature, + UserInviteId: join.InviteId, + EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), }, }, } @@ -389,6 +389,7 @@ func (t *ThreadBuilder) parseACLChange(ch *ACLChange) (convCh *pb.ACLChangeACLCo EncryptPublicKey: rawEncKey, EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey), Permissions: t.convertPermission(invite.Permissions), + InviteId: invite.InviteId, }, }, } diff --git a/testutils/threadbuilder/ymlentities.go b/testutils/threadbuilder/ymlentities.go index 37e983b7..34d9cbaf 100644 --- a/testutils/threadbuilder/ymlentities.go +++ b/testutils/threadbuilder/ymlentities.go @@ -45,6 +45,7 @@ type ACLChange struct { EncryptionKey string `yaml:"encryptionKey"` EncryptedReadKeys []string `yaml:"encryptedReadKeys"` Permissions string `yaml:"permissions"` + InviteId string `yaml:"inviteId"` } `yaml:"userInvite"` UserConfirm *struct { diff --git a/testutils/yamltests/userjoinexample.yml b/testutils/yamltests/userjoinexample.yml index 60080ccc..2736d311 100644 --- a/testutils/yamltests/userjoinexample.yml +++ b/testutils/yamltests/userjoinexample.yml @@ -29,6 +29,7 @@ changes: encryptionKey: key.Enc.Onetime1 encryptedReadKeys: [key.Read.1] permissions: writer + inviteId: A.1.2 - userAdd: identity: C permission: reader diff --git a/testutils/yamltests/userjoinexampleupdate.yml b/testutils/yamltests/userjoinexampleupdate.yml index c3916cc6..04ea7450 100644 --- a/testutils/yamltests/userjoinexampleupdate.yml +++ b/testutils/yamltests/userjoinexampleupdate.yml @@ -29,6 +29,7 @@ changes: encryptionKey: key.Enc.Onetime1 encryptedReadKeys: [key.Read.1] permissions: writer + inviteId: A.1.2 - userAdd: identity: C permission: reader diff --git a/testutils/yamltests/userremoveexample.yml b/testutils/yamltests/userremoveexample.yml index 614a0687..bf1dfc57 100644 --- a/testutils/yamltests/userremoveexample.yml +++ b/testutils/yamltests/userremoveexample.yml @@ -29,6 +29,7 @@ changes: encryptionKey: key.Enc.Onetime1 encryptedReadKeys: [key.Read.1] permissions: writer + inviteId: A.1.2 readKey: key.Read.1 - id: A.1.3 identity: A From f27418127051905debb35870e6d8dd43677f8d05 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 21:27:27 +0200 Subject: [PATCH 50/53] Add test for thread utility --- acltree/aclstate.go | 11 +++++++- acltree/changebuilder.go | 34 ++++++++++++----------- acltree/threadutility.go | 9 ++++++- acltree/threadutility_test.go | 51 +++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 acltree/threadutility_test.go diff --git a/acltree/aclstate.go b/acltree/aclstate.go index b56142b6..9f9a5347 100644 --- a/acltree/aclstate.go +++ b/acltree/aclstate.go @@ -110,8 +110,11 @@ func (st *ACLState) applyChange(change *pb.ACLChange) (err error) { } st.currentReadKeyHash = change.CurrentReadKeyHash }() + // we can't check this for the user which is joining, because it will not be in our list - if !st.isUserJoin(change) { + // the same is for the first change to be added + skipIdentityCheck := st.isUserJoin(change) || (st.currentReadKeyHash == 0 && st.isUserAdd(change)) + if !skipIdentityCheck { // we check signature when we add this to the Tree, so no need to do it here if _, exists := st.userStates[change.Identity]; !exists { err = ErrNoSuchUser @@ -320,6 +323,12 @@ func (st *ACLState) isUserJoin(ch *pb.ACLChange) bool { return ch.AclData.GetAclContent() != nil && ch.AclData.GetAclContent()[0].GetUserJoin() != nil } +func (st *ACLState) isUserAdd(ch *pb.ACLChange) bool { + // if we have a UserAdd, then it should always be the first one applied + userAdd := ch.AclData.GetAclContent()[0].GetUserAdd() + return ch.AclData.GetAclContent() != nil && userAdd != nil && userAdd.GetIdentity() == ch.Identity +} + func (st *ACLState) getPermissionDecreasedUsers(ch *pb.ACLChange) (identities []*pb.ACLChangeUserPermissionChange) { // this should be called after general checks are completed if ch.GetAclData().GetAclContent() == nil { diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go index 4a31250e..1c97397c 100644 --- a/acltree/changebuilder.go +++ b/acltree/changebuilder.go @@ -8,6 +8,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/crypto/symmetric" "hash/fnv" + "time" ) type MarshalledChange = []byte @@ -105,28 +106,16 @@ func (c *changeBuilder) UserAdd(identity string, encryptionKey keys.EncryptionPu } func (c *changeBuilder) BuildAndApply() (*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(), AclData: c.aclData, - ChangesData: encrypted, - CurrentReadKeyHash: c.aclState.currentReadKeyHash, - Timestamp: 0, + CurrentReadKeyHash: c.readKeyHash, + Timestamp: int64(time.Now().Nanosecond()), Identity: c.acc.Identity, } - err = c.aclState.applyChange(aclChange) + err := c.aclState.applyChange(aclChange) if err != nil { return nil, nil, err } @@ -135,6 +124,21 @@ func (c *changeBuilder) BuildAndApply() (*Change, []byte, error) { c.aclData.AclSnapshot = c.aclState.makeSnapshot() } + var marshalled []byte + if c.changeContent != nil { + 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.ChangesData = encrypted + } + fullMarshalledChange, err := proto.Marshal(aclChange) if err != nil { return nil, nil, err diff --git a/acltree/threadutility.go b/acltree/threadutility.go index 8b847bfe..034ea2fd 100644 --- a/acltree/threadutility.go +++ b/acltree/threadutility.go @@ -31,5 +31,12 @@ func BuildThreadWithACL( Signature: change.Signature(), Id: change.CID(), } - return create(rawChange) + + thr, err := create(rawChange) + if err != nil { + return nil, err + } + + thr.SetHeads([]string{change.CID()}) + return thr, nil } diff --git a/acltree/threadutility_test.go b/acltree/threadutility_test.go new file mode 100644 index 00000000..91258127 --- /dev/null +++ b/acltree/threadutility_test.go @@ -0,0 +1,51 @@ +package acltree + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestBuildThreadWithACL(t *testing.T) { + keychain := threadbuilder.NewKeychain() + keychain.AddSigningKey("A") + keychain.AddEncryptionKey("A") + data := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + thr, err := BuildThreadWithACL( + data, + func(builder ChangeBuilder) error { + return builder.UserAdd( + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"].GetPublic(), + pb.ACLChange_Admin) + }, + thread.NewInMemoryThread) + if err != nil { + t.Fatalf("build should not return error") + } + if len(thr.Heads()) == 0 { + t.Fatalf("thread should have non-empty heads") + } + if thr.Header() == nil { + t.Fatalf("thread should have non-empty header") + } + assert.Equal(t, thr.Heads()[0], thr.Header().FirstChangeId) + assert.NotEmpty(t, thr.ID()) + ch, err := thr.GetChange(context.Background(), thr.Header().FirstChangeId) + if err != nil { + t.Fatalf("get change should not return error: %v", err) + } + + _, err = NewFromRawChange(ch) + if err != nil { + t.Fatalf("we should be able to unmarshall change: %v", err) + } +} From d743317069b111c887b06fcf858640c6a08a5427 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 23:07:05 +0200 Subject: [PATCH 51/53] Add document tests --- acltree/acltree.go | 34 ++++++----- acltree/treebuilder.go | 3 + plaintextdocument/document.go | 5 +- plaintextdocument/document_test.go | 97 ++++++++++++++---------------- 4 files changed, 73 insertions(+), 66 deletions(-) diff --git a/acltree/acltree.go b/acltree/acltree.go index 46191d4c..82aa688c 100644 --- a/acltree/acltree.go +++ b/acltree/acltree.go @@ -54,7 +54,7 @@ type aclTree struct { snapshotValidator *snapshotValidator changeBuilder *changeBuilder - sync.Mutex + sync.RWMutex } func BuildACLTree( @@ -86,6 +86,7 @@ func BuildACLTree( } aclTree.removeOrphans() t.SetHeads(aclTree.Heads()) + listener.Rebuild(aclTree) return aclTree, nil } @@ -183,13 +184,19 @@ func (a *aclTree) rebuildFromThread(fromStart bool) error { } func (a *aclTree) ACLState() *ACLState { + a.RLock() + defer a.RUnlock() return a.aclState } func (a *aclTree) AddContent(build func(builder ChangeBuilder) error) (*Change, error) { // TODO: add snapshot creation logic a.Lock() - defer a.Unlock() + defer func() { + a.Unlock() + // TODO: should this be called in a separate goroutine to prevent accidental cycles (tree->updater->tree) + a.updateListener.Update(a) + }() a.changeBuilder.Init(a.aclState, a.fullTree, a.accountData) err := build(a.changeBuilder) @@ -213,13 +220,11 @@ func (a *aclTree) AddContent(build func(builder ChangeBuilder) error) (*Change, } a.thread.SetHeads([]string{ch.Id}) - a.updateListener.Update(a) return ch, nil } 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 err error var mode Mode @@ -230,6 +235,7 @@ func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { } a.removeOrphans() a.thread.SetHeads(a.fullTree.Heads()) + a.Unlock() switch mode { case Append: a.updateListener.Update(a) @@ -286,20 +292,20 @@ func (a *aclTree) AddChanges(changes ...*Change) (AddResult, error) { } func (a *aclTree) Iterate(f func(change *Change) bool) { - a.Lock() - defer a.Unlock() + a.RLock() + defer a.RUnlock() a.fullTree.Iterate(a.fullTree.RootId(), f) } func (a *aclTree) IterateFrom(s string, f func(change *Change) bool) { - a.Lock() - defer a.Unlock() + a.RLock() + defer a.RUnlock() a.fullTree.Iterate(s, f) } func (a *aclTree) HasChange(s string) bool { - a.Lock() - defer a.Unlock() + a.RLock() + defer a.RUnlock() _, attachedExists := a.fullTree.attached[s] _, unattachedExists := a.fullTree.unAttached[s] _, invalidExists := a.fullTree.invalidChanges[s] @@ -307,13 +313,13 @@ func (a *aclTree) HasChange(s string) bool { } func (a *aclTree) Heads() []string { - a.Lock() - defer a.Unlock() + a.RLock() + defer a.RUnlock() return a.fullTree.Heads() } func (a *aclTree) Root() *Change { - a.Lock() - defer a.Unlock() + a.RLock() + defer a.RUnlock() return a.fullTree.Root() } diff --git a/acltree/treebuilder.go b/acltree/treebuilder.go index 944a44e1..1a34a3ae 100644 --- a/acltree/treebuilder.go +++ b/acltree/treebuilder.go @@ -169,6 +169,9 @@ func (tb *treeBuilder) findBreakpoint(heads []string) (breakpoint string, err er return } shId := ch.SnapshotId + if ch.IsSnapshot { + shId = ch.Id + } if slice.FindPos(snapshotIds, shId) == -1 { snapshotIds = append(snapshotIds, shId) } diff --git a/plaintextdocument/document.go b/plaintextdocument/document.go index 653388f9..63c07bde 100644 --- a/plaintextdocument/document.go +++ b/plaintextdocument/document.go @@ -23,7 +23,10 @@ type plainTextDocument struct { } func (p *plainTextDocument) Text() string { - return p.state.Text + if p.state != nil { + return p.state.Text + } + return "" } func (p *plainTextDocument) AddText(text string) error { diff --git a/plaintextdocument/document_test.go b/plaintextdocument/document_test.go index 0532240e..af596e54 100644 --- a/plaintextdocument/document_test.go +++ b/plaintextdocument/document_test.go @@ -1,53 +1,48 @@ package plaintextdocument -// -//import ( -// "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" -// "github.com/stretchr/testify/assert" -// "testing" -//) -// -//func TestDocument_Build(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") -// if err != nil { -// t.Fatal(err) -// } -// keychain := thread.GetKeychain() -// accountData := &AccountData{ -// Identity: keychain.GetIdentity("A"), -// EncKey: keychain.EncryptionKeys["A"], -// } -// doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) -// res, err := doc.Build() -// if err != nil { -// t.Fatal(err) -// } -// -// st := res.(*DocumentState) -// assert.Equal(t, st.Text, "some text|first") -//} -// -//func TestDocument_Update(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") -// if err != nil { -// t.Fatal(err) -// } -// keychain := thread.GetKeychain() -// accountData := &AccountData{ -// Identity: keychain.GetIdentity("A"), -// EncKey: keychain.EncryptionKeys["A"], -// } -// doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData) -// res, err := doc.Build() -// if err != nil { -// t.Fatal(err) -// } -// -// st := res.(*DocumentState) -// assert.Equal(t, st.Text, "some text|first") -// -// rawChs := thread.GetUpdatedChanges() -// res, updateResult, err := doc.Update(rawChs...) -// assert.Equal(t, updateResult, UpdateResultAppend) -// assert.Equal(t, res.(*DocumentState).Text, "some text|first|second") -//} +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestDocument_NewPlainTextDocument(t *testing.T) { + keychain := threadbuilder.NewKeychain() + keychain.AddSigningKey("A") + keychain.AddEncryptionKey("A") + data := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + + doc, err := NewPlainTextDocument(data, thread.NewInMemoryThread, "Some text") + if err != nil { + t.Fatalf("should not create document with error: %v", err) + } + assert.Equal(t, doc.Text(), "Some text") +} + +func TestDocument_PlainTextDocument_AddText(t *testing.T) { + keychain := threadbuilder.NewKeychain() + keychain.AddSigningKey("A") + keychain.AddEncryptionKey("A") + data := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + + doc, err := NewPlainTextDocument(data, thread.NewInMemoryThread, "Some text") + if err != nil { + t.Fatalf("should not create document with error: %v", err) + } + + err = doc.AddText("Next") + if err != nil { + t.Fatalf("should be able to add document: %v", err) + } + assert.Equal(t, doc.Text(), "Some text|Next") +} From 3643719161e7a73d80e58c314275917914c37f3f Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 23:13:51 +0200 Subject: [PATCH 52/53] Improve test --- plaintextdocument/document_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plaintextdocument/document_test.go b/plaintextdocument/document_test.go index af596e54..204115a0 100644 --- a/plaintextdocument/document_test.go +++ b/plaintextdocument/document_test.go @@ -45,4 +45,10 @@ func TestDocument_PlainTextDocument_AddText(t *testing.T) { t.Fatalf("should be able to add document: %v", err) } assert.Equal(t, doc.Text(), "Some text|Next") + + err = doc.AddText("Shmext") + if err != nil { + t.Fatalf("should be able to add document: %v", err) + } + assert.Equal(t, doc.Text(), "Some text|Next|Shmext") } From 5b3f594b2b0e0fb12f8983c8d69723e9e474da64 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Wed, 13 Jul 2022 09:00:10 +0200 Subject: [PATCH 53/53] Remove unneded plaintextdocument file --- plaintextdocument/plaintextdocument.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 plaintextdocument/plaintextdocument.go diff --git a/plaintextdocument/plaintextdocument.go b/plaintextdocument/plaintextdocument.go deleted file mode 100644 index d033fb38..00000000 --- a/plaintextdocument/plaintextdocument.go +++ /dev/null @@ -1 +0,0 @@ -package plaintextdocument