diff --git a/Makefile b/Makefile index 82cb14de..f7c161d1 100644 --- a/Makefile +++ b/Makefile @@ -13,25 +13,22 @@ endif export PATH=$(GOPATH)/bin:$(shell echo $$PATH) # TODO: folders were changed, so we should update Makefile and protos generation -protos-go: +proto: @echo 'Generating protobuf packages (Go)...' # Uncomment if needed @$(eval ROOT_PKG := pkg) @$(eval GOGO_START := GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1) - @$(eval P_TREE_STORAGE_PATH_PB := $(ROOT_PKG)/acl/treestorage/treepb) @$(eval P_ACL_CHANGES_PATH_PB := $(ROOT_PKG)/acl/aclchanges/aclpb) - @$(eval P_PLAINTEXT_CHANGES_PATH_PB := $(ROOT_PKG)/acl/testutils/testchanges/testchangepb) @$(eval P_SYNC_CHANGES_PATH_PB := syncproto) + @$(eval P_TEST_CHANGES_PATH_PB := $(ROOT_PKG)/acl/testutils/testchanges) @$(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 := M$(P_ACL_CHANGES_PATH_PB)/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/$(P_ACL_CHANGES_PATH_PB)) - @$(eval P_TREE_CHANGES := M$(P_TREE_STORAGE_PATH_PB)/protos/tree.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/$(P_TREE_STORAGE_PATH_PB)) # use if needed $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT)) $(GOGO_START) protoc --gogofaster_out=:. $(P_ACL_CHANGES_PATH_PB)/protos/*.proto; mv $(P_ACL_CHANGES_PATH_PB)/protos/*.go $(P_ACL_CHANGES_PATH_PB) - $(GOGO_START) protoc --gogofaster_out=:. $(P_TREE_STORAGE_PATH_PB)/protos/*.proto; mv $(P_TREE_STORAGE_PATH_PB)/protos/*.go $(P_TREE_STORAGE_PATH_PB) - $(GOGO_START) protoc --gogofaster_out=:. $(P_PLAINTEXT_CHANGES_PATH_PB)/protos/*.proto; mv $(P_PLAINTEXT_CHANGES_PATH_PB)/protos/*.go $(P_PLAINTEXT_CHANGES_PATH_PB) - $(eval PKGMAP := $$(P_ACL_CHANGES),$$(P_TREE_CHANGES)) + $(GOGO_START) protoc --gogofaster_out=:. $(P_TEST_CHANGES_PATH_PB)/proto/*.proto + $(eval PKGMAP := $$(P_ACL_CHANGES)) $(GOGO_START) protoc --gogofaster_out=$(PKGMAP):. $(P_SYNC_CHANGES_PATH_PB)/proto/*.proto $(GOGO_START) protoc --gogofaster_out=$(PKGMAP):. service/space/spacesync/protos/*.proto diff --git a/cmd/node/node.go b/cmd/node/node.go index d9433a35..79f1b891 100644 --- a/cmd/node/node.go +++ b/cmd/node/node.go @@ -10,12 +10,13 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/service/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/api" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/configuration" + "github.com/anytypeio/go-anytype-infrastructure-experiments/service/document" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/dialer" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/pool" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/rpc/server" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/secure" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/node" - "github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/document" + "github.com/anytypeio/go-anytype-infrastructure-experiments/service/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/message" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/requesthandler" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache" @@ -98,6 +99,7 @@ func Bootstrap(a *app.App) { Register(server.New()). Register(dialer.New()). Register(pool.NewPool()). + Register(storage.New()). Register(configuration.New()). Register(document.New()). Register(message.New()). diff --git a/etc/acl.yml b/etc/acl.yml new file mode 100644 index 00000000..a9a44268 --- /dev/null +++ b/etc/acl.yml @@ -0,0 +1,28 @@ +records: + - identity: A + 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 +keys: + Enc: + - name: A + value: JgG4CcCbae1qEpe7mKpBzsHjZhXUmDSNVNX2B1gxFZsJyMX4V6kBQUott9zRWyeXaW1ZmpzuxDXnwSQpAnNurhXyGa9iQaAPqzY9A9VWBPD33Yy1eW7TRuVemzToh8jJQKQKnZNbF8ucTWV9qahusKzyvN8uyhrqoW2tAPfA9S3E3ognCuqbLSW6yjE2rBKayvyS1BVwzjSd6FZK4DDyjfU3pbEVjut3wytGEAn9af6sNMmyCnf2MX5vLovWs9rU8av61wD4z7HTsXyGFx4K75N4Go249Hpe9SKAT6HxhRc3yvj63krPLiQV5yMuH2UeMUXBDekUQyNmBEdn9wrur7mLqB67Bc6tcc2PP8XApBCdWJHvHjN4FktSpaG5vbCqoZbLD1oCbk36q2x9s6XM8pydVqD1J9P3nTbfgMb5pJCTFjNtgKeuKv6wjfJeA9jF1VhcJQisfsahgv9MvZ9M8FJpZTq1zKUhYDCRnZxUkraoMS5yNNVdDzaUckKEDthqik7BMWCWT79vq7uVgMwEvGwGi76gtoMg1159bbPMLZ4bdPVfhH2S9QjPrzQfwZSrzB2YeVPjWpaXDeLDity5H8n1NK2oniAQR6gE71n81neSptsuhV6o6QpQ89AU8y57XmEsou4VEryn8vUxBHhULLxrLNUouxyWamCeFiDjk5cSN6koQsf9BYKSNTPFTrwjTKForDokMhcPdMtFktKwjv7u9UEGcY4MKvNzZZkc77gHiP8bqVtdNNoLpTFUC5SZ9i7bKdHvK12HpSy7yzzPeMXJ9UwhLxkok1g81ngTbN1yxRhvYXyHZFtguCR9kvGojDjka91MTBtk551qDw9eCn2xZT9U8jqzBCjdpvSg3mRWKMPnYAGB7m7u1ye165wyGFvzcHAx3vtXjxAqLUeKYZCjv2m6V9D2Y4qH1TQNddWqH14T1JVMis971UCH9Ddpj6a3387oUnufD1P6HZN2ieJCvptrmbGVvxJYYSvmVf1dkwbtqurDRNWD7TJ7gf6iqSP549C9bxP4GpLt3ygjHmMtcuUzstBuztvunJUnQhfnJxqU6LjRdsFzm53wGWgXNxab7ZvQcPyLwsevn1b98FGPnVpS5iY4LjmqW4ugrC6HgrbsjrXiKzR1yZKhLQkCbLzPoaHb8iB5iBnCr7d4yf5CtfpFRqgoqMFdK5LNZYmDX4HzUKN6A7wC3gGiSRFTLcgGZeSMkB5Pa61CZBU7WCQgFxykycE9HRA7PiQa496GWDCV15teToCpFRsAa6jDmR1MGXPeLRqQgve49VXnQN5FL7c1VuEv5SWjeTuCnMB47DJKBaP7eKJNKgLwETALzSCMF3nRiRgeb15kfoS4BbrJ5yupjrvwmbmvNg1AYFFS5sYNWft7K8v87wQvBakRtGP71Kp8NX77XFtu6xdB7sR6jpfC6qJPyB9akWNXgCrWy9kE4ih42gwAZdUugNZ9YtEsgRM3pwb6qJhkAPyEJtrxrja859PCAgqPSQiPQN33PaMkgQ6HJknu8CrjKRiXAycZ16KLUkHV64TNhEjPTcX1a7rqpD131AYMWX8d7CCdc9Ys7RUb6BwguuNSh8rJK3x4AkMDSUsaE8ynKvpC7RXZpJ9Nxfhd + - name: B + value: JgG4CcCbae1qEpe7mKXzp7m5hNc56SSyZd9DwUaEStKJrq7RToAC2Vgd3i6hKRwa58zCWeN6Wjc3o6qrdKPEPRvcyEPysamajVo5mdQiUgWAmr97pGEsyjuRjQoC2GY2LvLiEQxEgwFgJxKGMHMiaWMtDfxCDUaDEm4bu5RdMhqRZekAWho6c3WoEeruSr14iX1TrocFNfBkBY7CjEw8kcywXCTNgtvhb2Qiwgj5AxEF4wyw4bzaNA9ctXb1hoHPFVMu6C51pkFY7jUD9zwyH3ukgnAewkGAcPNbKmaTAtMosKRVaAN97mAwXh2VRt1hWmRvVk7r76EjnVKhD4vbsKZc56RVcHTVWRVdhU7FGyPsiE5rSQAz1JQGYzxnZpX7EG77CyrmUGyfueVfRHhwY2oq8A4uQCRaQxSaJHYLowjXSxh8DQ2V6MTqyzti32C27utBYdHzLVCJSGkmdzGwrFcHqsq7nLDxmvJVErPvyReixEe8kFmqopJ3e6LLm8WdYw9K6JYBjXnEfwPzm7Von9sf3dcaGDUHYfttMyeke7fAXJkvPRje69hYVyzdQGAauuojzGkkvQWCSMK1KCMNMznRaPDCNvofrQhYrub24WhmwpKhorufdfW8Cb4T6reBDCtaWVsbuinjtL6F6Sui5aYHJFLJ6e4pPewr1P4EuZYRbMBZwN5KvDLhTGLBuBnaTqUUdF6bj2U22NoRYMogiHiftqKqiexKNDXX1Zg9RQEvxgjuVo6SBW42mVEA8agrLhruRqCmiduJxVrfqLNGeYXHXrcmMEgW7uosJbPXvTcfRvdFWS1ov7oSALvj6vhDQ28Yi9D2ETNdNsfVWAFQuwvPpW7CHQGXTitprVbqH8JYxNZuGygcLmr5efbB22Vzu4ntd1HoraQpG12qeDEUA7tXYUpoYyuSdWwKPjSAMtaQcCSfVrhKQHQuKJargrVrez8vjWuwLfvSucV7ZHe7gjqvYgULdE1ubRCRSd7DuLjEN2Vd6obzV2c3MRet7ZSf4Sp88WM5AuTyW7BjArBc4S3gUQ8rYaiZ8Tu7NCxkEzbFwWRaemZkwfvcsX3XxqjyF37tFSGkEqE5kuBvpZW72675LkDffj7kH1zA8yE6dVujJjWsNYVFJWndUtz5Vy2KCdZAbBgq19q4AtsxWPodU2N3yZXzFAFAzTrxS6V4P7Scpdau1avgRvHLcBQPunA37xaYMy8YMifJwtmRY25mnAQwZAk3eANk7tXwZd58SDnciLNvARJvwKzTQBXcshkwyy52SX8XmXDJsPnRLaHmiYBJ63Yzr5XpZuuAtxb9qrWG2NHCNxfomHokWacV1hjZPPd6ZxT1FuRozB6Qt2NLcyqY7bnTcQJb1jPUaTAGXXCR8WVmmmYo2fDQe8CdBmgyPvbzNTEJUyScBz4RdycB5PZap4SurJCWtHbuMyQbQUB6jJgURDstfXS5Akfe4oruNq9rnYcNtnsDJPtrhXHBqzDizmf1BDxR5FB2RCxzCgeAfg8WQ1Ug9PVAGTzob6ZqCrGXzWXEUniZnf1vjr7QhGKBYXEX9SWDoSMUpP4FreVDTnx15ijRZTV3p8xG5fE9e36TnugRVvTyq7XzmyPBjW2r66f1bior + Sign: + - name: A + value: 3id6ddLcoNoe9rDgGM88ET8T6TnvHm5GFqFdN6kBzn7Q8d6VUGgjeT59CNWFiaofdeRnHBvX2A5ZacMXvfwaYEFuCbug + - name: B + value: 3iiLPj6wMUQpPwTBNZcUgkbXub1jumg4AEV9LfMyFHZVc84GLyAjVbVvH6EAGhcNrxRxL82aW4BimhDZCpLsRCqx5vwj + Read: + - name: 1 + value: bamccoi5jdypwnjkiuuogkawvhkbowha4qg756uhnbkecr5vt3h4q diff --git a/etc/path.go b/etc/path.go new file mode 100644 index 00000000..a90b9125 --- /dev/null +++ b/etc/path.go @@ -0,0 +1,15 @@ +package etc + +import ( + "path/filepath" + "runtime" +) + +var ( + _, b, _, _ = runtime.Caller(0) + basepath = filepath.Dir(b) +) + +func Path() string { + return basepath +} diff --git a/pkg/acl/account/accountdata.go b/pkg/acl/account/accountdata.go index 7b0c773b..07530083 100644 --- a/pkg/acl/account/accountdata.go +++ b/pkg/acl/account/accountdata.go @@ -1,6 +1,7 @@ package account import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" ) @@ -9,5 +10,5 @@ type AccountData struct { // TODO: create a convenient constructor for this Identity string // TODO: this is essentially the same as sign key SignKey signingkey.PrivKey EncKey encryptionkey.PrivKey - Decoder signingkey.PubKeyDecoder + Decoder keys.Decoder } diff --git a/pkg/acl/aclchanges/aclpb/aclchanges.pb.go b/pkg/acl/aclchanges/aclpb/aclchanges.pb.go index 4a892fdf..c026319c 100644 --- a/pkg/acl/aclchanges/aclpb/aclchanges.pb.go +++ b/pkg/acl/aclchanges/aclpb/aclchanges.pb.go @@ -50,7 +50,32 @@ func (x ACLChangeUserPermissions) String() string { } func (ACLChangeUserPermissions) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_37a022c841a51877, []int{1, 0} + return fileDescriptor_37a022c841a51877, []int{2, 0} +} + +type HeaderDocType int32 + +const ( + Header_ACL HeaderDocType = 0 + Header_DocTree HeaderDocType = 1 +) + +var HeaderDocType_name = map[int32]string{ + 0: "ACL", + 1: "DocTree", +} + +var HeaderDocType_value = map[string]int32{ + "ACL": 0, + "DocTree": 1, +} + +func (x HeaderDocType) String() string { + return proto.EnumName(HeaderDocType_name, int32(x)) +} + +func (HeaderDocType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_37a022c841a51877, []int{5, 0} } type RawChange struct { @@ -113,6 +138,66 @@ func (m *RawChange) GetId() string { return "" } +type RawRecord struct { + Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *RawRecord) Reset() { *m = RawRecord{} } +func (m *RawRecord) String() string { return proto.CompactTextString(m) } +func (*RawRecord) ProtoMessage() {} +func (*RawRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_37a022c841a51877, []int{1} +} +func (m *RawRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RawRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RawRecord.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 *RawRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_RawRecord.Merge(m, src) +} +func (m *RawRecord) XXX_Size() int { + return m.Size() +} +func (m *RawRecord) XXX_DiscardUnknown() { + xxx_messageInfo_RawRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_RawRecord proto.InternalMessageInfo + +func (m *RawRecord) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *RawRecord) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *RawRecord) GetId() string { + if m != nil { + return m.Id + } + return "" +} + // 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"` @@ -130,7 +215,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_37a022c841a51877, []int{1} + return fileDescriptor_37a022c841a51877, []int{2} } func (m *ACLChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -230,7 +315,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_37a022c841a51877, []int{1, 0} + return fileDescriptor_37a022c841a51877, []int{2, 0} } func (m *ACLChangeACLContentValue) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -361,7 +446,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_37a022c841a51877, []int{1, 1} + return fileDescriptor_37a022c841a51877, []int{2, 1} } func (m *ACLChangeACLData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -413,7 +498,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_37a022c841a51877, []int{1, 2} + return fileDescriptor_37a022c841a51877, []int{2, 2} } func (m *ACLChangeACLSnapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -459,7 +544,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_37a022c841a51877, []int{1, 3} + return fileDescriptor_37a022c841a51877, []int{2, 3} } func (m *ACLChangeACLState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -521,7 +606,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_37a022c841a51877, []int{1, 4} + return fileDescriptor_37a022c841a51877, []int{2, 4} } func (m *ACLChangeUserState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -597,7 +682,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_37a022c841a51877, []int{1, 5} + return fileDescriptor_37a022c841a51877, []int{2, 5} } func (m *ACLChangeUserAdd) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -664,7 +749,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_37a022c841a51877, []int{1, 6} + return fileDescriptor_37a022c841a51877, []int{2, 6} } func (m *ACLChangeUserConfirm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -719,7 +804,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_37a022c841a51877, []int{1, 7} + return fileDescriptor_37a022c841a51877, []int{2, 7} } func (m *ACLChangeUserInvite) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -795,7 +880,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_37a022c841a51877, []int{1, 8} + return fileDescriptor_37a022c841a51877, []int{2, 8} } func (m *ACLChangeUserJoin) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -868,7 +953,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_37a022c841a51877, []int{1, 9} + return fileDescriptor_37a022c841a51877, []int{2, 9} } func (m *ACLChangeUserRemove) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -921,7 +1006,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_37a022c841a51877, []int{1, 10} + return fileDescriptor_37a022c841a51877, []int{2, 10} } func (m *ACLChangeReadKeyReplace) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -980,7 +1065,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_37a022c841a51877, []int{1, 11} + return fileDescriptor_37a022c841a51877, []int{2, 11} } func (m *ACLChangeUserPermissionChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1023,9 +1108,255 @@ func (m *ACLChangeUserPermissionChange) GetPermissions() ACLChangeUserPermission return ACLChange_Admin } +type Change struct { + TreeHeadIds []string `protobuf:"bytes,1,rep,name=treeHeadIds,proto3" json:"treeHeadIds,omitempty"` + AclHeadId string `protobuf:"bytes,2,opt,name=aclHeadId,proto3" json:"aclHeadId,omitempty"` + SnapshotBaseId string `protobuf:"bytes,3,opt,name=snapshotBaseId,proto3" json:"snapshotBaseId,omitempty"` + ChangesData []byte `protobuf:"bytes,4,opt,name=changesData,proto3" json:"changesData,omitempty"` + CurrentReadKeyHash uint64 `protobuf:"varint,5,opt,name=currentReadKeyHash,proto3" json:"currentReadKeyHash,omitempty"` + Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Identity string `protobuf:"bytes,7,opt,name=identity,proto3" json:"identity,omitempty"` + IsSnapshot bool `protobuf:"varint,8,opt,name=isSnapshot,proto3" json:"isSnapshot,omitempty"` +} + +func (m *Change) Reset() { *m = Change{} } +func (m *Change) String() string { return proto.CompactTextString(m) } +func (*Change) ProtoMessage() {} +func (*Change) Descriptor() ([]byte, []int) { + return fileDescriptor_37a022c841a51877, []int{3} +} +func (m *Change) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Change) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Change.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 *Change) XXX_Merge(src proto.Message) { + xxx_messageInfo_Change.Merge(m, src) +} +func (m *Change) XXX_Size() int { + return m.Size() +} +func (m *Change) XXX_DiscardUnknown() { + xxx_messageInfo_Change.DiscardUnknown(m) +} + +var xxx_messageInfo_Change proto.InternalMessageInfo + +func (m *Change) GetTreeHeadIds() []string { + if m != nil { + return m.TreeHeadIds + } + return nil +} + +func (m *Change) GetAclHeadId() string { + if m != nil { + return m.AclHeadId + } + return "" +} + +func (m *Change) GetSnapshotBaseId() string { + if m != nil { + return m.SnapshotBaseId + } + return "" +} + +func (m *Change) GetChangesData() []byte { + if m != nil { + return m.ChangesData + } + return nil +} + +func (m *Change) GetCurrentReadKeyHash() uint64 { + if m != nil { + return m.CurrentReadKeyHash + } + return 0 +} + +func (m *Change) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *Change) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *Change) GetIsSnapshot() bool { + if m != nil { + return m.IsSnapshot + } + return false +} + +type Record struct { + PrevId string `protobuf:"bytes,1,opt,name=prevId,proto3" json:"prevId,omitempty"` + Identity string `protobuf:"bytes,2,opt,name=identity,proto3" json:"identity,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + CurrentReadKeyHash uint64 `protobuf:"varint,4,opt,name=currentReadKeyHash,proto3" json:"currentReadKeyHash,omitempty"` + Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *Record) Reset() { *m = Record{} } +func (m *Record) String() string { return proto.CompactTextString(m) } +func (*Record) ProtoMessage() {} +func (*Record) Descriptor() ([]byte, []int) { + return fileDescriptor_37a022c841a51877, []int{4} +} +func (m *Record) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Record) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Record.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 *Record) XXX_Merge(src proto.Message) { + xxx_messageInfo_Record.Merge(m, src) +} +func (m *Record) XXX_Size() int { + return m.Size() +} +func (m *Record) XXX_DiscardUnknown() { + xxx_messageInfo_Record.DiscardUnknown(m) +} + +var xxx_messageInfo_Record proto.InternalMessageInfo + +func (m *Record) GetPrevId() string { + if m != nil { + return m.PrevId + } + return "" +} + +func (m *Record) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *Record) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *Record) GetCurrentReadKeyHash() uint64 { + if m != nil { + return m.CurrentReadKeyHash + } + return 0 +} + +func (m *Record) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type Header struct { + FirstId string `protobuf:"bytes,1,opt,name=firstId,proto3" json:"firstId,omitempty"` + AclListId string `protobuf:"bytes,2,opt,name=aclListId,proto3" json:"aclListId,omitempty"` + WorkspaceId string `protobuf:"bytes,3,opt,name=workspaceId,proto3" json:"workspaceId,omitempty"` + DocType HeaderDocType `protobuf:"varint,4,opt,name=docType,proto3,enum=acl.HeaderDocType" json:"docType,omitempty"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { + return fileDescriptor_37a022c841a51877, []int{5} +} +func (m *Header) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Header.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 *Header) XXX_Merge(src proto.Message) { + xxx_messageInfo_Header.Merge(m, src) +} +func (m *Header) XXX_Size() int { + return m.Size() +} +func (m *Header) XXX_DiscardUnknown() { + xxx_messageInfo_Header.DiscardUnknown(m) +} + +var xxx_messageInfo_Header proto.InternalMessageInfo + +func (m *Header) GetFirstId() string { + if m != nil { + return m.FirstId + } + return "" +} + +func (m *Header) GetAclListId() string { + if m != nil { + return m.AclListId + } + return "" +} + +func (m *Header) GetWorkspaceId() string { + if m != nil { + return m.WorkspaceId + } + return "" +} + +func (m *Header) GetDocType() HeaderDocType { + if m != nil { + return m.DocType + } + return Header_ACL +} + func init() { proto.RegisterEnum("acl.ACLChangeUserPermissions", ACLChangeUserPermissions_name, ACLChangeUserPermissions_value) + proto.RegisterEnum("acl.HeaderDocType", HeaderDocType_name, HeaderDocType_value) proto.RegisterType((*RawChange)(nil), "acl.RawChange") + proto.RegisterType((*RawRecord)(nil), "acl.RawRecord") proto.RegisterType((*ACLChange)(nil), "acl.ACLChange") proto.RegisterType((*ACLChangeACLContentValue)(nil), "acl.ACLChange.ACLContentValue") proto.RegisterType((*ACLChangeACLData)(nil), "acl.ACLChange.ACLData") @@ -1040,6 +1371,9 @@ func init() { proto.RegisterType((*ACLChangeUserRemove)(nil), "acl.ACLChange.UserRemove") proto.RegisterType((*ACLChangeReadKeyReplace)(nil), "acl.ACLChange.ReadKeyReplace") proto.RegisterType((*ACLChangeUserPermissionChange)(nil), "acl.ACLChange.UserPermissionChange") + proto.RegisterType((*Change)(nil), "acl.Change") + proto.RegisterType((*Record)(nil), "acl.Record") + proto.RegisterType((*Header)(nil), "acl.Header") } func init() { @@ -1047,67 +1381,78 @@ func init() { } var fileDescriptor_37a022c841a51877 = []byte{ - // 948 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4f, 0x6f, 0xe3, 0x44, - 0x14, 0xf7, 0xc4, 0x4d, 0x1d, 0x3f, 0x87, 0x36, 0x0c, 0x2b, 0xd6, 0x58, 0x4b, 0x88, 0xca, 0x0a, - 0x45, 0x08, 0xa5, 0xab, 0xac, 0x90, 0x56, 0x80, 0x2a, 0xda, 0x82, 0x36, 0xa1, 0x1c, 0x56, 0x53, - 0x2d, 0x08, 0x6e, 0x53, 0x7b, 0x68, 0xad, 0x75, 0x6c, 0xe3, 0x99, 0x14, 0xe5, 0x82, 0xc4, 0x89, - 0x2b, 0x67, 0xbe, 0x0a, 0x5f, 0x60, 0x8f, 0x7b, 0xe4, 0x06, 0x6a, 0xef, 0x5c, 0xf8, 0x02, 0x68, - 0xfe, 0xd8, 0x71, 0x12, 0x6f, 0x24, 0xa4, 0x15, 0x12, 0x87, 0x4a, 0x33, 0xbf, 0xf7, 0x7b, 0xd3, - 0xf7, 0xde, 0xef, 0xbd, 0x17, 0xc3, 0x83, 0xfc, 0xd9, 0xe5, 0x21, 0x0d, 0x13, 0xf9, 0x17, 0x5e, - 0xd1, 0xf4, 0x92, 0x71, 0x79, 0xcc, 0x2f, 0x0e, 0xf3, 0x22, 0x13, 0x19, 0xaf, 0xe1, 0x23, 0x85, - 0x60, 0x9b, 0x86, 0xc9, 0xc1, 0x39, 0xb8, 0x84, 0xfe, 0x70, 0xaa, 0x0c, 0xd8, 0x07, 0x27, 0xa7, - 0x8b, 0x24, 0xa3, 0x91, 0x8f, 0x06, 0x68, 0xd8, 0x25, 0xe5, 0x15, 0xdf, 0x03, 0x97, 0xc7, 0x97, - 0x29, 0x15, 0xf3, 0x82, 0xf9, 0x2d, 0x65, 0x5b, 0x02, 0x78, 0x0f, 0x5a, 0x71, 0xe4, 0xdb, 0x03, - 0x34, 0x74, 0x49, 0x2b, 0x8e, 0x0e, 0xfe, 0x7e, 0x03, 0xdc, 0xe3, 0xd3, 0x2f, 0xcd, 0xab, 0x03, - 0xf0, 0x44, 0xc1, 0xd8, 0x84, 0xd1, 0x68, 0x1a, 0x71, 0x1f, 0x0d, 0xec, 0xa1, 0x4b, 0xea, 0x10, - 0xee, 0x03, 0xd0, 0x30, 0x29, 0x09, 0x2d, 0x45, 0xa8, 0x21, 0xf8, 0x3d, 0xd8, 0xe3, 0x29, 0xcd, - 0xf9, 0x55, 0x26, 0x4e, 0x28, 0x67, 0xd3, 0xf2, 0x7f, 0xad, 0xa1, 0xf8, 0x01, 0x38, 0x34, 0x4c, - 0x3e, 0xa3, 0x82, 0xfa, 0x3b, 0x03, 0x34, 0xf4, 0xc6, 0x6f, 0x8e, 0x68, 0x98, 0x8c, 0xaa, 0x50, - 0xe4, 0x49, 0x5a, 0x49, 0x49, 0x93, 0xb1, 0x99, 0xa2, 0x28, 0xaf, 0xb6, 0xca, 0xac, 0x0e, 0xe1, - 0x11, 0xe0, 0x70, 0x5e, 0x14, 0x2c, 0x15, 0x84, 0xd1, 0xe8, 0x8c, 0x2d, 0x26, 0x94, 0x5f, 0xf9, - 0xbb, 0x03, 0x34, 0xdc, 0x21, 0x0d, 0x16, 0x59, 0x29, 0x11, 0xcf, 0x18, 0x17, 0x74, 0x96, 0xfb, - 0xce, 0x00, 0x0d, 0x6d, 0xb2, 0x04, 0x70, 0x00, 0x9d, 0x38, 0x62, 0xa9, 0x88, 0xc5, 0xc2, 0xef, - 0xa8, 0x1c, 0xaa, 0x7b, 0xf0, 0xab, 0x0d, 0xfb, 0x32, 0xd4, 0x2c, 0x15, 0x2c, 0x15, 0x5f, 0xd1, - 0x64, 0xce, 0xf0, 0x18, 0x9c, 0x39, 0x67, 0xc5, 0x71, 0xa4, 0x15, 0xd9, 0xcc, 0xe8, 0xa9, 0xb6, - 0x4e, 0x2c, 0x52, 0x12, 0xf1, 0xc7, 0x00, 0xf2, 0x48, 0xd8, 0x2c, 0xbb, 0xd6, 0x62, 0x79, 0xe3, - 0xb7, 0x1a, 0xdc, 0x34, 0x61, 0x62, 0x91, 0x1a, 0x1d, 0x7f, 0x03, 0x77, 0xe4, 0xed, 0x09, 0x2b, - 0x66, 0x31, 0xe7, 0x71, 0x96, 0x6a, 0x07, 0x55, 0x70, 0x6f, 0xfc, 0x6e, 0xc3, 0x33, 0xeb, 0xd4, - 0x89, 0x45, 0x1a, 0x9f, 0x28, 0xe3, 0x9a, 0xa6, 0xd7, 0xb1, 0x60, 0x46, 0xa0, 0xa6, 0xb8, 0x34, - 0xa1, 0x8c, 0x4b, 0xdf, 0xf0, 0x87, 0xd0, 0x91, 0xb7, 0x2f, 0xb2, 0x38, 0x55, 0x2a, 0x79, 0xe3, - 0xbb, 0x0d, 0xae, 0xd2, 0x3c, 0xb1, 0x48, 0x45, 0xc5, 0x47, 0xe0, 0xc9, 0xf3, 0x69, 0x96, 0x7e, - 0x17, 0x17, 0x33, 0x25, 0x9b, 0x37, 0x0e, 0x1a, 0x3c, 0x0d, 0x63, 0x62, 0x91, 0xba, 0xc3, 0x89, - 0x03, 0xed, 0x6b, 0x29, 0x44, 0xf0, 0x33, 0x02, 0xc7, 0x74, 0x0f, 0xfe, 0x04, 0x3c, 0x1a, 0x26, - 0xe7, 0xa6, 0xf7, 0x8c, 0x30, 0xc1, 0x66, 0xab, 0x95, 0x0c, 0x52, 0xa7, 0xe3, 0x23, 0xd5, 0xec, - 0x46, 0x65, 0xd5, 0xec, 0xde, 0xb8, 0xbf, 0xe9, 0x5c, 0x6f, 0x03, 0x52, 0xf3, 0x08, 0x4e, 0xc0, - 0xab, 0xbd, 0x8d, 0x1f, 0x42, 0x47, 0xbe, 0x2e, 0xa8, 0x60, 0x26, 0x92, 0xbb, 0x0d, 0x91, 0x48, - 0x33, 0xa9, 0x88, 0xc1, 0x4f, 0x2d, 0xe8, 0x94, 0x30, 0xbe, 0x0f, 0xaf, 0x15, 0xcb, 0x06, 0x66, - 0x7a, 0x42, 0x77, 0xc8, 0x2a, 0x88, 0x1f, 0x69, 0xf5, 0x94, 0x0b, 0x37, 0x61, 0xfb, 0x0d, 0x85, - 0xd4, 0xff, 0xaa, 0xc6, 0xc5, 0x47, 0xe0, 0xc4, 0x4a, 0x44, 0xee, 0xdb, 0xca, 0xed, 0xfe, 0x4b, - 0x02, 0x1c, 0x69, 0xad, 0xf9, 0xe7, 0xa9, 0x28, 0x16, 0xa4, 0x74, 0x0a, 0x9e, 0x42, 0xb7, 0x6e, - 0xc0, 0x3d, 0xb0, 0x9f, 0xb1, 0x85, 0x4a, 0xd6, 0x25, 0xf2, 0x88, 0x0f, 0x8d, 0x4a, 0x5b, 0x9a, - 0x5d, 0xbf, 0x40, 0x34, 0xef, 0xa3, 0xd6, 0x23, 0x14, 0xfc, 0x81, 0xc0, 0xad, 0x02, 0x5e, 0x19, - 0x4c, 0xb4, 0x3a, 0x98, 0xb2, 0x40, 0x2c, 0x0d, 0x8b, 0x45, 0x2e, 0xe2, 0x2c, 0x3d, 0x63, 0x0b, - 0xb3, 0x00, 0x57, 0x41, 0xfc, 0x01, 0xbc, 0x6e, 0x00, 0x16, 0x99, 0x85, 0xa0, 0x13, 0xee, 0x92, - 0x4d, 0x03, 0xfe, 0x14, 0xbc, 0xbc, 0x1a, 0x10, 0xae, 0xa6, 0x61, 0x6f, 0xa3, 0x0d, 0x56, 0xc7, - 0x8b, 0x93, 0xba, 0x8b, 0x5c, 0x5d, 0x53, 0x6e, 0xfa, 0x94, 0x45, 0x6a, 0x28, 0x3a, 0xa4, 0x0e, - 0x05, 0xbf, 0x21, 0x70, 0xcc, 0x7e, 0xf8, 0xff, 0xe5, 0x17, 0x3c, 0x06, 0xaf, 0x36, 0x98, 0x5b, - 0x13, 0xb8, 0x07, 0xae, 0x59, 0x7e, 0xd3, 0x48, 0x05, 0xef, 0x92, 0x25, 0x10, 0xfc, 0x85, 0x00, - 0x96, 0x2d, 0x80, 0x87, 0xb0, 0x4f, 0xc3, 0x90, 0xe5, 0xe2, 0xc9, 0xfc, 0x22, 0x89, 0xc3, 0x33, - 0xd3, 0x4a, 0x5d, 0xb2, 0x0e, 0xe3, 0xf7, 0xa1, 0x67, 0x12, 0x5b, 0x52, 0x75, 0x69, 0x36, 0xf0, - 0xff, 0x5c, 0xfd, 0x00, 0x3a, 0x3a, 0x9f, 0xa9, 0x96, 0xde, 0x25, 0xd5, 0x3d, 0x78, 0x8e, 0xa0, - 0x53, 0x6e, 0xc3, 0x57, 0x20, 0x7c, 0x55, 0xb0, 0xf3, 0xea, 0x0b, 0xc0, 0xae, 0x17, 0xac, 0x82, - 0xf1, 0x01, 0x74, 0x97, 0x2b, 0x7b, 0x1a, 0xa9, 0xbc, 0x5c, 0xb2, 0x82, 0x35, 0x17, 0xaa, 0xfd, - 0x92, 0x42, 0x05, 0xdf, 0x6b, 0xe9, 0xcc, 0x8f, 0xd3, 0xb6, 0x5c, 0x1e, 0xc3, 0xbe, 0x59, 0x58, - 0x84, 0xe5, 0x09, 0x0d, 0xab, 0x6d, 0xf3, 0xf6, 0x5a, 0x59, 0xc9, 0x0a, 0x8b, 0xac, 0x7b, 0x05, - 0x3f, 0xc2, 0xde, 0x2a, 0xe5, 0x15, 0x94, 0x70, 0xd9, 0x49, 0x55, 0x6e, 0xa6, 0x86, 0x1b, 0x78, - 0x20, 0xe0, 0x4e, 0xd3, 0xcf, 0xea, 0xd6, 0x28, 0xd6, 0xfa, 0xa9, 0xf5, 0xaf, 0xfb, 0xe9, 0xe0, - 0x18, 0xf6, 0xd7, 0xec, 0xd8, 0x85, 0xf6, 0x71, 0x34, 0x8b, 0xd3, 0x9e, 0x85, 0x01, 0x76, 0xbf, - 0x2e, 0x62, 0xc1, 0x8a, 0x1e, 0x92, 0x67, 0x19, 0x2a, 0x2b, 0x7a, 0x2d, 0xec, 0x81, 0xa3, 0xa5, - 0x89, 0x7a, 0xf6, 0xc9, 0x3b, 0xcf, 0x6f, 0xfa, 0xe8, 0xc5, 0x4d, 0x1f, 0xfd, 0x79, 0xd3, 0x47, - 0xbf, 0xdc, 0xf6, 0xad, 0x17, 0xb7, 0x7d, 0xeb, 0xf7, 0xdb, 0xbe, 0xf5, 0x6d, 0x5b, 0x7d, 0x88, - 0x5e, 0xec, 0xaa, 0xef, 0xce, 0x87, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x2a, 0xe8, 0x23, 0x71, - 0xab, 0x0a, 0x00, 0x00, + // 1131 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xf7, 0xf8, 0xdf, 0x7a, 0xdf, 0x86, 0xc4, 0x4c, 0xab, 0xd6, 0xac, 0x8a, 0x6b, 0x99, 0x0a, + 0x59, 0x08, 0x9c, 0xca, 0x15, 0x52, 0x05, 0x28, 0x22, 0x49, 0x51, 0x6d, 0x92, 0x43, 0x35, 0xa1, + 0x20, 0xb8, 0x4d, 0x76, 0xa7, 0xc9, 0x2a, 0xf6, 0xee, 0xb2, 0x33, 0x49, 0xe5, 0x0b, 0x12, 0x27, + 0xae, 0x9c, 0x11, 0xe2, 0x43, 0x70, 0xe5, 0x0b, 0xf4, 0xd8, 0x23, 0x37, 0x50, 0x72, 0xe7, 0xc2, + 0x17, 0x40, 0xf3, 0x67, 0xd7, 0xeb, 0xf5, 0x26, 0x0a, 0x52, 0x84, 0xc4, 0x21, 0xd2, 0xcc, 0xef, + 0xfd, 0xde, 0xf8, 0xbd, 0xf7, 0x7b, 0xf3, 0x32, 0x0b, 0x0f, 0xe3, 0x93, 0xa3, 0x4d, 0xea, 0x4d, + 0xe5, 0x9f, 0x77, 0x4c, 0xc3, 0x23, 0xc6, 0xe5, 0x32, 0x3e, 0xdc, 0x8c, 0x93, 0x48, 0x44, 0x3c, + 0x87, 0x0f, 0x15, 0x82, 0x6b, 0xd4, 0x9b, 0xf6, 0x0f, 0xc0, 0x26, 0xf4, 0xe5, 0xae, 0x32, 0xe0, + 0x0e, 0x58, 0x31, 0x9d, 0x4f, 0x23, 0xea, 0x77, 0x50, 0x0f, 0x0d, 0xd6, 0x48, 0xba, 0xc5, 0xf7, + 0xc0, 0xe6, 0xc1, 0x51, 0x48, 0xc5, 0x69, 0xc2, 0x3a, 0x55, 0x65, 0x5b, 0x00, 0x78, 0x1d, 0xaa, + 0x81, 0xdf, 0xa9, 0xf5, 0xd0, 0xc0, 0x26, 0xd5, 0xc0, 0x37, 0x87, 0x12, 0xe6, 0x45, 0x89, 0x7f, + 0x63, 0x87, 0xfe, 0x7d, 0x0b, 0xec, 0xed, 0xdd, 0x7d, 0x13, 0x6a, 0x0f, 0x1c, 0x91, 0x30, 0x36, + 0x66, 0xd4, 0x9f, 0xf8, 0xbc, 0x83, 0x7a, 0xb5, 0x81, 0x4d, 0xf2, 0x10, 0xee, 0x02, 0x50, 0x6f, + 0x9a, 0x12, 0xaa, 0x8a, 0x90, 0x43, 0xf0, 0xbb, 0xb0, 0xce, 0x43, 0x1a, 0xf3, 0xe3, 0x48, 0xec, + 0x50, 0xce, 0x26, 0xe9, 0x6f, 0x15, 0x50, 0xfc, 0x10, 0x2c, 0xea, 0x4d, 0x9f, 0x50, 0x41, 0x3b, + 0xf5, 0x1e, 0x1a, 0x38, 0xa3, 0x3b, 0x43, 0xea, 0x4d, 0x87, 0x59, 0x28, 0x72, 0x25, 0xad, 0x24, + 0xa5, 0xc9, 0xd8, 0x4c, 0xa5, 0x95, 0x57, 0x43, 0x65, 0x96, 0x87, 0xf0, 0x10, 0xb0, 0x77, 0x9a, + 0x24, 0x2c, 0x14, 0x84, 0x51, 0x7f, 0x8f, 0xcd, 0xc7, 0x94, 0x1f, 0x77, 0x9a, 0x3d, 0x34, 0xa8, + 0x93, 0x12, 0x8b, 0xac, 0x94, 0x08, 0x66, 0x8c, 0x0b, 0x3a, 0x8b, 0x3b, 0x56, 0x0f, 0x0d, 0x6a, + 0x64, 0x01, 0x60, 0x17, 0x5a, 0x81, 0xcf, 0x42, 0x11, 0x88, 0x79, 0xa7, 0xa5, 0x72, 0xc8, 0xf6, + 0xee, 0x4f, 0x35, 0xd8, 0x90, 0xa1, 0x46, 0xa1, 0x60, 0xa1, 0xf8, 0x92, 0x4e, 0x4f, 0x19, 0x1e, + 0x81, 0x75, 0xca, 0x59, 0xb2, 0xed, 0x6b, 0x45, 0x56, 0x33, 0x7a, 0xae, 0xad, 0xe3, 0x0a, 0x49, + 0x89, 0xf8, 0x63, 0x00, 0xb9, 0x24, 0x6c, 0x16, 0x9d, 0x69, 0xb1, 0x9c, 0xd1, 0x5b, 0x25, 0x6e, + 0x9a, 0x30, 0xae, 0x90, 0x1c, 0x1d, 0x7f, 0x0d, 0xb7, 0xe5, 0xee, 0x19, 0x4b, 0x66, 0x01, 0xe7, + 0x41, 0x14, 0x6a, 0x07, 0x55, 0x70, 0x67, 0xf4, 0x4e, 0xc9, 0x31, 0x45, 0xea, 0xb8, 0x42, 0x4a, + 0x8f, 0x48, 0xe3, 0x9a, 0x84, 0x67, 0x81, 0x60, 0x46, 0xa0, 0xb2, 0xb8, 0x34, 0x21, 0x8d, 0x4b, + 0xef, 0xf0, 0x87, 0xd0, 0x92, 0xbb, 0xcf, 0xa3, 0x20, 0x54, 0x2a, 0x39, 0xa3, 0xbb, 0x25, 0xae, + 0xd2, 0x3c, 0xae, 0x90, 0x8c, 0x8a, 0xb7, 0xc0, 0x91, 0xeb, 0xdd, 0x28, 0x7c, 0x11, 0x24, 0x33, + 0x25, 0x9b, 0x33, 0x72, 0x4b, 0x3c, 0x0d, 0x63, 0x5c, 0x21, 0x79, 0x87, 0x1d, 0x0b, 0x1a, 0x67, + 0x52, 0x08, 0xf7, 0x07, 0x04, 0x96, 0xe9, 0x1e, 0xfc, 0x09, 0x38, 0xd4, 0x9b, 0x1e, 0x98, 0xde, + 0x33, 0xc2, 0xb8, 0xab, 0xad, 0x96, 0x32, 0x48, 0x9e, 0x8e, 0xb7, 0x54, 0xb3, 0x1b, 0x95, 0x55, + 0xb3, 0x3b, 0xa3, 0xee, 0xaa, 0x73, 0xbe, 0x0d, 0x48, 0xce, 0xc3, 0xdd, 0x01, 0x27, 0x77, 0x36, + 0x7e, 0x04, 0x2d, 0x79, 0xba, 0xa0, 0x82, 0x99, 0x48, 0xee, 0x96, 0x44, 0x22, 0xcd, 0x24, 0x23, + 0xba, 0xdf, 0x57, 0xa1, 0x95, 0xc2, 0xf8, 0x01, 0xbc, 0x91, 0x2c, 0x1a, 0x98, 0xe9, 0x1b, 0x5a, + 0x27, 0xcb, 0x20, 0x7e, 0xac, 0xd5, 0x53, 0x2e, 0xdc, 0x84, 0xdd, 0x29, 0x29, 0xa4, 0xfe, 0xa9, + 0x1c, 0x17, 0x6f, 0x81, 0x15, 0x28, 0x11, 0x79, 0xa7, 0xa6, 0xdc, 0x1e, 0x5c, 0x12, 0xe0, 0x50, + 0x6b, 0xcd, 0x3f, 0x0b, 0x45, 0x32, 0x27, 0xa9, 0x93, 0xfb, 0x1c, 0xd6, 0xf2, 0x06, 0xdc, 0x86, + 0xda, 0x09, 0x9b, 0xab, 0x64, 0x6d, 0x22, 0x97, 0x78, 0xd3, 0xa8, 0x74, 0x45, 0xb3, 0xeb, 0x13, + 0x88, 0xe6, 0x7d, 0x54, 0x7d, 0x8c, 0xdc, 0x3f, 0x10, 0xd8, 0x59, 0xc0, 0x4b, 0x17, 0x13, 0x2d, + 0x5f, 0x4c, 0x59, 0x20, 0x16, 0x7a, 0xc9, 0x3c, 0x16, 0x41, 0x14, 0xee, 0xb1, 0xb9, 0x19, 0x80, + 0xcb, 0x20, 0x7e, 0x1f, 0xde, 0x34, 0x00, 0xf3, 0xcd, 0x40, 0xd0, 0x09, 0xaf, 0x91, 0x55, 0x03, + 0xfe, 0x14, 0x9c, 0x38, 0xbb, 0x20, 0x5c, 0xdd, 0x86, 0xf5, 0x95, 0x36, 0x58, 0xbe, 0x5e, 0x9c, + 0xe4, 0x5d, 0xe4, 0xe8, 0x9a, 0x70, 0xd3, 0xa7, 0xcc, 0x57, 0x97, 0xa2, 0x45, 0xf2, 0x90, 0xfb, + 0x1b, 0x02, 0xcb, 0xcc, 0x87, 0xff, 0x5f, 0x7e, 0xee, 0x53, 0x70, 0x72, 0x17, 0xf3, 0xca, 0x04, + 0xee, 0x81, 0x6d, 0x86, 0xdf, 0xc4, 0x57, 0xc1, 0xdb, 0x64, 0x01, 0xb8, 0x7f, 0x21, 0x80, 0x45, + 0x0b, 0xe0, 0x01, 0x6c, 0x50, 0xcf, 0x63, 0xb1, 0x78, 0x76, 0x7a, 0x38, 0x0d, 0xbc, 0x3d, 0xd3, + 0x4a, 0x6b, 0xa4, 0x08, 0xe3, 0xf7, 0xa0, 0x6d, 0x12, 0x5b, 0x50, 0x75, 0x69, 0x56, 0xf0, 0xff, + 0x5c, 0x7d, 0x17, 0x5a, 0x3a, 0x9f, 0x89, 0x96, 0xde, 0x26, 0xd9, 0xde, 0x7d, 0x85, 0xa0, 0x95, + 0x4e, 0xc3, 0x1b, 0x10, 0x3e, 0x2b, 0xd8, 0x41, 0xf6, 0x02, 0xa8, 0xe5, 0x0b, 0x96, 0xc1, 0xb8, + 0x0f, 0x6b, 0x8b, 0x91, 0x3d, 0xf1, 0x55, 0x5e, 0x36, 0x59, 0xc2, 0xca, 0x0b, 0xd5, 0xb8, 0xa4, + 0x50, 0xee, 0xb7, 0x5a, 0x3a, 0xf3, 0xcf, 0xe9, 0xaa, 0x5c, 0x9e, 0xc2, 0x86, 0x19, 0x58, 0x84, + 0xc5, 0x53, 0xea, 0x65, 0xd3, 0xe6, 0xed, 0x42, 0x59, 0xc9, 0x12, 0x8b, 0x14, 0xbd, 0xdc, 0xef, + 0x60, 0x7d, 0x99, 0x72, 0x03, 0x25, 0x5c, 0x74, 0x52, 0x96, 0x9b, 0xa9, 0xe1, 0x0a, 0xee, 0x0a, + 0xb8, 0x5d, 0xf6, 0x6f, 0xf5, 0xca, 0x28, 0x0a, 0xfd, 0x54, 0xfd, 0xd7, 0xfd, 0xd4, 0xdf, 0x86, + 0x8d, 0x82, 0x1d, 0xdb, 0xd0, 0xd8, 0xf6, 0x67, 0x41, 0xd8, 0xae, 0x60, 0x80, 0xe6, 0x57, 0x49, + 0x20, 0x58, 0xd2, 0x46, 0x72, 0x2d, 0x43, 0x65, 0x49, 0xbb, 0x8a, 0x1d, 0xb0, 0xb4, 0x34, 0x7e, + 0xbb, 0xd6, 0xff, 0xb9, 0x0a, 0xcd, 0x6b, 0x3f, 0xf9, 0xee, 0x81, 0x9d, 0x3d, 0xf0, 0xd2, 0x2b, + 0x9b, 0x01, 0xd7, 0x7e, 0xf0, 0x15, 0x9e, 0x6f, 0xf5, 0xeb, 0x3e, 0xdf, 0x1a, 0xd7, 0x7b, 0xbe, + 0x35, 0xaf, 0x7a, 0xbe, 0x59, 0x05, 0x0d, 0xba, 0x00, 0x01, 0xcf, 0x1e, 0x05, 0x2d, 0x35, 0x8e, + 0x73, 0x48, 0xff, 0x17, 0x24, 0x0b, 0xa7, 0xde, 0xd9, 0x77, 0xa0, 0x19, 0x27, 0xec, 0x6c, 0xe2, + 0x1b, 0x21, 0xcd, 0x6e, 0xe9, 0xf8, 0x6a, 0xe1, 0x78, 0x0c, 0x75, 0x5f, 0xe6, 0xa8, 0xdb, 0x46, + 0xad, 0x2f, 0x49, 0xae, 0x7e, 0xbd, 0xe4, 0x1a, 0x85, 0xe4, 0xfa, 0xbf, 0x22, 0x68, 0x8e, 0x95, + 0xb2, 0xf2, 0x43, 0xe0, 0x45, 0x90, 0x70, 0x91, 0x45, 0x98, 0x6e, 0x8d, 0x6e, 0xfb, 0x81, 0xb2, + 0x2d, 0x74, 0xd3, 0x80, 0xd4, 0xe3, 0x65, 0x94, 0x9c, 0xf0, 0x98, 0x7a, 0x0b, 0xd1, 0xf2, 0x10, + 0xfe, 0x00, 0x2c, 0x3f, 0xf2, 0xbe, 0x98, 0xc7, 0xcc, 0x4c, 0xbd, 0x5b, 0xaa, 0x4b, 0xf5, 0xef, + 0x0e, 0x9f, 0x68, 0x13, 0x49, 0x39, 0xfd, 0xfb, 0x60, 0x19, 0x0c, 0x5b, 0x50, 0xdb, 0xde, 0xdd, + 0x6f, 0x57, 0x64, 0xd3, 0x49, 0x2c, 0x61, 0xac, 0x8d, 0x76, 0xee, 0xbf, 0x3a, 0xef, 0xa2, 0xd7, + 0xe7, 0x5d, 0xf4, 0xe7, 0x79, 0x17, 0xfd, 0x78, 0xd1, 0xad, 0xbc, 0xbe, 0xe8, 0x56, 0x7e, 0xbf, + 0xe8, 0x56, 0xbe, 0x69, 0xa8, 0x4f, 0xaa, 0xc3, 0xa6, 0xfa, 0x82, 0x7a, 0xf4, 0x4f, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x14, 0x6d, 0xee, 0xf8, 0x75, 0x0d, 0x00, 0x00, } func (m *RawChange) Marshal() (dAtA []byte, err error) { @@ -1154,6 +1499,50 @@ func (m *RawChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *RawRecord) 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 *RawRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RawRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0x1a + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if len(m.Payload) > 0 { + i -= len(m.Payload) + copy(dAtA[i:], m.Payload) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Payload))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *ACLChange) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1951,6 +2340,189 @@ func (m *ACLChangeUserPermissionChange) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } +func (m *Change) 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 *Change) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Change) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsSnapshot { + i-- + if m.IsSnapshot { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + 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] = 0x3a + } + if m.Timestamp != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x30 + } + if m.CurrentReadKeyHash != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.CurrentReadKeyHash)) + i-- + dAtA[i] = 0x28 + } + 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] = 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.AclHeadId) > 0 { + i -= len(m.AclHeadId) + copy(dAtA[i:], m.AclHeadId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.AclHeadId))) + 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 *Record) 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 *Record) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Record) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x28 + } + if m.CurrentReadKeyHash != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.CurrentReadKeyHash)) + i-- + dAtA[i] = 0x20 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.Data))) + 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] = 0x12 + } + if len(m.PrevId) > 0 { + i -= len(m.PrevId) + copy(dAtA[i:], m.PrevId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.PrevId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Header) 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 *Header) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DocType != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.DocType)) + i-- + dAtA[i] = 0x20 + } + if len(m.WorkspaceId) > 0 { + i -= len(m.WorkspaceId) + copy(dAtA[i:], m.WorkspaceId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.WorkspaceId))) + i-- + dAtA[i] = 0x1a + } + if len(m.AclListId) > 0 { + i -= len(m.AclListId) + copy(dAtA[i:], m.AclListId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.AclListId))) + i-- + dAtA[i] = 0x12 + } + if len(m.FirstId) > 0 { + i -= len(m.FirstId) + copy(dAtA[i:], m.FirstId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.FirstId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintAclchanges(dAtA []byte, offset int, v uint64) int { offset -= sovAclchanges(v) base := offset @@ -1983,6 +2555,27 @@ func (m *RawChange) Size() (n int) { return n } +func (m *RawRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Payload) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.Id) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + return n +} + func (m *ACLChange) Size() (n int) { if m == nil { return 0 @@ -2366,6 +2959,97 @@ func (m *ACLChangeUserPermissionChange) Size() (n int) { return n } +func (m *Change) 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)) + } + } + l = len(m.AclHeadId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.SnapshotBaseId) + if l > 0 { + 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 m.IsSnapshot { + n += 2 + } + return n +} + +func (m *Record) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PrevId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.Data) + 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)) + } + return n +} + +func (m *Header) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FirstId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.AclListId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.WorkspaceId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if m.DocType != 0 { + n += 1 + sovAclchanges(uint64(m.DocType)) + } + return n +} + func sovAclchanges(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2522,6 +3206,156 @@ func (m *RawChange) Unmarshal(dAtA []byte) error { } return nil } +func (m *RawRecord) 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: RawRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RawRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payload", 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.Payload = append(m.Payload[:0], dAtA[iNdEx:postIndex]...) + if m.Payload == nil { + m.Payload = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", 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.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", 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.Id = 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 *ACLChange) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -4813,6 +5647,627 @@ func (m *ACLChangeUserPermissionChange) Unmarshal(dAtA []byte) error { } return nil } +func (m *Change) 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: Change: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Change: 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 AclHeadId", 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.AclHeadId = 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 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 5: + 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 6: + 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 7: + 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 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsSnapshot", 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.IsSnapshot = 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 *Record) 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: Record: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Record: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PrevId", 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.PrevId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + 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 Data", 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.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 4: + 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 5: + 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 + } + } + 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 *Header) 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: Header: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FirstId", 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.FirstId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclListId", 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.AclListId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkspaceId", 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.WorkspaceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DocType", wireType) + } + m.DocType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DocType |= HeaderDocType(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 diff --git a/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto b/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto index 1c8ceaf2..76177a7c 100644 --- a/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto +++ b/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto @@ -8,6 +8,12 @@ message RawChange { string id = 3; } +message RawRecord { + bytes payload = 1; + bytes signature = 2; + string id = 3; +} + // the element of change tree used to store and internal apply smartBlock history message ACLChange { repeated string treeHeadIds = 1; @@ -109,3 +115,34 @@ message ACLChange { Removed = 3; } } + +message Change { + repeated string treeHeadIds = 1; + string aclHeadId = 2; + string snapshotBaseId = 3; // we will only have one base snapshot for both + bytes changesData = 4; + uint64 currentReadKeyHash = 5; + int64 timestamp = 6; + string identity = 7; + bool isSnapshot = 8; +} + +message Record { + string prevId = 1; + string identity = 2; + bytes data = 3; + uint64 currentReadKeyHash = 4; + int64 timestamp = 5; +} + +message Header { + string firstId = 1; + string aclListId = 2; + string workspaceId = 3; + DocType docType = 4; + + enum DocType { + ACL = 0; + DocTree = 1; + } +} diff --git a/pkg/acl/aclchanges/change.go b/pkg/acl/aclchanges/change.go index be08fca2..3d3b4376 100644 --- a/pkg/acl/aclchanges/change.go +++ b/pkg/acl/aclchanges/change.go @@ -1,11 +1,11 @@ package aclchanges import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/gogo/protobuf/proto" ) type Change interface { - ProtoChange() *aclpb.ACLChange + ProtoChange() proto.Marshaler DecryptedChangeContent() []byte Signature() []byte CID() string diff --git a/pkg/acl/acltree/aclstatebuilder.go b/pkg/acl/acltree/aclstatebuilder.go deleted file mode 100644 index d63d535c..00000000 --- a/pkg/acl/acltree/aclstatebuilder.go +++ /dev/null @@ -1,186 +0,0 @@ -package acltree - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" -) - -type aclStateBuilder struct { - tree *Tree - identity string - key encryptionkey.PrivKey - decoder signingkey.PubKeyDecoder -} - -type decreasedPermissionsParameters struct { - users []*aclpb.ACLChangeUserPermissionChange - startChange string -} - -func newACLStateBuilder(decoder signingkey.PubKeyDecoder, accountData *account.AccountData) *aclStateBuilder { - return &aclStateBuilder{ - decoder: decoder, - identity: accountData.Identity, - key: accountData.EncKey, - } -} - -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 -} - -// 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 nil, false, fmt.Errorf("root should always be a snapshot") - } - - state, err := newACLStateFromSnapshotChange( - root.Content, - sb.identity, - sb.key, - sb.decoder) - if err != nil { - return nil, false, fmt.Errorf("could not build ACLState from snapshot: %w", err) - } - - idSeenMap[startChange.Content.Identity] = append(idSeenMap[startChange.Content.Identity], startChange) - - if startChange.Content.GetChangesData() != nil { - key, exists := state.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 state, 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.Id, startChange.Id, 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 = state.applyChange(c.Content) - if err != nil { - return false - } - - // if we have some users who have less permissions now - users := state.getPermissionDecreasedUsers(c.Content) - if len(users) > 0 { - decreasedPermissions = &decreasedPermissionsParameters{ - users: users, - startChange: c.Id, - } - return false - } - } - - // the user can't make changes - if !state.hasPermission(c.Content.Identity, aclpb.ACLChange_Writer) && !state.hasPermission(c.Content.Identity, aclpb.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 := state.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 > aclpb.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 state, foundId, err -} diff --git a/pkg/acl/acltree/acltree.go b/pkg/acl/acltree/acltree.go deleted file mode 100644 index 07acee6c..00000000 --- a/pkg/acl/acltree/acltree.go +++ /dev/null @@ -1,520 +0,0 @@ -package acltree - -import ( - "context" - "errors" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" - "go.uber.org/zap" - "sync" -) - -type AddResultSummary int - -const ( - AddResultSummaryNothing AddResultSummary = iota - AddResultSummaryAppend - AddResultSummaryRebuild -) - -type AddResult struct { - OldHeads []string - Heads []string - Added []*aclpb.RawChange - // TODO: add summary for changes - Summary AddResultSummary -} - -type TreeUpdateListener interface { - Update(tree ACLTree) - Rebuild(tree ACLTree) -} - -type NoOpListener struct{} - -func (n NoOpListener) Update(tree ACLTree) {} - -func (n NoOpListener) Rebuild(tree ACLTree) {} - -type RWLocker interface { - sync.Locker - RLock() - RUnlock() -} - -var ErrNoCommonSnapshot = errors.New("trees doesn't have a common snapshot") - -type ACLTree interface { - RWLocker - ID() string - Header() *treepb.TreeHeader - ACLState() *ACLState - AddContent(ctx context.Context, f func(builder ChangeBuilder) error) (*aclpb.RawChange, error) - AddRawChanges(ctx context.Context, changes ...*aclpb.RawChange) (AddResult, error) - Heads() []string - Root() *Change - Iterate(func(change *Change) bool) - IterateFrom(string, func(change *Change) bool) - HasChange(string) bool - SnapshotPath() []string - ChangesAfterCommonSnapshot(snapshotPath []string) ([]*aclpb.RawChange, error) - Storage() treestorage.TreeStorage - DebugDump() (string, error) - - Close() error -} - -type aclTree struct { - treeStorage treestorage.TreeStorage - accountData *account.AccountData - updateListener TreeUpdateListener - - id string - header *treepb.TreeHeader - 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.RWMutex -} - -func BuildACLTree( - t treestorage.TreeStorage, - acc *account.AccountData, - listener TreeUpdateListener) (ACLTree, error) { - aclTreeBuilder := newACLTreeBuilder(t, acc.Decoder) - treeBuilder := newTreeBuilder(t, acc.Decoder) - snapshotValidator := newSnapshotValidator(acc.Decoder, acc) // TODO: this looks weird, change it - aclStateBuilder := newACLStateBuilder(acc.Decoder, acc) - changeBuilder := newChangeBuilder() - - aclTree := &aclTree{ - treeStorage: t, - accountData: acc, - fullTree: nil, - aclState: nil, - treeBuilder: treeBuilder, - aclTreeBuilder: aclTreeBuilder, - aclStateBuilder: aclStateBuilder, - snapshotValidator: snapshotValidator, - changeBuilder: changeBuilder, - updateListener: listener, - } - err := aclTree.rebuildFromStorage(false) - if err != nil { - return nil, err - } - err = aclTree.removeOrphans() - if err != nil { - return nil, err - } - err = t.SetHeads(aclTree.Heads()) - if err != nil { - return nil, err - } - aclTree.id, err = t.TreeID() - if err != nil { - return nil, err - } - aclTree.header, err = t.Header() - if err != nil { - return nil, err - } - - listener.Rebuild(aclTree) - - 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 -// } -// -// valid, err := a.snapshotValidator.ValidateSnapshot(a.fullTree.root) -// if err != nil { -// return err -// } -// if !valid { -// return a.rebuildFromStorage(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) removeOrphans() error { - // removing attached or invalid orphans - var toRemove []string - - orphans, err := a.treeStorage.Orphans() - if err != nil { - return err - } - for _, orphan := range orphans { - if _, exists := a.fullTree.attached[orphan]; exists { - toRemove = append(toRemove, orphan) - } - if _, exists := a.fullTree.invalidChanges[orphan]; exists { - toRemove = append(toRemove, orphan) - } - } - return a.treeStorage.RemoveOrphans(toRemove...) -} - -func (a *aclTree) rebuildFromStorage(fromStart bool) error { - 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 - a.aclTreeFromStart, err = a.aclTreeBuilder.Build() - if err != nil { - return err - } - - if !fromStart { - 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.rebuildFromStorage(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 - } - - a.aclState, err = a.aclStateBuilder.Build() - if err != nil { - return err - } - - return nil -} - -func (a *aclTree) ID() string { - return a.id -} - -func (a *aclTree) Header() *treepb.TreeHeader { - return a.header -} - -func (a *aclTree) ACLState() *ACLState { - return a.aclState -} - -func (a *aclTree) Storage() treestorage.TreeStorage { - return a.treeStorage -} - -func (a *aclTree) AddContent(ctx context.Context, build func(builder ChangeBuilder) error) (*aclpb.RawChange, error) { - // TODO: add snapshot creation logic - defer func() { - // 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) - if err != nil { - return nil, err - } - - ch, marshalled, err := a.changeBuilder.BuildAndApply() - if err != nil { - return nil, err - } - a.fullTree.AddFast(ch) - rawCh := &aclpb.RawChange{ - Payload: marshalled, - Signature: ch.Signature(), - Id: ch.Id, - } - - err = a.treeStorage.AddRawChange(rawCh) - if err != nil { - return nil, err - } - - err = a.treeStorage.SetHeads([]string{ch.Id}) - if err != nil { - return nil, err - } - return rawCh, nil -} - -func (a *aclTree) AddRawChanges(ctx context.Context, rawChanges ...*aclpb.RawChange) (AddResult, error) { - // TODO: make proper error handling, because there are a lot of corner cases where this will break - var err error - var mode Mode - - var changes []*Change // TODO: = addChangesBuf[:0] ... - for _, ch := range rawChanges { - change, err := NewFromRawChange(ch) - // TODO: think what if we will have incorrect signatures on rawChanges, how everything will work - if err != nil { - continue - } - changes = append(changes, change) - } - - defer func() { - if err != nil { - return - } - - err = a.removeOrphans() - if err != nil { - return - } - - err = a.treeStorage.SetHeads(a.fullTree.Heads()) - if err != nil { - return - } - - switch mode { - case Append: - a.updateListener.Update(a) - case Rebuild: - a.updateListener.Rebuild(a) - default: - break - } - }() - - getAddedChanges := func() []*aclpb.RawChange { - var added []*aclpb.RawChange - for _, ch := range rawChanges { - if _, exists := a.fullTree.attached[ch.Id]; exists { - added = append(added, ch) - } - } - return added - } - - for _, ch := range changes { - err = a.treeStorage.AddChange(ch) - if err != nil { - return AddResult{}, err - } - err = a.treeStorage.AddOrphans(ch.Id) - if err != nil { - return AddResult{}, err - } - } - - prevHeads := a.fullTree.Heads() - mode = a.fullTree.Add(changes...) - switch mode { - case Nothing: - return AddResult{ - OldHeads: prevHeads, - Heads: prevHeads, - Summary: AddResultSummaryNothing, - }, nil - - case Rebuild: - err = a.rebuildFromStorage(false) - if err != nil { - return AddResult{}, err - } - - return AddResult{ - OldHeads: prevHeads, - Heads: a.fullTree.Heads(), - Added: getAddedChanges(), - Summary: AddResultSummaryRebuild, - }, nil - default: - // just rebuilding the state from start without reloading everything from tree storage - // 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 - } - - return AddResult{ - OldHeads: prevHeads, - Heads: a.fullTree.Heads(), - Added: getAddedChanges(), - Summary: AddResultSummaryAppend, - }, nil - } -} - -func (a *aclTree) Iterate(f func(change *Change) bool) { - a.fullTree.Iterate(a.fullTree.RootId(), f) -} - -func (a *aclTree) IterateFrom(s string, f func(change *Change) bool) { - a.fullTree.Iterate(s, f) -} - -func (a *aclTree) HasChange(s string) bool { - _, attachedExists := a.fullTree.attached[s] - _, unattachedExists := a.fullTree.unAttached[s] - _, invalidExists := a.fullTree.invalidChanges[s] - return attachedExists || unattachedExists || invalidExists -} - -func (a *aclTree) Heads() []string { - return a.fullTree.Heads() -} - -func (a *aclTree) Root() *Change { - return a.fullTree.Root() -} - -func (a *aclTree) Close() error { - return nil -} - -func (a *aclTree) SnapshotPath() []string { - // TODO: think about caching this - - var path []string - // TODO: think that the user may have not all of the snapshots locally - currentSnapshotId := a.fullTree.RootId() - for currentSnapshotId != "" { - sn, err := a.treeBuilder.loadChange(currentSnapshotId) - if err != nil { - break - } - path = append(path, currentSnapshotId) - currentSnapshotId = sn.SnapshotId - } - return path -} - -func (a *aclTree) ChangesAfterCommonSnapshot(theirPath []string) ([]*aclpb.RawChange, error) { - // TODO: think about when the clients will have their full acl tree and thus full snapshots - // but no changes after some of the snapshots - - var ( - isNewDocument = len(theirPath) == 0 - ourPath = a.SnapshotPath() - // by default returning everything we have - commonSnapshot = ourPath[len(ourPath)-1] // TODO: root snapshot, probably it is better to have a specific method in treestorage - err error - ) - - // if this is non-empty request - if !isNewDocument { - commonSnapshot, err = a.commonSnapshotForTwoPaths(ourPath, theirPath) - if err != nil { - return nil, err - } - } - var rawChanges []*aclpb.RawChange - // using custom load function to skip verification step and save raw changes - load := func(id string) (*Change, error) { - raw, err := a.treeStorage.GetChange(context.Background(), id) - if err != nil { - return nil, err - } - - aclChange, err := a.treeBuilder.makeUnverifiedACLChange(raw) - if err != nil { - return nil, err - } - - ch := NewChange(id, aclChange) - rawChanges = append(rawChanges, raw) - return ch, nil - } - // we presume that we have everything after the common snapshot, though this may not be the case in case of clients and only ACL tree changes - log.With( - zap.Strings("heads", a.fullTree.Heads()), - zap.String("breakpoint", commonSnapshot), - zap.String("id", a.id)). - Debug("getting all changes from common snapshot") - _, err = a.treeBuilder.dfs(a.fullTree.Heads(), commonSnapshot, load) - if err != nil { - return nil, err - } - if isNewDocument { - // adding snapshot to raw changes - _, err = load(commonSnapshot) - if err != nil { - return nil, err - } - } - log.With( - zap.Int("len(changes)", len(rawChanges)), - zap.String("id", a.id)). - Debug("returning all changes after common snapshot") - - return rawChanges, nil -} - -func (a *aclTree) DebugDump() (string, error) { - return a.fullTree.Graph() -} - -func (a *aclTree) commonSnapshotForTwoPaths(ourPath []string, theirPath []string) (string, error) { - var i int - var j int - log.With(zap.Strings("our path", ourPath), zap.Strings("their path", theirPath)). - Debug("finding common snapshot for two paths") -OuterLoop: - // find starting point from the right - for i = len(ourPath) - 1; i >= 0; i-- { - for j = len(theirPath) - 1; j >= 0; j-- { - // most likely there would be only one comparison, because mostly the snapshot path will start from the root for nodes - if ourPath[i] == theirPath[j] { - break OuterLoop - } - } - } - if i < 0 || j < 0 { - return "", ErrNoCommonSnapshot - } - // find last common element of the sequence moving from right to left - for i >= 0 && j >= 0 { - if ourPath[i] == theirPath[j] { - i-- - j-- - } - } - return ourPath[i+1], nil -} diff --git a/pkg/acl/acltree/acltree_test.go b/pkg/acl/acltree/acltree_test.go deleted file mode 100644 index 8d89bac3..00000000 --- a/pkg/acl/acltree/acltree_test.go +++ /dev/null @@ -1,262 +0,0 @@ -package acltree - -import ( - "context" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/treestoragebuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "testing" - - "github.com/stretchr/testify/assert" -) - -type mockListener struct{} - -func (m *mockListener) Update(tree ACLTree) {} - -func (m *mockListener) Rebuild(tree ACLTree) {} - -func TestACLTree_UserJoinBuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("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"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - 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, aclpb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, aclpb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, aclpb.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_UserJoinUpdate_Append(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("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"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - - 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") - res, err := tree.AddRawChanges(context.Background(), rawChanges...) - 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, aclpb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, aclpb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, aclpb.ACLChange_Reader) - assert.Equal(t, aclState.userStates[dId].Permissions, aclpb.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_UserJoinUpdate_Rebuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("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"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - 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") - res, err := tree.AddRawChanges(context.Background(), rawChanges...) - 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, aclpb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, aclpb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, aclpb.ACLChange_Reader) - assert.Equal(t, aclState.userStates[dId].Permissions, aclpb.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 := treestoragebuilder.NewTreeStorageBuilderWithTestName("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"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - aId := keychain.GeneratedIdentities["A"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, aclpb.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 := treestoragebuilder.NewTreeStorageBuilderWithTestName("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"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - for _, s := range []string{"A", "C", "E"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, aclpb.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 := treestoragebuilder.NewTreeStorageBuilderWithTestName("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"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, aclpb.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 := treestoragebuilder.NewTreeStorageBuilderWithTestName("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"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, aclpb.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/pkg/acl/acltree/acltreebuilder.go b/pkg/acl/acltree/acltreebuilder.go deleted file mode 100644 index 71b5b304..00000000 --- a/pkg/acl/acltree/acltreebuilder.go +++ /dev/null @@ -1,154 +0,0 @@ -package acltree - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" -) - -type aclTreeBuilder struct { - cache map[string]*Change - identityKeys map[string]signingkey.PubKey - signingPubKeyDecoder signingkey.PubKeyDecoder - tree *Tree - treeStorage treestorage.TreeStorage - - *changeLoader -} - -func newACLTreeBuilder(t treestorage.TreeStorage, decoder signingkey.PubKeyDecoder) *aclTreeBuilder { - return &aclTreeBuilder{ - signingPubKeyDecoder: decoder, - treeStorage: t, - changeLoader: newChangeLoader( - t, - decoder, - NewACLChange), - } -} - -func (tb *aclTreeBuilder) Init() { - tb.cache = make(map[string]*Change) - tb.identityKeys = make(map[string]signingkey.PubKey) - tb.tree = &Tree{} - tb.changeLoader.Init(tb.cache, tb.identityKeys) -} - -func (tb *aclTreeBuilder) Build() (*Tree, error) { - var headsAndOrphans []string - orphans, err := tb.treeStorage.Orphans() - if err != nil { - return nil, err - } - heads, err := tb.treeStorage.Heads() - if err != nil { - return nil, err - } - headsAndOrphans = append(headsAndOrphans, orphans...) - headsAndOrphans = append(headsAndOrphans, heads...) - aclHeads, err := tb.getACLHeads(headsAndOrphans) - - if err != nil { - return nil, err - } - - if err = tb.buildTreeFromStart(aclHeads); 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, root, err := tb.dfsFromStart(heads) - if err != nil { - return err - } - - tb.tree.AddFast(root) - tb.tree.AddFast(changes...) - return -} - -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) - - 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) - } - } - header, err := tb.treeStorage.Header() - if err != nil { - return nil, nil, err - } - - 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 *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 { - continue - } - - 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 tree storage") - } - 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/pkg/acl/acltree/acltreestorage.go b/pkg/acl/acltree/acltreestorage.go deleted file mode 100644 index df03898a..00000000 --- a/pkg/acl/acltree/acltreestorage.go +++ /dev/null @@ -1,70 +0,0 @@ -package acltree - -import ( - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/gogo/protobuf/proto" -) - -func CreateNewTreeStorageWithACL( - acc *account.AccountData, - build func(builder ChangeBuilder) error, - create treestorage.CreatorFunc) (treestorage.TreeStorage, error) { - bld := newChangeBuilder() - bld.Init( - newACLState(acc.Identity, acc.EncKey, signingkey.NewEd25519PubKeyDecoder()), - &Tree{}, - acc) - err := build(bld) - if err != nil { - return nil, err - } - bld.SetMakeSnapshot(true) - - change, payload, err := bld.BuildAndApply() - if err != nil { - return nil, err - } - - rawChange := &aclpb.RawChange{ - Payload: payload, - Signature: change.Signature(), - Id: change.CID(), - } - header, id, err := createTreeHeaderAndId(rawChange) - if err != nil { - return nil, err - } - - thr, err := create(id, header, []*aclpb.RawChange{rawChange}) - if err != nil { - return nil, err - } - - err = thr.SetHeads([]string{change.CID()}) - if err != nil { - return nil, err - } - return thr, nil -} - -func createTreeHeaderAndId(change *aclpb.RawChange) (*treepb.TreeHeader, string, error) { - header := &treepb.TreeHeader{ - FirstChangeId: change.Id, - IsWorkspace: false, - } - marshalledHeader, err := proto.Marshal(header) - if err != nil { - return nil, "", err - } - treeId, err := cid.NewCIDFromBytes(marshalledHeader) - if err != nil { - return nil, "", err - } - - return header, treeId, nil -} diff --git a/pkg/acl/acltree/acltreestorage_test.go b/pkg/acl/acltree/acltreestorage_test.go deleted file mode 100644 index 8730a542..00000000 --- a/pkg/acl/acltree/acltreestorage_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package acltree - -import ( - "context" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/treestoragebuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/stretchr/testify/assert" - "testing" -) - -func Test_BuildTreeStorageWithACL(t *testing.T) { - keychain := treestoragebuilder.NewKeychain() - keychain.AddSigningKey("A") - keychain.AddEncryptionKey("A") - data := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - } - thr, err := CreateNewTreeStorageWithACL( - data, - func(builder ChangeBuilder) error { - return builder.UserAdd( - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"].GetPublic(), - aclpb.ACLChange_Admin) - }, - treestorage.NewInMemoryTreeStorage) - if err != nil { - t.Fatalf("build should not return error") - } - - heads, err := thr.Heads() - if err != nil { - t.Fatalf("should return heads: %v", err) - } - if len(heads) == 0 { - t.Fatalf("tree storage should have non-empty heads") - } - - header, err := thr.Header() - if err != nil { - t.Fatalf("tree storage header should return without error: %v", err) - } - assert.Equal(t, heads[0], header.FirstChangeId) - - treeId, err := thr.TreeID() - if err != nil { - t.Fatalf("tree id should return without error: %v", err) - } - assert.NotEmpty(t, treeId) - ch, err := thr.GetChange(context.Background(), 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) - } -} diff --git a/pkg/acl/acltree/change.go b/pkg/acl/acltree/change.go deleted file mode 100644 index c768ef23..00000000 --- a/pkg/acl/acltree/change.go +++ /dev/null @@ -1,97 +0,0 @@ -package acltree - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/gogo/protobuf/proto" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" -) - -type ChangeContent struct { - ChangesData proto.Marshaler - ACLData *aclpb.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 - Unattached []*Change - PreviousIds []string - Id string - SnapshotId string - IsSnapshot bool - DecryptedDocumentChange []byte - - Content *aclpb.ACLChange - Sign []byte -} - -func (ch *Change) DecryptContents(key *symmetric.Key) error { - if ch.Content.ChangesData == nil { - return nil - } - - decrypted, err := key.Decrypt(ch.Content.ChangesData) - if err != nil { - return fmt.Errorf("failed to decrypt changes data: %w", err) - } - - ch.DecryptedDocumentChange = decrypted - return nil -} - -func (ch *Change) IsACLChange() bool { - return ch.Content.GetAclData() != nil -} - -func NewFromRawChange(rawChange *aclpb.RawChange) (*Change, error) { - unmarshalled := &aclpb.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 *aclpb.ACLChange) *Change { - return &Change{ - Next: nil, - PreviousIds: ch.TreeHeadIds, - Id: id, - Content: ch, - SnapshotId: ch.SnapshotBaseId, - IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - } -} - -func NewACLChange(id string, ch *aclpb.ACLChange) *Change { - return &Change{ - Next: nil, - PreviousIds: ch.AclHeadIds, - Id: id, - Content: ch, - SnapshotId: ch.SnapshotBaseId, - IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - } -} - -func (ch *Change) ProtoChange() *aclpb.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/pkg/acl/acltree/changeloader.go b/pkg/acl/acltree/changeloader.go deleted file mode 100644 index b308397a..00000000 --- a/pkg/acl/acltree/changeloader.go +++ /dev/null @@ -1,99 +0,0 @@ -package acltree - -import ( - "context" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "time" - - "github.com/gogo/protobuf/proto" -) - -type changeLoader struct { - cache map[string]*Change - identityKeys map[string]signingkey.PubKey - signingPubKeyDecoder signingkey.PubKeyDecoder - treeStorage treestorage.TreeStorage - changeCreator func(id string, ch *aclpb.ACLChange) *Change -} - -func newChangeLoader( - treeStorage treestorage.TreeStorage, - signingPubKeyDecoder signingkey.PubKeyDecoder, - changeCreator func(id string, ch *aclpb.ACLChange) *Change) *changeLoader { - return &changeLoader{ - signingPubKeyDecoder: signingPubKeyDecoder, - treeStorage: treeStorage, - changeCreator: changeCreator, - } -} - -func (c *changeLoader) Init(cache map[string]*Change, - identityKeys map[string]signingkey.PubKey) { - 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.treeStorage.GetChange(ctx, id) - if err != nil { - return nil, err - } - - aclChange, err := c.makeVerifiedACLChange(change) - if err != nil { - return nil, err - } - - ch = c.changeCreator(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) -} - -func (c *changeLoader) makeVerifiedACLChange(change *aclpb.RawChange) (aclChange *aclpb.ACLChange, err error) { - aclChange = new(aclpb.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 -} - -func (c *changeLoader) makeUnverifiedACLChange(change *aclpb.RawChange) (aclChange *aclpb.ACLChange, err error) { - aclChange = new(aclpb.ACLChange) - err = proto.Unmarshal(change.Payload, aclChange) - return -} diff --git a/pkg/acl/acltree/snapshotvalidator.go b/pkg/acl/acltree/snapshotvalidator.go deleted file mode 100644 index 8df94d32..00000000 --- a/pkg/acl/acltree/snapshotvalidator.go +++ /dev/null @@ -1,50 +0,0 @@ -package acltree - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" -) - -type snapshotValidator struct { - aclTree *Tree - identity string - key encryptionkey.PrivKey - decoder signingkey.PubKeyDecoder - stateBuilder *aclStateBuilder -} - -func newSnapshotValidator( - decoder signingkey.PubKeyDecoder, - accountData *account.AccountData) *snapshotValidator { - return &snapshotValidator{ - identity: accountData.Identity, - key: accountData.EncKey, - decoder: decoder, - stateBuilder: newACLStateBuilder(decoder, accountData), - } -} - -func (s *snapshotValidator) Init(aclTree *Tree) error { - s.aclTree = aclTree - return s.stateBuilder.Init(aclTree) -} - -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") - } - - otherSt, err := newACLStateFromSnapshotChange(ch.Content, s.identity, s.key, s.decoder) - if err != nil { - return false, err - } - - return st.equal(otherSt), nil -} diff --git a/pkg/acl/acltree/treebuilder_test.go b/pkg/acl/acltree/treebuilder_test.go deleted file mode 100644 index cc586148..00000000 --- a/pkg/acl/acltree/treebuilder_test.go +++ /dev/null @@ -1,64 +0,0 @@ -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.NewThreadBuilderWithTestName("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.NewThreadBuilderWithTestName("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.NewThreadBuilderWithTestName("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/pkg/acl/acltree/treeiterator.go b/pkg/acl/acltree/treeiterator.go deleted file mode 100644 index 19a20095..00000000 --- a/pkg/acl/acltree/treeiterator.go +++ /dev/null @@ -1,158 +0,0 @@ -package acltree - -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 - } - } - 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 -} - -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/pkg/acl/example/plaintextdocument/document.go b/pkg/acl/example/plaintextdocument/document.go deleted file mode 100644 index d97bf430..00000000 --- a/pkg/acl/example/plaintextdocument/document.go +++ /dev/null @@ -1,169 +0,0 @@ -package plaintextdocument - -import ( - "context" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - aclpb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - - "github.com/gogo/protobuf/proto" -) - -type PlainTextDocument interface { - Text() string - AddText(ctx context.Context, text string) error -} - -// TODO: this struct is not thread-safe, so use it wisely :-) -type plainTextDocument struct { - heads []string - aclTree acltree.ACLTree - state *DocumentState -} - -func (p *plainTextDocument) Text() string { - if p.state != nil { - return p.state.Text - } - return "" -} - -func (p *plainTextDocument) AddText(ctx context.Context, text string) error { - _, err := p.aclTree.AddContent(ctx, func(builder acltree.ChangeBuilder) error { - builder.AddChangeContent( - &testchangepb.PlainTextChangeData{ - Content: []*testchangepb.PlainTextChangeContent{ - createAppendTextChangeContent(text), - }, - }) - return nil - }) - return err -} - -func (p *plainTextDocument) Update(tree acltree.ACLTree) { - p.aclTree = tree - 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.aclTree = tree - 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 NewInMemoryPlainTextDocument(acc *account.AccountData, text string) (PlainTextDocument, error) { - return NewPlainTextDocument(acc, treestorage.NewInMemoryTreeStorage, text) -} - -func NewPlainTextDocument( - acc *account.AccountData, - create treestorage.CreatorFunc, - text string) (PlainTextDocument, error) { - 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.CreateNewTreeStorageWithACL( - 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 &testchangepb.PlainTextChangeData{ - Content: []*testchangepb.PlainTextChangeContent{ - createAppendTextChangeContent(text), - }, - Snapshot: &testchangepb.PlainTextChangeSnapshot{Text: text}, - } -} - -func createAppendTextChangeContent(text string) *testchangepb.PlainTextChangeContent { - return &testchangepb.PlainTextChangeContent{ - Value: &testchangepb.PlainTextChangeContentValueOfTextAppend{ - TextAppend: &testchangepb.PlainTextChangeTextAppend{ - Text: text, - }, - }, - } -} diff --git a/pkg/acl/example/plaintextdocument/document_test.go b/pkg/acl/example/plaintextdocument/document_test.go deleted file mode 100644 index 7cabb4af..00000000 --- a/pkg/acl/example/plaintextdocument/document_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package plaintextdocument - -import ( - "context" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/treestoragebuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestDocument_NewPlainTextDocument(t *testing.T) { - keychain := treestoragebuilder.NewKeychain() - keychain.AddSigningKey("A") - keychain.AddEncryptionKey("A") - data := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - - doc, err := NewPlainTextDocument(data, treestorage.NewInMemoryTreeStorage, "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 := treestoragebuilder.NewKeychain() - keychain.AddSigningKey("A") - keychain.AddEncryptionKey("A") - data := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - - doc, err := NewPlainTextDocument(data, treestorage.NewInMemoryTreeStorage, "Some text") - if err != nil { - t.Fatalf("should not create document with error: %v", err) - } - - err = doc.AddText(context.Background(), "Next") - if err != nil { - t.Fatalf("should be able to add document: %v", err) - } - assert.Equal(t, doc.Text(), "Some text|Next") - - err = doc.AddText(context.Background(), "Shmext") - if err != nil { - t.Fatalf("should be able to add document: %v", err) - } - assert.Equal(t, doc.Text(), "Some text|Next|Shmext") -} diff --git a/pkg/acl/example/plaintextdocument/plaintextdocstate.go b/pkg/acl/example/plaintextdocument/plaintextdocstate.go deleted file mode 100644 index 3a3afec3..00000000 --- a/pkg/acl/example/plaintextdocument/plaintextdocstate.go +++ /dev/null @@ -1,59 +0,0 @@ -package plaintextdocument - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - - "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 testchangepb.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 testchangepb.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 *testchangepb.PlainTextChangeContent) error { - switch { - case ch.GetTextAppend() != nil: - text := ch.GetTextAppend().GetText() - p.Text += "|" + text - } - return nil -} diff --git a/pkg/acl/acltree/aclstate.go b/pkg/acl/list/aclstate.go similarity index 69% rename from pkg/acl/acltree/aclstate.go rename to pkg/acl/list/aclstate.go index a7443f69..c34e73e8 100644 --- a/pkg/acl/acltree/aclstate.go +++ b/pkg/acl/list/aclstate.go @@ -1,135 +1,181 @@ -package acltree +package list import ( "bytes" "errors" "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" + "github.com/gogo/protobuf/proto" + "go.uber.org/zap" "hash/fnv" ) +var log = logger.NewNamed("acllist").Sugar() + 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") +var ErrNoSuchRecord = errors.New("no such record") +var ErrInsufficientPermissions = errors.New("insufficient permissions") +var ErrNoReadKey = errors.New("acl state doesn't have a read key") + +type UserPermissionPair struct { + Identity string + Permission aclpb.ACLChangeUserPermissions +} type ACLState struct { currentReadKeyHash uint64 userReadKeys map[uint64]*symmetric.Key userStates map[string]*aclpb.ACLChangeUserState userInvites map[string]*aclpb.ACLChangeUserInvite - signingPubKeyDecoder signingkey.PubKeyDecoder + signingPubKeyDecoder keys.Decoder encryptionKey encryptionkey.PrivKey identity string + permissionsAtRecord map[string][]UserPermissionPair } -func newACLState( +func newACLStateWithIdentity( identity string, encryptionKey encryptionkey.PrivKey, - signingPubKeyDecoder signingkey.PubKeyDecoder) *ACLState { + decoder keys.Decoder) *ACLState { return &ACLState{ identity: identity, encryptionKey: encryptionKey, userReadKeys: make(map[uint64]*symmetric.Key), userStates: make(map[string]*aclpb.ACLChangeUserState), userInvites: make(map[string]*aclpb.ACLChangeUserInvite), - signingPubKeyDecoder: signingPubKeyDecoder, + signingPubKeyDecoder: decoder, + permissionsAtRecord: make(map[string][]UserPermissionPair), } } -func newACLStateFromSnapshotChange( - snapshotChange *aclpb.ACLChange, - identity string, - encryptionKey encryptionkey.PrivKey, - signingPubKeyDecoder signingkey.PubKeyDecoder) (*ACLState, error) { - st := &ACLState{ - identity: identity, - encryptionKey: encryptionKey, +func newACLState(decoder keys.Decoder) *ACLState { + return &ACLState{ + signingPubKeyDecoder: decoder, userReadKeys: make(map[uint64]*symmetric.Key), userStates: make(map[string]*aclpb.ACLChangeUserState), userInvites: make(map[string]*aclpb.ACLChangeUserInvite), - signingPubKeyDecoder: signingPubKeyDecoder, + permissionsAtRecord: make(map[string][]UserPermissionPair), } - err := st.recreateFromSnapshotChange(snapshotChange) - if err != nil { - return nil, err - } - return st, nil } -func (st *ACLState) recreateFromSnapshotChange(snapshotChange *aclpb.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 - } +func (st *ACLState) CurrentReadKeyHash() uint64 { + return st.currentReadKeyHash +} - userState, exists := st.userStates[st.identity] +func (st *ACLState) CurrentReadKey() (*symmetric.Key, error) { + key, exists := st.userReadKeys[st.currentReadKeyHash] if !exists { - return ErrNoSuchUser + return nil, ErrNoReadKey } - for _, key := range userState.EncryptedReadKeys { - key, hash, err := st.decryptReadKeyAndHash(key) - if err != nil { - return ErrFailedToDecrypt + return key, nil +} + +func (st *ACLState) UserReadKeys() map[uint64]*symmetric.Key { + return st.userReadKeys +} + +func (st *ACLState) PermissionsAtRecord(id string, identity string) (UserPermissionPair, error) { + permissions, ok := st.permissionsAtRecord[id] + if !ok { + log.Errorf("missing record at id %s", id) + return UserPermissionPair{}, ErrNoSuchRecord + } + + for _, perm := range permissions { + if perm.Identity == identity { + return perm, nil } - - st.userReadKeys[hash] = key } - st.currentReadKeyHash = snapshotChange.CurrentReadKeyHash - if snapshot.GetAclState().GetInvites() != nil { - st.userInvites = snapshot.GetAclState().GetInvites() - } - return nil + return UserPermissionPair{}, ErrNoSuchUser } -func (st *ACLState) makeSnapshot() *aclpb.ACLChangeACLSnapshot { - var userStates []*aclpb.ACLChangeUserState - for _, st := range st.userStates { - userStates = append(userStates, st) +func (st *ACLState) applyRecord(record *aclpb.Record) (err error) { + // TODO: this should be probably changed + aclData := &aclpb.ACLChangeACLData{} + + err = proto.Unmarshal(record.Data, aclData) + if err != nil { + return } - return &aclpb.ACLChangeACLSnapshot{AclState: &aclpb.ACLChangeACLState{ - ReadKeyHashes: nil, - UserStates: userStates, // TODO: make states and invites in same format - Invites: st.userInvites, - }} -} - -func (st *ACLState) applyChange(change *aclpb.ACLChange) (err error) { defer func() { if err != nil { return } - st.currentReadKeyHash = change.CurrentReadKeyHash + st.currentReadKeyHash = record.CurrentReadKeyHash + }() + + return st.applyChangeData(aclData, record.CurrentReadKeyHash, record.Identity) +} + +func (st *ACLState) applyChangeAndUpdate(recordWrapper *Record) (err error) { + change := recordWrapper.Content + aclData := &aclpb.ACLChangeACLData{} + + if recordWrapper.ParsedModel != nil { + aclData = recordWrapper.ParsedModel.(*aclpb.ACLChangeACLData) + } else { + err = proto.Unmarshal(change.Data, aclData) + if err != nil { + return + } + recordWrapper.ParsedModel = aclData + } + + err = st.applyChangeData(aclData, recordWrapper.Content.CurrentReadKeyHash, recordWrapper.Content.Identity) + if err != nil { + return err + } + + var permissions []UserPermissionPair + for _, state := range st.userStates { + permission := UserPermissionPair{ + Identity: state.Identity, + Permission: state.Permissions, + } + permissions = append(permissions, permission) + } + st.permissionsAtRecord[recordWrapper.Id] = permissions + log.Infof("adding permissions at record %s", recordWrapper.Id) + return nil +} + +func (st *ACLState) applyChangeData(changeData *aclpb.ACLChangeACLData, hash uint64, identity string) (err error) { + defer func() { + if err != nil { + return + } + st.currentReadKeyHash = hash }() // we can't check this for the user which is joining, because it will not be in our list // the same is for the first change to be added - skipIdentityCheck := st.isUserJoin(change) || (st.currentReadKeyHash == 0 && st.isUserAdd(change)) + skipIdentityCheck := st.isUserJoin(changeData) || (st.currentReadKeyHash == 0 && st.isUserAdd(changeData, identity)) 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 { + if _, exists := st.userStates[identity]; !exists { err = ErrNoSuchUser return } - if !st.hasPermission(change.Identity, aclpb.ACLChange_Admin) { - err = fmt.Errorf("user %s must have admin permissions", change.Identity) + if !st.hasPermission(identity, aclpb.ACLChange_Admin) { + err = fmt.Errorf("user %s must have admin permissions", identity) return } } - for _, ch := range change.GetAclData().GetAclContent() { + for _, ch := range changeData.GetAclContent() { if err = st.applyChangeContent(ch); err != nil { - //log.Infof("error while applying changes: %v; ignore", err) + log.Info("error while applying changes: %v; ignore", zap.Error(err)) return err } } @@ -137,7 +183,6 @@ func (st *ACLState) applyChange(change *aclpb.ACLChange) (err error) { return nil } -// TODO: remove changeId, because it is not needed func (st *ACLState) applyChangeContent(ch *aclpb.ACLChangeACLContentValue) error { switch { case ch.GetUserPermissionChange() != nil: @@ -193,7 +238,7 @@ func (st *ACLState) applyUserJoin(ch *aclpb.ACLChangeUserJoin) error { return fmt.Errorf("failed to decode signing identity as bytes") } - res, err := verificationKey.Verify(rawSignedId, signature) + res, err := verificationKey.(signingkey.PubKey).Verify(rawSignedId, signature) if err != nil { return fmt.Errorf("verification returned error: %w", err) } @@ -318,15 +363,15 @@ func (st *ACLState) hasPermission(identity string, permission aclpb.ACLChangeUse return state.Permissions == permission } -func (st *ACLState) isUserJoin(ch *aclpb.ACLChange) bool { +func (st *ACLState) isUserJoin(data *aclpb.ACLChangeACLData) 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 + return data.GetAclContent() != nil && data.GetAclContent()[0].GetUserJoin() != nil } -func (st *ACLState) isUserAdd(ch *aclpb.ACLChange) bool { +func (st *ACLState) isUserAdd(data *aclpb.ACLChangeACLData, identity string) 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 + userAdd := data.GetAclContent()[0].GetUserAdd() + return data.GetAclContent() != nil && userAdd != nil && userAdd.GetIdentity() == identity } func (st *ACLState) getPermissionDecreasedUsers(ch *aclpb.ACLChange) (identities []*aclpb.ACLChangeUserPermissionChange) { @@ -409,3 +454,7 @@ func (st *ACLState) GetUserStates() map[string]*aclpb.ACLChangeUserState { // TODO: we should provide better API that would not allow to change this map from the outside return st.userStates } + +func (st *ACLState) isNodeIdentity() bool { + return st.identity == "" +} diff --git a/pkg/acl/list/aclstatebuilder.go b/pkg/acl/list/aclstatebuilder.go new file mode 100644 index 00000000..f7a06f51 --- /dev/null +++ b/pkg/acl/list/aclstatebuilder.go @@ -0,0 +1,48 @@ +package list + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" +) + +type aclStateBuilder struct { + identity string + key encryptionkey.PrivKey + decoder keys.Decoder +} + +func newACLStateBuilderWithIdentity(decoder keys.Decoder, accountData *account.AccountData) *aclStateBuilder { + return &aclStateBuilder{ + decoder: decoder, + identity: accountData.Identity, + key: accountData.EncKey, + } +} + +func newACLStateBuilder(decoder keys.Decoder) *aclStateBuilder { + return &aclStateBuilder{ + decoder: decoder, + } +} + +func (sb *aclStateBuilder) Build(records []*Record) (*ACLState, error) { + var ( + err error + state *ACLState + ) + + if sb.key != nil { + state = newACLStateWithIdentity(sb.identity, sb.key, sb.decoder) + } else { + state = newACLState(sb.decoder) + } + for _, rec := range records { + err = state.applyChangeAndUpdate(rec) + if err != nil { + return nil, err + } + } + + return state, err +} diff --git a/pkg/acl/acltree/changebuilder.go b/pkg/acl/list/changebuilder.go similarity index 56% rename from pkg/acl/acltree/changebuilder.go rename to pkg/acl/list/changebuilder.go index b5b26b63..bcdabe62 100644 --- a/pkg/acl/acltree/changebuilder.go +++ b/pkg/acl/list/changebuilder.go @@ -1,4 +1,4 @@ -package acltree +package list import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" @@ -15,35 +15,27 @@ type MarshalledChange = []byte type ACLChangeBuilder interface { UserAdd(identity string, encryptionKey encryptionkey.PubKey, permissions aclpb.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 + AddId(id string) // TODO: this is only for testing } -type ChangeBuilder interface { - ACLChangeBuilder - AddChangeContent(marshaler proto.Marshaler) // user code should be responsible for making regular snapshots -} - -type changeBuilder struct { +type aclChangeBuilder struct { aclState *ACLState - tree *Tree + list ACLList acc *account.AccountData - aclData *aclpb.ACLChangeACLData - changeContent proto.Marshaler - id string - makeSnapshot bool - readKey *symmetric.Key - readKeyHash uint64 + aclData *aclpb.ACLChangeACLData + id string + readKey *symmetric.Key + readKeyHash uint64 } -func newChangeBuilder() *changeBuilder { - return &changeBuilder{} +func newACLChangeBuilder() *aclChangeBuilder { + return &aclChangeBuilder{} } -func (c *changeBuilder) Init(state *ACLState, tree *Tree, acc *account.AccountData) { +func (c *aclChangeBuilder) Init(state *ACLState, list ACLList, acc *account.AccountData) { c.aclState = state - c.tree = tree + c.list = list c.acc = acc c.aclData = &aclpb.ACLChangeACLData{} @@ -60,15 +52,11 @@ func (c *changeBuilder) Init(state *ACLState, tree *Tree, acc *account.AccountDa } } -func (c *changeBuilder) AddId(id string) { +func (c *aclChangeBuilder) AddId(id string) { c.id = id } -func (c *changeBuilder) SetMakeSnapshot(b bool) { - c.makeSnapshot = b -} - -func (c *changeBuilder) UserAdd(identity string, encryptionKey encryptionkey.PubKey, permissions aclpb.ACLChangeUserPermissions) error { +func (c *aclChangeBuilder) UserAdd(identity string, encryptionKey encryptionkey.PubKey, permissions aclpb.ACLChangeUserPermissions) error { var allKeys []*symmetric.Key if c.aclState.currentReadKeyHash != 0 { for _, key := range c.aclState.userReadKeys { @@ -105,41 +93,25 @@ func (c *changeBuilder) UserAdd(identity string, encryptionKey encryptionkey.Pub return nil } -func (c *changeBuilder) BuildAndApply() (*Change, []byte, error) { - aclChange := &aclpb.ACLChange{ - TreeHeadIds: c.tree.Heads(), - AclHeadIds: c.tree.ACLHeads(), - SnapshotBaseId: c.tree.RootId(), - AclData: c.aclData, +func (c *aclChangeBuilder) BuildAndApply() (*Record, []byte, error) { + aclRecord := &aclpb.Record{ + PrevId: c.list.Head().Id, CurrentReadKeyHash: c.readKeyHash, Timestamp: int64(time.Now().Nanosecond()), Identity: c.acc.Identity, } - err := c.aclState.applyChange(aclChange) + + marshalledData, err := proto.Marshal(c.aclData) + if err != nil { + return nil, nil, err + } + aclRecord.Data = marshalledData + err = c.aclState.applyRecord(aclRecord) if err != nil { return nil, nil, err } - if c.makeSnapshot { - 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) + fullMarshalledChange, err := proto.Marshal(aclRecord) if err != nil { return nil, nil, err } @@ -151,13 +123,9 @@ func (c *changeBuilder) BuildAndApply() (*Change, []byte, error) { if err != nil { return nil, nil, err } - ch := NewChange(id, aclChange) - ch.DecryptedDocumentChange = marshalled + ch := NewRecord(id, aclRecord) + ch.ParsedModel = c.aclData ch.Sign = signature return ch, fullMarshalledChange, nil } - -func (c *changeBuilder) AddChangeContent(marshaler proto.Marshaler) { - c.changeContent = marshaler -} diff --git a/pkg/acl/list/list.go b/pkg/acl/list/list.go new file mode 100644 index 00000000..acd2b131 --- /dev/null +++ b/pkg/acl/list/list.go @@ -0,0 +1,179 @@ +package list + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + "go.uber.org/zap" + "sync" +) + +type IterFunc = func(record *Record) (IsContinue bool) + +type RWLocker interface { + sync.Locker + RLock() + RUnlock() +} + +type ACLList interface { + RWLocker + ID() string + Header() *aclpb.Header + Records() []*Record + ACLState() *ACLState + IsAfter(first string, second string) (bool, error) + Head() *Record + Get(id string) (*Record, error) + Iterate(iterFunc IterFunc) + IterateFrom(startId string, iterFunc IterFunc) + Close() (err error) +} + +type aclList struct { + header *aclpb.Header + records []*Record + indexes map[string]int + id string + + builder *aclStateBuilder + aclState *ACLState + + sync.RWMutex +} + +func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) { + builder := newACLStateBuilderWithIdentity(acc.Decoder, acc) + return buildWithACLStateBuilder(builder, storage) +} + +func BuildACLList(decoder keys.Decoder, storage storage.ListStorage) (ACLList, error) { + return buildWithACLStateBuilder(newACLStateBuilder(decoder), storage) +} + +func buildWithACLStateBuilder(builder *aclStateBuilder, storage storage.ListStorage) (ACLList, error) { + header, err := storage.Header() + if err != nil { + return nil, err + } + + id, err := storage.ID() + if err != nil { + return nil, err + } + + rawRecord, err := storage.Head() + if err != nil { + return nil, err + } + + record, err := NewFromRawRecord(rawRecord) + if err != nil { + return nil, err + } + records := []*Record{record} + + for record.Content.PrevId != "" { + rawRecord, err = storage.GetRawRecord(context.Background(), record.Content.PrevId) + if err != nil { + return nil, err + } + record, err = NewFromRawRecord(rawRecord) + if err != nil { + return nil, err + } + records = append(records, record) + } + + indexes := make(map[string]int) + for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 { + records[i], records[j] = records[j], records[i] + indexes[records[i].Id] = i + indexes[records[j].Id] = j + } + // adding missed index if needed + if len(records)%2 != 0 { + indexes[records[len(records)/2].Id] = len(records) / 2 + } + + log.With(zap.String("head id", records[len(records)-1].Id), zap.String("list id", id)). + Info("building acl tree") + state, err := builder.Build(records) + if err != nil { + return nil, err + } + + return &aclList{ + header: header, + records: records, + indexes: indexes, + builder: builder, + aclState: state, + id: id, + RWMutex: sync.RWMutex{}, + }, nil +} + +func (a *aclList) Records() []*Record { + return a.records +} + +func (a *aclList) ID() string { + return a.id +} + +func (a *aclList) Header() *aclpb.Header { + return a.header +} + +func (a *aclList) ACLState() *ACLState { + return a.aclState +} + +func (a *aclList) IsAfter(first string, second string) (bool, error) { + firstRec, okFirst := a.indexes[first] + secondRec, okSecond := a.indexes[second] + if !okFirst || !okSecond { + return false, fmt.Errorf("not all entries are there: first (%t), second (%t)", okFirst, okSecond) + } + return firstRec >= secondRec, nil +} + +func (a *aclList) Head() *Record { + return a.records[len(a.records)-1] +} + +func (a *aclList) Get(id string) (*Record, error) { + recIdx, ok := a.indexes[id] + if !ok { + return nil, fmt.Errorf("no such record") + } + return a.records[recIdx], nil +} + +func (a *aclList) Iterate(iterFunc IterFunc) { + for _, rec := range a.records { + if !iterFunc(rec) { + return + } + } +} + +func (a *aclList) IterateFrom(startId string, iterFunc IterFunc) { + recIdx, ok := a.indexes[startId] + if !ok { + return + } + for i := recIdx; i < len(a.records); i++ { + if !iterFunc(a.records[i]) { + return + } + } +} + +func (a *aclList) Close() (err error) { + return nil +} diff --git a/pkg/acl/list/list_test.go b/pkg/acl/list/list_test.go new file mode 100644 index 00000000..8ca208be --- /dev/null +++ b/pkg/acl/list/list_test.go @@ -0,0 +1,92 @@ +package list + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestAclList_ACLState_UserInviteAndJoin(t *testing.T) { + st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml") + require.NoError(t, err, "building storage should not result in error") + + keychain := st.(*acllistbuilder.ACLListStorageBuilder).GetKeychain() + + aclList, err := BuildACLList(signingkey.NewEDPubKeyDecoder(), st) + require.NoError(t, err, "building acl list should be without error") + + idA := keychain.GetIdentity("A") + idB := keychain.GetIdentity("B") + idC := keychain.GetIdentity("C") + + // checking final state + assert.Equal(t, aclpb.ACLChange_Admin, aclList.ACLState().GetUserStates()[idA].Permissions) + assert.Equal(t, aclpb.ACLChange_Writer, aclList.ACLState().GetUserStates()[idB].Permissions) + assert.Equal(t, aclpb.ACLChange_Reader, aclList.ACLState().GetUserStates()[idC].Permissions) + assert.Equal(t, aclList.Head().Content.CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash()) + + var records []*Record + aclList.Iterate(func(record *Record) (IsContinue bool) { + records = append(records, record) + return true + }) + + // checking permissions at specific records + assert.Equal(t, 3, len(records)) + + _, err = aclList.ACLState().PermissionsAtRecord(records[1].Id, idB) + assert.Error(t, err, "B should have no permissions at record 1") + + perm, err := aclList.ACLState().PermissionsAtRecord(records[2].Id, idB) + assert.NoError(t, err, "should have no error with permissions of B in the record 2") + assert.Equal(t, UserPermissionPair{ + Identity: idB, + Permission: aclpb.ACLChange_Writer, + }, perm) +} + +func TestAclList_ACLState_UserJoinAndRemove(t *testing.T) { + st, err := acllistbuilder.NewListStorageWithTestName("userremoveexample.yml") + require.NoError(t, err, "building storage should not result in error") + + keychain := st.(*acllistbuilder.ACLListStorageBuilder).GetKeychain() + + aclList, err := BuildACLList(signingkey.NewEDPubKeyDecoder(), st) + require.NoError(t, err, "building acl list should be without error") + + idA := keychain.GetIdentity("A") + idB := keychain.GetIdentity("B") + idC := keychain.GetIdentity("C") + + // checking final state + assert.Equal(t, aclpb.ACLChange_Admin, aclList.ACLState().GetUserStates()[idA].Permissions) + assert.Equal(t, aclpb.ACLChange_Reader, aclList.ACLState().GetUserStates()[idC].Permissions) + assert.Equal(t, aclList.Head().Content.CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash()) + + _, exists := aclList.ACLState().GetUserStates()[idB] + assert.Equal(t, false, exists) + + var records []*Record + aclList.Iterate(func(record *Record) (IsContinue bool) { + records = append(records, record) + return true + }) + + // checking permissions at specific records + assert.Equal(t, 4, len(records)) + + assert.NotEqual(t, records[2].Content.CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash()) + + perm, err := aclList.ACLState().PermissionsAtRecord(records[2].Id, idB) + assert.NoError(t, err, "should have no error with permissions of B in the record 2") + assert.Equal(t, UserPermissionPair{ + Identity: idB, + Permission: aclpb.ACLChange_Writer, + }, perm) + + _, err = aclList.ACLState().PermissionsAtRecord(records[3].Id, idB) + assert.Error(t, err, "B should have no permissions at record 3, because user should be removed") +} diff --git a/pkg/acl/list/record.go b/pkg/acl/list/record.go new file mode 100644 index 00000000..3227db27 --- /dev/null +++ b/pkg/acl/list/record.go @@ -0,0 +1,34 @@ +package list + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/gogo/protobuf/proto" +) + +type Record struct { + Id string + Content *aclpb.Record + ParsedModel interface{} + Sign []byte +} + +func NewRecord(id string, aclRecord *aclpb.Record) *Record { + return &Record{ + Id: id, + Content: aclRecord, + } +} + +func NewFromRawRecord(rawRec *aclpb.RawRecord) (*Record, error) { + aclRec := &aclpb.Record{} + err := proto.Unmarshal(rawRec.Payload, aclRec) + if err != nil { + return nil, err + } + + return &Record{ + Id: rawRec.Id, + Content: aclRec, + Sign: rawRec.Signature, + }, nil +} diff --git a/pkg/acl/storage/inmemory.go b/pkg/acl/storage/inmemory.go new file mode 100644 index 00000000..16879c38 --- /dev/null +++ b/pkg/acl/storage/inmemory.go @@ -0,0 +1,191 @@ +package storage + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "sync" +) + +type inMemoryACLListStorage struct { + header *aclpb.Header + records []*aclpb.RawRecord + + id string + + sync.RWMutex +} + +func NewInMemoryACLListStorage( + id string, + header *aclpb.Header, + records []*aclpb.RawRecord) (ListStorage, error) { + return &inMemoryACLListStorage{ + id: id, + header: header, + records: records, + RWMutex: sync.RWMutex{}, + }, nil +} + +func (i *inMemoryACLListStorage) Header() (*aclpb.Header, error) { + i.RLock() + defer i.RUnlock() + return i.header, nil +} + +func (i *inMemoryACLListStorage) Head() (*aclpb.RawRecord, error) { + i.RLock() + defer i.RUnlock() + return i.records[len(i.records)-1], nil +} + +func (i *inMemoryACLListStorage) GetRawRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) { + i.RLock() + defer i.RUnlock() + for _, rec := range i.records { + if rec.Id == id { + return rec, nil + } + } + return nil, fmt.Errorf("no such record") +} + +func (i *inMemoryACLListStorage) AddRawRecord(ctx context.Context, rec *aclpb.RawRecord) error { + panic("implement me") +} + +func (i *inMemoryACLListStorage) ID() (string, error) { + i.RLock() + defer i.RUnlock() + return i.id, nil +} + +type inMemoryTreeStorage struct { + id string + header *aclpb.Header + heads []string + changes map[string]*aclpb.RawChange + + sync.RWMutex +} + +func NewInMemoryTreeStorage( + treeId string, + header *aclpb.Header, + heads []string, + changes []*aclpb.RawChange) (TreeStorage, error) { + allChanges := make(map[string]*aclpb.RawChange) + for _, ch := range changes { + allChanges[ch.Id] = ch + } + + return &inMemoryTreeStorage{ + id: treeId, + header: header, + heads: heads, + changes: allChanges, + RWMutex: sync.RWMutex{}, + }, nil +} + +func (t *inMemoryTreeStorage) ID() (string, error) { + t.RLock() + defer t.RUnlock() + return t.id, nil +} + +func (t *inMemoryTreeStorage) Header() (*aclpb.Header, error) { + t.RLock() + defer t.RUnlock() + return t.header, nil +} + +func (t *inMemoryTreeStorage) Heads() ([]string, error) { + t.RLock() + defer t.RUnlock() + return t.heads, nil +} + +func (t *inMemoryTreeStorage) SetHeads(heads []string) error { + t.Lock() + defer t.Unlock() + t.heads = t.heads[:0] + + for _, h := range heads { + t.heads = append(t.heads, h) + } + return nil +} + +func (t *inMemoryTreeStorage) AddRawChange(change *aclpb.RawChange) error { + t.Lock() + defer t.Unlock() + // TODO: better to do deep copy + t.changes[change.Id] = change + return nil +} + +func (t *inMemoryTreeStorage) GetRawChange(ctx context.Context, changeId string) (*aclpb.RawChange, error) { + t.RLock() + defer t.RUnlock() + if res, exists := t.changes[changeId]; exists { + return res, nil + } + return nil, fmt.Errorf("could not get change with id: %s", changeId) +} + +type inMemoryStorageProvider struct { + objects map[string]Storage + sync.RWMutex +} + +func (i *inMemoryStorageProvider) AddStorage(id string, st Storage) error { + i.Lock() + defer i.Unlock() + if _, exists := i.objects[id]; exists { + return fmt.Errorf("storage already exists") + } + + i.objects[id] = st + return nil +} + +func (i *inMemoryStorageProvider) Storage(id string) (Storage, error) { + i.RLock() + defer i.RUnlock() + if tree, exists := i.objects[id]; exists { + return tree, nil + } + return nil, ErrUnknownTreeId +} + +func (i *inMemoryStorageProvider) CreateTreeStorage(payload TreeStorageCreatePayload) (TreeStorage, error) { + i.Lock() + defer i.Unlock() + res, err := NewInMemoryTreeStorage(payload.TreeId, payload.Header, payload.Heads, payload.Changes) + if err != nil { + return nil, err + } + + i.objects[payload.TreeId] = res + return res, nil +} + +func (i *inMemoryStorageProvider) CreateACLListStorage(payload ACLListStorageCreatePayload) (ListStorage, error) { + i.Lock() + defer i.Unlock() + res, err := NewInMemoryACLListStorage(payload.ListId, payload.Header, payload.Records) + if err != nil { + return nil, err + } + + i.objects[payload.ListId] = res + return res, nil +} + +func NewInMemoryTreeStorageProvider() Provider { + return &inMemoryStorageProvider{ + objects: make(map[string]Storage), + } +} diff --git a/pkg/acl/storage/liststorage.go b/pkg/acl/storage/liststorage.go new file mode 100644 index 00000000..ff2ff426 --- /dev/null +++ b/pkg/acl/storage/liststorage.go @@ -0,0 +1,14 @@ +package storage + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" +) + +type ListStorage interface { + Storage + Head() (*aclpb.RawRecord, error) + + GetRawRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) + AddRawRecord(ctx context.Context, rec *aclpb.RawRecord) error +} diff --git a/pkg/acl/storage/provider.go b/pkg/acl/storage/provider.go new file mode 100644 index 00000000..12d47d00 --- /dev/null +++ b/pkg/acl/storage/provider.go @@ -0,0 +1,28 @@ +package storage + +import ( + "errors" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" +) + +var ErrUnknownTreeId = errors.New("tree does not exist") + +type TreeStorageCreatePayload struct { + TreeId string + Header *aclpb.Header + Changes []*aclpb.RawChange + Heads []string +} + +type ACLListStorageCreatePayload struct { + ListId string + Header *aclpb.Header + Records []*aclpb.RawRecord +} + +type Provider interface { + Storage(id string) (Storage, error) + AddStorage(id string, st Storage) error + CreateTreeStorage(payload TreeStorageCreatePayload) (TreeStorage, error) + CreateACLListStorage(payload ACLListStorageCreatePayload) (ListStorage, error) +} diff --git a/pkg/acl/storage/storage.go b/pkg/acl/storage/storage.go new file mode 100644 index 00000000..7f14166c --- /dev/null +++ b/pkg/acl/storage/storage.go @@ -0,0 +1,8 @@ +package storage + +import "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + +type Storage interface { + ID() (string, error) + Header() (*aclpb.Header, error) +} diff --git a/pkg/acl/storage/treestorage.go b/pkg/acl/storage/treestorage.go new file mode 100644 index 00000000..d2cf7eec --- /dev/null +++ b/pkg/acl/storage/treestorage.go @@ -0,0 +1,17 @@ +package storage + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" +) + +type TreeStorage interface { + Storage + Heads() ([]string, error) + SetHeads(heads []string) error + + AddRawChange(change *aclpb.RawChange) error + GetRawChange(ctx context.Context, recordID string) (*aclpb.RawChange, error) +} + +type TreeStorageCreatorFunc = func(payload TreeStorageCreatePayload) (TreeStorage, error) diff --git a/pkg/acl/testutils/treestoragebuilder/keychain.go b/pkg/acl/testutils/acllistbuilder/keychain.go similarity index 57% rename from pkg/acl/testutils/treestoragebuilder/keychain.go rename to pkg/acl/testutils/acllistbuilder/keychain.go index 5635f072..d968cd4c 100644 --- a/pkg/acl/testutils/treestoragebuilder/keychain.go +++ b/pkg/acl/testutils/acllistbuilder/keychain.go @@ -1,4 +1,4 @@ -package treestoragebuilder +package acllistbuilder import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" @@ -50,69 +50,110 @@ func (k *Keychain) ParseKeys(keys *Keys) { } } -func (k *Keychain) AddEncryptionKey(name string) { - if _, exists := k.EncryptionKeys[name]; exists { +func (k *Keychain) AddEncryptionKey(key *Key) { + if _, exists := k.EncryptionKeys[key.Name]; exists { return } - newPrivKey, _, err := encryptionkey.GenerateRandomRSAKeyPair(2048) - if err != nil { - panic(err) + var ( + newPrivKey encryptionkey.PrivKey + err error + ) + if key.Value == "generated" { + newPrivKey, _, err = encryptionkey.GenerateRandomRSAKeyPair(2048) + if err != nil { + panic(err) + } + } else { + decoder := encryptionkey.NewRSAPrivKeyDecoder() + privKey, err := decoder.DecodeFromString(key.Value) + if err != nil { + panic(err) + } + newPrivKey = privKey.(encryptionkey.PrivKey) } - - k.EncryptionKeys[name] = newPrivKey + k.EncryptionKeys[key.Name] = newPrivKey } -func (k *Keychain) AddSigningKey(name string) { - if _, exists := k.SigningKeys[name]; exists { +func (k *Keychain) AddSigningKey(key *Key) { + if _, exists := k.SigningKeys[key.Name]; exists { return } - newPrivKey, pubKey, err := signingkey.GenerateRandomEd25519KeyPair() - if err != nil { - panic(err) + var ( + newPrivKey signingkey.PrivKey + pubKey signingkey.PubKey + err error + ) + if key.Value == "generated" { + newPrivKey, pubKey, err = signingkey.GenerateRandomEd25519KeyPair() + if err != nil { + panic(err) + } + } else { + decoder := signingkey.NewEDPrivKeyDecoder() + privKey, err := decoder.DecodeFromString(key.Value) + if err != nil { + panic(err) + } + newPrivKey = privKey.(signingkey.PrivKey) + pubKey = newPrivKey.GetPublic() } - k.SigningKeys[name] = newPrivKey + k.SigningKeys[key.Name] = newPrivKey res, err := k.coder.EncodeToString(pubKey) if err != nil { panic(err) } k.SigningKeysByIdentity[res] = newPrivKey - k.GeneratedIdentities[name] = res + k.GeneratedIdentities[key.Name] = res } -func (k *Keychain) AddReadKey(name string) { - if _, exists := k.ReadKeys[name]; exists { +func (k *Keychain) AddReadKey(key *Key) { + if _, exists := k.ReadKeys[key.Name]; exists { return } - key, _ := symmetric.NewRandom() + + var ( + rkey *symmetric.Key + err error + ) + if key.Value == "generated" { + rkey, err = symmetric.NewRandom() + if err != nil { + panic("should be able to generate symmetric key") + } + } else { + rkey, err = symmetric.FromString(key.Value) + if err != nil { + panic("should be able to parse symmetric key") + } + } hasher := fnv.New64() - hasher.Write(key.Bytes()) + hasher.Write(rkey.Bytes()) - k.ReadKeys[name] = &SymKey{ + k.ReadKeys[key.Name] = &SymKey{ Hash: hasher.Sum64(), - Key: key, + Key: rkey, } k.ReadKeysByHash[hasher.Sum64()] = &SymKey{ Hash: hasher.Sum64(), - Key: key, + Key: rkey, } } -func (k *Keychain) AddKey(key string) { - parts := strings.Split(key, ".") +func (k *Keychain) AddKey(key *Key) { + parts := strings.Split(key.Name, ".") if len(parts) != 3 { panic("cannot parse a key") } - name := parts[2] switch parts[1] { case "Sign": - k.AddSigningKey(name) + k.AddSigningKey(key) case "Enc": - k.AddEncryptionKey(name) + k.AddEncryptionKey(key) case "Read": - k.AddReadKey(name) + k.AddReadKey(key) default: panic("incorrect format") } diff --git a/pkg/acl/testutils/acllistbuilder/liststoragebuilder.go b/pkg/acl/testutils/acllistbuilder/liststoragebuilder.go new file mode 100644 index 00000000..324f8733 --- /dev/null +++ b/pkg/acl/testutils/acllistbuilder/liststoragebuilder.go @@ -0,0 +1,322 @@ +package acllistbuilder + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/yamltests" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" + "io/ioutil" + "path" + "time" + + "github.com/gogo/protobuf/proto" + "gopkg.in/yaml.v3" +) + +type ACLListStorageBuilder struct { + aclList string + records []*aclpb.Record + rawRecords []*aclpb.RawRecord + indexes map[string]int + keychain *Keychain + header *aclpb.Header + id string +} + +func NewACLListStorageBuilder(keychain *Keychain) *ACLListStorageBuilder { + return &ACLListStorageBuilder{ + records: make([]*aclpb.Record, 0), + indexes: make(map[string]int), + keychain: keychain, + } +} + +func NewListStorageWithTestName(name string) (storage.ListStorage, error) { + filePath := path.Join(yamltests.Path(), name) + return NewACLListStorageBuilderFromFile(filePath) +} + +func NewACLListStorageBuilderFromFile(file string) (*ACLListStorageBuilder, error) { + content, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + + ymlTree := YMLList{} + err = yaml.Unmarshal(content, &ymlTree) + if err != nil { + return nil, err + } + + tb := NewACLListStorageBuilder(NewKeychain()) + tb.Parse(&ymlTree) + + return tb, nil +} + +func (t *ACLListStorageBuilder) createRaw(rec *aclpb.Record) *aclpb.RawRecord { + aclMarshaled, err := proto.Marshal(rec) + if err != nil { + panic("should be able to marshal final acl message!") + } + + signature, err := t.keychain.SigningKeysByIdentity[rec.Identity].Sign(aclMarshaled) + if err != nil { + panic("should be able to sign final acl message!") + } + + id, _ := cid.NewCIDFromBytes(aclMarshaled) + + return &aclpb.RawRecord{ + Payload: aclMarshaled, + Signature: signature, + Id: id, + } +} + +func (t *ACLListStorageBuilder) getRecord(idx int) *aclpb.RawRecord { + return t.rawRecords[idx] +} + +func (t *ACLListStorageBuilder) Head() (*aclpb.RawRecord, error) { + return t.getRecord(len(t.records) - 1), nil +} + +func (t *ACLListStorageBuilder) Header() (*aclpb.Header, error) { + return t.header, nil +} + +func (t *ACLListStorageBuilder) GetRawRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) { + recIdx, ok := t.indexes[id] + if !ok { + return nil, fmt.Errorf("no such record") + } + return t.getRecord(recIdx), nil +} + +func (t *ACLListStorageBuilder) AddRawRecord(ctx context.Context, rec *aclpb.RawRecord) error { + panic("implement me") +} + +func (t *ACLListStorageBuilder) ID() (string, error) { + return t.id, nil +} + +func (t *ACLListStorageBuilder) GetRawRecords() []*aclpb.RawRecord { + return t.rawRecords +} + +func (t *ACLListStorageBuilder) GetKeychain() *Keychain { + return t.keychain +} + +func (t *ACLListStorageBuilder) Parse(tree *YMLList) { + // 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(&tree.Keys) + prevId := "" + for idx, rec := range tree.Records { + newRecord := t.parseRecord(rec, prevId) + rawRecord := t.createRaw(newRecord) + t.records = append(t.records, newRecord) + t.rawRecords = append(t.rawRecords, t.createRaw(newRecord)) + t.indexes[rawRecord.Id] = idx + prevId = rawRecord.Id + } + + t.createHeaderAndId() +} + +func (t *ACLListStorageBuilder) parseRecord(rec *Record, prevId string) *aclpb.Record { + k := t.keychain.GetKey(rec.ReadKey).(*SymKey) + var aclChangeContents []*aclpb.ACLChangeACLContentValue + for _, ch := range rec.AclChanges { + aclChangeContent := t.parseACLChange(ch) + aclChangeContents = append(aclChangeContents, aclChangeContent) + } + data := &aclpb.ACLChangeACLData{ + AclContent: aclChangeContents, + } + bytes, _ := data.Marshal() + + return &aclpb.Record{ + PrevId: prevId, + Identity: t.keychain.GetIdentity(rec.Identity), + Data: bytes, + CurrentReadKeyHash: k.Hash, + Timestamp: time.Now().Unix(), + } +} + +func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACLChangeACLContentValue) { + switch { + case ch.UserAdd != nil: + add := ch.UserAdd + + encKey := t.keychain.GetKey(add.EncryptionKey).(encryptionkey.PrivKey) + rawKey, _ := encKey.GetPublic().Raw() + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserAdd{ + UserAdd: &aclpb.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).(encryptionkey.PrivKey) + rawKey, _ := encKey.GetPublic().Raw() + + idKey, _ := t.keychain.SigningKeys[join.Identity].GetPublic().Raw() + signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey) + signature, err := signKey.Sign(idKey) + if err != nil { + panic(err) + } + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserJoin{ + UserJoin: &aclpb.ACLChangeUserJoin{ + Identity: t.keychain.GetIdentity(join.Identity), + EncryptionKey: rawKey, + AcceptSignature: signature, + UserInviteId: join.InviteId, + EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), + }, + }, + } + case ch.UserInvite != nil: + invite := ch.UserInvite + rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(signingkey.PrivKey).GetPublic().Raw() + encKey := t.keychain. + GetKey(invite.EncryptionKey).(encryptionkey.PrivKey) + rawEncKey, _ := encKey.GetPublic().Raw() + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserInvite{ + UserInvite: &aclpb.ACLChangeUserInvite{ + AcceptPublicKey: rawAcceptKey, + EncryptPublicKey: rawEncKey, + EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey), + Permissions: t.convertPermission(invite.Permissions), + InviteId: invite.InviteId, + }, + }, + } + case ch.UserConfirm != nil: + confirm := ch.UserConfirm + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserConfirm{ + UserConfirm: &aclpb.ACLChangeUserConfirm{ + Identity: t.keychain.GetIdentity(confirm.Identity), + UserAddId: confirm.UserAddId, + }, + }, + } + case ch.UserPermissionChange != nil: + permissionChange := ch.UserPermissionChange + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserPermissionChange{ + UserPermissionChange: &aclpb.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 []*aclpb.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, &aclpb.ACLChangeReadKeyReplace{ + Identity: identity, + EncryptionKey: rawEncKey, + EncryptedReadKey: encReadKey, + }) + } + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserRemove{ + UserRemove: &aclpb.ACLChangeUserRemove{ + Identity: t.keychain.GetIdentity(remove.RemovedIdentity), + ReadKeyReplaces: replaces, + }, + }, + } + } + if convCh == nil { + panic("cannot have empty acl change") + } + + return convCh +} + +func (t *ACLListStorageBuilder) encryptReadKeys(keys []string, encKey encryptionkey.PrivKey) (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 *ACLListStorageBuilder) convertPermission(perm string) aclpb.ACLChangeUserPermissions { + switch perm { + case "admin": + return aclpb.ACLChange_Admin + case "writer": + return aclpb.ACLChange_Writer + case "reader": + return aclpb.ACLChange_Reader + default: + panic(fmt.Sprintf("incorrect permission: %s", perm)) + } +} + +func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclpb.Record, id string) error) (err error) { + for i := len(t.records) - 1; i >= 0; i-- { + err = f(t.records[i], t.rawRecords[i].Id) + if err != nil { + return err + } + } + return nil +} + +func (t *ACLListStorageBuilder) createHeaderAndId() { + t.header = &aclpb.Header{ + FirstId: t.rawRecords[0].Id, + AclListId: "", + WorkspaceId: "", + DocType: aclpb.Header_ACL, + } + bytes, _ := t.header.Marshal() + id, _ := cid.NewCIDFromBytes(bytes) + t.id = id +} diff --git a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph.go b/pkg/acl/testutils/acllistbuilder/liststoragebuildergraph.go similarity index 72% rename from pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph.go rename to pkg/acl/testutils/acllistbuilder/liststoragebuildergraph.go index 11c6609d..ae4f1027 100644 --- a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph.go +++ b/pkg/acl/testutils/acllistbuilder/liststoragebuildergraph.go @@ -2,10 +2,10 @@ // +build !linux,!darwin android ios nographviz // +build !amd64 -package treestoragebuilder +package acllistbuilder import "fmt" -func (t *TreeStorageBuilder) Graph() (string, error) { +func (t *ACLListStorageBuilder) Graph() (string, error) { return "", fmt.Errorf("building graphs is not supported") } diff --git a/pkg/acl/testutils/acllistbuilder/liststoragebuildergraph_nix.go b/pkg/acl/testutils/acllistbuilder/liststoragebuildergraph_nix.go new file mode 100644 index 00000000..b637827f --- /dev/null +++ b/pkg/acl/testutils/acllistbuilder/liststoragebuildergraph_nix.go @@ -0,0 +1,121 @@ +//go:build (linux || darwin) && !android && !ios && !nographviz && (amd64 || arm64) +// +build linux darwin +// +build !android +// +build !ios +// +build !nographviz +// +build amd64 arm64 + +package acllistbuilder + +import ( + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + + "github.com/gogo/protobuf/proto" + "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 *ACLListStorageBuilder) 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 *aclpb.Record, id string) error { + style := "solid" + + var chSymbs []string + aclData := &aclpb.ACLChangeACLData{} + err := proto.Unmarshal(r.GetData(), aclData) + if err != nil { + return err + } + + for _, chc := range 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) + } + + shortId := id + label := fmt.Sprintf("Id: %s\nChanges: %s\n", + shortId, + strings.Join(chSymbs, ","), + ) + e := graph.AddNode("G", "\""+id+"\"", map[string]string{ + "label": "\"" + label + "\"", + "style": "\"" + style + "\"", + }) + if e != nil { + return e + } + nodes[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(r *aclpb.Record, id string) error { + if r.PrevId == "" { + return nil + } + err := createEdge(id, r.PrevId, EdgeParameters{ + style: "dashed", + color: "red", + }) + if err != nil { + return err + } + + return nil + } + + err := t.traverseFromHead(addNodes) + if err != nil { + return "", err + } + + err = t.traverseFromHead(addLinks) + if err != nil { + return "", err + } + + return graph.String(), nil +} diff --git a/pkg/acl/testutils/acllistbuilder/ymlentities.go b/pkg/acl/testutils/acllistbuilder/ymlentities.go new file mode 100644 index 00000000..4d0fb069 --- /dev/null +++ b/pkg/acl/testutils/acllistbuilder/ymlentities.go @@ -0,0 +1,70 @@ +package acllistbuilder + +type Key struct { + Name string `yaml:"name"` + Value string `yaml:"value"` +} + +type Keys struct { + Enc []*Key `yaml:"Enc"` + Sign []*Key `yaml:"Sign"` + Read []*Key `yaml:"Read"` +} + +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"` + InviteId string `yaml:"inviteId"` + } `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 Record struct { + Identity string `yaml:"identity"` + AclChanges []*ACLChange `yaml:"aclChanges"` + ReadKey string `yaml:"readKey"` +} + +type Header struct { + FirstChangeId string `yaml:"firstChangeId"` + IsWorkspace bool `yaml:"isWorkspace"` +} + +type YMLList struct { + Records []*Record `yaml:"records"` + + Keys Keys `yaml:"keys"` +} diff --git a/pkg/acl/testutils/testchanges/testchangepb/testdocumentchanges.pb.go b/pkg/acl/testutils/testchanges/proto/test.pb.go similarity index 81% rename from pkg/acl/testutils/testchanges/testchangepb/testdocumentchanges.pb.go rename to pkg/acl/testutils/testchanges/proto/test.pb.go index 874061c6..53be5aba 100644 --- a/pkg/acl/testutils/testchanges/testchangepb/testdocumentchanges.pb.go +++ b/pkg/acl/testutils/testchanges/proto/test.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto +// source: pkg/acl/testutils/testchanges/proto/test.proto -package testchangepb +package testchanges import ( fmt "fmt" @@ -29,7 +29,7 @@ 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_c07268f9f08f2beb, []int{0} + return fileDescriptor_37f33c266ada4318, []int{0} } func (m *PlainTextChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -68,7 +68,7 @@ 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_c07268f9f08f2beb, []int{0, 0} + return fileDescriptor_37f33c266ada4318, []int{0, 0} } func (m *PlainTextChangeContent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -138,7 +138,7 @@ func (m *PlainTextChangeTextAppend) Reset() { *m = PlainTextChangeTextAp func (m *PlainTextChangeTextAppend) String() string { return proto.CompactTextString(m) } func (*PlainTextChangeTextAppend) ProtoMessage() {} func (*PlainTextChangeTextAppend) Descriptor() ([]byte, []int) { - return fileDescriptor_c07268f9f08f2beb, []int{0, 1} + return fileDescriptor_37f33c266ada4318, []int{0, 1} } func (m *PlainTextChangeTextAppend) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -182,7 +182,7 @@ 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_c07268f9f08f2beb, []int{0, 2} + return fileDescriptor_37f33c266ada4318, []int{0, 2} } func (m *PlainTextChangeSnapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -227,7 +227,7 @@ 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_c07268f9f08f2beb, []int{0, 3} + return fileDescriptor_37f33c266ada4318, []int{0, 3} } func (m *PlainTextChangeData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -279,29 +279,28 @@ func init() { } func init() { - proto.RegisterFile("pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto", fileDescriptor_c07268f9f08f2beb) + proto.RegisterFile("pkg/acl/testutils/testchanges/proto/test.proto", fileDescriptor_37f33c266ada4318) } -var fileDescriptor_c07268f9f08f2beb = []byte{ - // 278 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xf2, 0x2e, 0xc8, 0x4e, 0xd7, +var fileDescriptor_37f33c266ada4318 = []byte{ + // 266 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2b, 0xc8, 0x4e, 0xd7, 0x4f, 0x4c, 0xce, 0xd1, 0x2f, 0x49, 0x2d, 0x2e, 0x29, 0x2d, 0xc9, 0xcc, 0x29, 0x06, 0xb3, 0x92, - 0x33, 0x12, 0xf3, 0xd2, 0x53, 0x91, 0xd9, 0x05, 0x49, 0xfa, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x10, - 0xb1, 0x94, 0xfc, 0xe4, 0xd2, 0xdc, 0xd4, 0x3c, 0x98, 0x3a, 0x3d, 0xb0, 0x94, 0x10, 0x7b, 0x62, - 0x5e, 0x65, 0x49, 0x65, 0x41, 0xaa, 0xd2, 0x26, 0x26, 0x2e, 0xfe, 0x80, 0x9c, 0xc4, 0xcc, 0xbc, - 0x90, 0xd4, 0x8a, 0x12, 0x67, 0xb0, 0x1a, 0xa9, 0x48, 0x2e, 0x76, 0xe7, 0xfc, 0xbc, 0x92, 0xd4, - 0xbc, 0x12, 0x21, 0x57, 0x2e, 0xae, 0x92, 0xd4, 0x8a, 0x12, 0xc7, 0x82, 0x82, 0xd4, 0xbc, 0x14, - 0x09, 0x46, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x65, 0x3d, 0xa8, 0x66, 0x3d, 0x34, 0x8d, 0x7a, 0x21, - 0x70, 0xa5, 0x1e, 0x0c, 0x41, 0x48, 0x1a, 0x9d, 0xd8, 0xb9, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, - 0xa5, 0x14, 0xb8, 0xb8, 0x10, 0x8a, 0x84, 0x84, 0xb8, 0x58, 0x40, 0x8a, 0xc0, 0xe6, 0x72, 0x06, - 0x81, 0xd9, 0x52, 0x72, 0x5c, 0x1c, 0xc1, 0x79, 0x89, 0x05, 0xc5, 0x19, 0xf9, 0x25, 0x58, 0xe5, - 0x1b, 0x19, 0xb9, 0x58, 0x5c, 0x12, 0x4b, 0x12, 0x85, 0xac, 0xb8, 0xd8, 0x93, 0x21, 0xae, 0x94, - 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x52, 0xc0, 0xe9, 0x2e, 0xa8, 0x6f, 0x82, 0x60, 0x1a, 0x84, - 0x6c, 0xb9, 0x38, 0x8a, 0xa1, 0x96, 0x48, 0x30, 0x81, 0x3d, 0xa5, 0x88, 0x53, 0x33, 0xcc, 0x35, - 0x41, 0x70, 0x2d, 0x4e, 0x6a, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, - 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xc5, - 0x83, 0x1c, 0x0d, 0x49, 0x6c, 0xe0, 0xc0, 0x36, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x73, - 0xe1, 0xf2, 0xbb, 0x01, 0x00, 0x00, + 0x33, 0x12, 0xf3, 0xd2, 0x53, 0x8b, 0xf5, 0x0b, 0x8a, 0xf2, 0x4b, 0xf2, 0xc1, 0x22, 0x7a, 0x60, + 0xa6, 0x10, 0x7b, 0x62, 0x5e, 0x65, 0x49, 0x65, 0x41, 0xaa, 0xd2, 0x26, 0x26, 0x2e, 0xfe, 0x80, + 0x9c, 0xc4, 0xcc, 0xbc, 0x90, 0xd4, 0x8a, 0x12, 0x67, 0xb0, 0x72, 0xa9, 0x48, 0x2e, 0x76, 0xe7, + 0xfc, 0xbc, 0x92, 0xd4, 0xbc, 0x12, 0x21, 0x57, 0x2e, 0xae, 0x92, 0xd4, 0x8a, 0x12, 0xc7, 0x82, + 0x82, 0xd4, 0xbc, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x65, 0x3d, 0xa8, 0x66, 0x3d, + 0x34, 0x8d, 0x7a, 0x21, 0x70, 0xa5, 0x1e, 0x0c, 0x41, 0x48, 0x1a, 0x9d, 0xd8, 0xb9, 0x58, 0xcb, + 0x12, 0x73, 0x4a, 0x53, 0xa5, 0x14, 0xb8, 0xb8, 0x10, 0x8a, 0x84, 0x84, 0xb8, 0x58, 0x40, 0x8a, + 0xc0, 0xe6, 0x72, 0x06, 0x81, 0xd9, 0x52, 0x72, 0x5c, 0x1c, 0xc1, 0x79, 0x89, 0x05, 0xc5, 0x19, + 0xf9, 0x25, 0x58, 0xe5, 0x1b, 0x19, 0xb9, 0x58, 0x5c, 0x12, 0x4b, 0x12, 0x85, 0xac, 0xb8, 0xd8, + 0x93, 0x21, 0xae, 0x94, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x52, 0xc0, 0xe9, 0x2e, 0xa8, 0x6f, + 0x82, 0x60, 0x1a, 0x84, 0x6c, 0xb9, 0x38, 0x8a, 0xa1, 0x96, 0x48, 0x30, 0x81, 0x3d, 0xa5, 0x88, + 0x53, 0x33, 0xcc, 0x35, 0x41, 0x70, 0x2d, 0x4e, 0xaa, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, + 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, + 0x2c, 0xc7, 0x10, 0xc5, 0x8d, 0x14, 0xea, 0x49, 0x6c, 0xe0, 0xb0, 0x36, 0x06, 0x04, 0x00, 0x00, + 0xff, 0xff, 0xf8, 0x8c, 0x6a, 0x1d, 0x9d, 0x01, 0x00, 0x00, } func (m *PlainTextChange) Marshal() (dAtA []byte, err error) { @@ -373,7 +372,7 @@ func (m *PlainTextChangeContentValueOfTextAppend) MarshalToSizedBuffer(dAtA []by return 0, err } i -= size - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(size)) + i = encodeVarintTest(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa @@ -403,7 +402,7 @@ func (m *PlainTextChangeTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, erro if len(m.Text) > 0 { i -= len(m.Text) copy(dAtA[i:], m.Text) - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(len(m.Text))) + i = encodeVarintTest(dAtA, i, uint64(len(m.Text))) i-- dAtA[i] = 0xa } @@ -433,7 +432,7 @@ func (m *PlainTextChangeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) if len(m.Text) > 0 { i -= len(m.Text) copy(dAtA[i:], m.Text) - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(len(m.Text))) + i = encodeVarintTest(dAtA, i, uint64(len(m.Text))) i-- dAtA[i] = 0xa } @@ -467,7 +466,7 @@ func (m *PlainTextChangeData) MarshalToSizedBuffer(dAtA []byte) (int, error) { return 0, err } i -= size - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(size)) + i = encodeVarintTest(dAtA, i, uint64(size)) } i-- dAtA[i] = 0x12 @@ -480,7 +479,7 @@ func (m *PlainTextChangeData) MarshalToSizedBuffer(dAtA []byte) (int, error) { return 0, err } i -= size - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(size)) + i = encodeVarintTest(dAtA, i, uint64(size)) } i-- dAtA[i] = 0xa @@ -489,8 +488,8 @@ func (m *PlainTextChangeData) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func encodeVarintTestdocumentchanges(dAtA []byte, offset int, v uint64) int { - offset -= sovTestdocumentchanges(v) +func encodeVarintTest(dAtA []byte, offset int, v uint64) int { + offset -= sovTest(v) base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) @@ -529,7 +528,7 @@ func (m *PlainTextChangeContentValueOfTextAppend) Size() (n int) { _ = l if m.TextAppend != nil { l = m.TextAppend.Size() - n += 1 + l + sovTestdocumentchanges(uint64(l)) + n += 1 + l + sovTest(uint64(l)) } return n } @@ -541,7 +540,7 @@ func (m *PlainTextChangeTextAppend) Size() (n int) { _ = l l = len(m.Text) if l > 0 { - n += 1 + l + sovTestdocumentchanges(uint64(l)) + n += 1 + l + sovTest(uint64(l)) } return n } @@ -554,7 +553,7 @@ func (m *PlainTextChangeSnapshot) Size() (n int) { _ = l l = len(m.Text) if l > 0 { - n += 1 + l + sovTestdocumentchanges(uint64(l)) + n += 1 + l + sovTest(uint64(l)) } return n } @@ -568,21 +567,21 @@ func (m *PlainTextChangeData) Size() (n int) { if len(m.Content) > 0 { for _, e := range m.Content { l = e.Size() - n += 1 + l + sovTestdocumentchanges(uint64(l)) + n += 1 + l + sovTest(uint64(l)) } } if m.Snapshot != nil { l = m.Snapshot.Size() - n += 1 + l + sovTestdocumentchanges(uint64(l)) + n += 1 + l + sovTest(uint64(l)) } return n } -func sovTestdocumentchanges(x uint64) (n int) { +func sovTest(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } -func sozTestdocumentchanges(x uint64) (n int) { - return sovTestdocumentchanges(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +func sozTest(x uint64) (n int) { + return sovTest(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } func (m *PlainTextChange) Unmarshal(dAtA []byte) error { l := len(dAtA) @@ -592,7 +591,7 @@ func (m *PlainTextChange) Unmarshal(dAtA []byte) error { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -615,12 +614,12 @@ func (m *PlainTextChange) Unmarshal(dAtA []byte) error { switch fieldNum { default: iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) + skippy, err := skipTest(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF @@ -642,7 +641,7 @@ func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -670,7 +669,7 @@ func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error { var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -683,11 +682,11 @@ func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error { } } if msglen < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } postIndex := iNdEx + msglen if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if postIndex > l { return io.ErrUnexpectedEOF @@ -700,12 +699,12 @@ func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error { iNdEx = postIndex default: iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) + skippy, err := skipTest(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF @@ -727,7 +726,7 @@ func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -755,7 +754,7 @@ func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error { var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -769,11 +768,11 @@ func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error { } intStringLen := int(stringLen) if intStringLen < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } postIndex := iNdEx + intStringLen if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if postIndex > l { return io.ErrUnexpectedEOF @@ -782,12 +781,12 @@ func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error { iNdEx = postIndex default: iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) + skippy, err := skipTest(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF @@ -809,7 +808,7 @@ func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -837,7 +836,7 @@ func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error { var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -851,11 +850,11 @@ func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error { } intStringLen := int(stringLen) if intStringLen < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } postIndex := iNdEx + intStringLen if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if postIndex > l { return io.ErrUnexpectedEOF @@ -864,12 +863,12 @@ func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error { iNdEx = postIndex default: iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) + skippy, err := skipTest(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF @@ -891,7 +890,7 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -919,7 +918,7 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -932,11 +931,11 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { } } if msglen < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } postIndex := iNdEx + msglen if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if postIndex > l { return io.ErrUnexpectedEOF @@ -953,7 +952,7 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { - return ErrIntOverflowTestdocumentchanges + return ErrIntOverflowTest } if iNdEx >= l { return io.ErrUnexpectedEOF @@ -966,11 +965,11 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { } } if msglen < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } postIndex := iNdEx + msglen if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if postIndex > l { return io.ErrUnexpectedEOF @@ -984,12 +983,12 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { iNdEx = postIndex default: iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) + skippy, err := skipTest(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges + return ErrInvalidLengthTest } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF @@ -1003,7 +1002,7 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { } return nil } -func skipTestdocumentchanges(dAtA []byte) (n int, err error) { +func skipTest(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 depth := 0 @@ -1011,7 +1010,7 @@ func skipTestdocumentchanges(dAtA []byte) (n int, err error) { var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { - return 0, ErrIntOverflowTestdocumentchanges + return 0, ErrIntOverflowTest } if iNdEx >= l { return 0, io.ErrUnexpectedEOF @@ -1028,7 +1027,7 @@ func skipTestdocumentchanges(dAtA []byte) (n int, err error) { case 0: for shift := uint(0); ; shift += 7 { if shift >= 64 { - return 0, ErrIntOverflowTestdocumentchanges + return 0, ErrIntOverflowTest } if iNdEx >= l { return 0, io.ErrUnexpectedEOF @@ -1044,7 +1043,7 @@ func skipTestdocumentchanges(dAtA []byte) (n int, err error) { var length int for shift := uint(0); ; shift += 7 { if shift >= 64 { - return 0, ErrIntOverflowTestdocumentchanges + return 0, ErrIntOverflowTest } if iNdEx >= l { return 0, io.ErrUnexpectedEOF @@ -1057,14 +1056,14 @@ func skipTestdocumentchanges(dAtA []byte) (n int, err error) { } } if length < 0 { - return 0, ErrInvalidLengthTestdocumentchanges + return 0, ErrInvalidLengthTest } iNdEx += length case 3: depth++ case 4: if depth == 0 { - return 0, ErrUnexpectedEndOfGroupTestdocumentchanges + return 0, ErrUnexpectedEndOfGroupTest } depth-- case 5: @@ -1073,7 +1072,7 @@ func skipTestdocumentchanges(dAtA []byte) (n int, err error) { return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } if iNdEx < 0 { - return 0, ErrInvalidLengthTestdocumentchanges + return 0, ErrInvalidLengthTest } if depth == 0 { return iNdEx, nil @@ -1083,7 +1082,7 @@ func skipTestdocumentchanges(dAtA []byte) (n int, err error) { } var ( - ErrInvalidLengthTestdocumentchanges = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTestdocumentchanges = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupTestdocumentchanges = fmt.Errorf("proto: unexpected end of group") + ErrInvalidLengthTest = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTest = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTest = fmt.Errorf("proto: unexpected end of group") ) diff --git a/pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto b/pkg/acl/testutils/testchanges/proto/test.proto similarity index 81% rename from pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto rename to pkg/acl/testutils/testchanges/proto/test.proto index 59a97c38..b861050e 100644 --- a/pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto +++ b/pkg/acl/testutils/testchanges/proto/test.proto @@ -1,8 +1,6 @@ syntax = "proto3"; package anytype; -option go_package = "testchangepb"; - -// TODO: move to separate package +option go_package = "testchanges"; message PlainTextChange { message Content { @@ -23,4 +21,4 @@ message PlainTextChange { repeated Content content = 1; Snapshot snapshot = 2; } -} +} \ No newline at end of file diff --git a/pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go b/pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go deleted file mode 100644 index f7bb65bd..00000000 --- a/pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go +++ /dev/null @@ -1,539 +0,0 @@ -package treestoragebuilder - -import ( - "context" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - testpb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/yamltests" - storagepb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "io/ioutil" - "path" - - "github.com/gogo/protobuf/proto" - "gopkg.in/yaml.v3" -) - -const plainTextDocType uint16 = 1 - -type treeChange struct { - *aclpb.ACLChange - id string - readKey *SymKey - signKey signingkey.PrivKey - - changesDataDecrypted []byte -} - -type updateUseCase struct { - changes map[string]*treeChange -} - -type TreeStorageBuilder struct { - treeId string - allChanges map[string]*treeChange - updates map[string]*updateUseCase - heads []string - orphans []string - keychain *Keychain - header *storagepb.TreeHeader -} - -func NewTreeStorageBuilder(keychain *Keychain) *TreeStorageBuilder { - return &TreeStorageBuilder{ - allChanges: make(map[string]*treeChange), - updates: make(map[string]*updateUseCase), - keychain: keychain, - } -} - -func NewTreeStorageBuilderWithTestName(name string) (*TreeStorageBuilder, error) { - filePath := path.Join(yamltests.Path(), name) - return NewTreeStorageBuilderFromFile(filePath) -} - -func NewTreeStorageBuilderFromFile(file string) (*TreeStorageBuilder, error) { - content, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - - ymlTree := YMLTree{} - err = yaml.Unmarshal(content, &ymlTree) - if err != nil { - return nil, err - } - - tb := NewTreeStorageBuilder(NewKeychain()) - tb.Parse(&ymlTree) - - return tb, nil -} - -func (t *TreeStorageBuilder) TreeID() (string, error) { - return t.treeId, nil -} - -func (t *TreeStorageBuilder) GetKeychain() *Keychain { - return t.keychain -} - -func (t *TreeStorageBuilder) Heads() ([]string, error) { - return t.heads, nil -} - -func (t *TreeStorageBuilder) AddRawChange(change *aclpb.RawChange) error { - aclChange := new(aclpb.ACLChange) - var err error - - 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.allChanges[change.Id] = &treeChange{ - ACLChange: aclChange, - id: change.Id, - readKey: readKey, - signKey: signKey, - changesDataDecrypted: changesData, - } - return nil -} - -func (t *TreeStorageBuilder) AddOrphans(orphans ...string) error { - t.orphans = append(t.orphans, orphans...) - return nil -} - -func (t *TreeStorageBuilder) 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()] = &treeChange{ - ACLChange: aclChange, - id: change.CID(), - readKey: readKey, - signKey: signKey, - changesDataDecrypted: changesData, - } - return nil -} - -func (t *TreeStorageBuilder) Orphans() ([]string, error) { - return t.orphans, nil -} - -func (t *TreeStorageBuilder) SetHeads(heads []string) error { - // we should copy here instead of just setting the value - t.heads = heads - return nil -} - -func (t *TreeStorageBuilder) RemoveOrphans(orphans ...string) error { - t.orphans = slice.Difference(t.orphans, orphans) - return nil -} - -func (t *TreeStorageBuilder) GetChange(ctx context.Context, recordID string) (*aclpb.RawChange, error) { - return t.getChange(recordID, t.allChanges), nil -} - -func (t *TreeStorageBuilder) GetUpdates(useCase string) []*aclpb.RawChange { - var res []*aclpb.RawChange - update := t.updates[useCase] - for _, ch := range update.changes { - rawCh := t.getChange(ch.id, update.changes) - res = append(res, rawCh) - } - return res -} - -func (t *TreeStorageBuilder) Header() (*storagepb.TreeHeader, error) { - return t.header, nil -} - -func (t *TreeStorageBuilder) getChange(changeId string, m map[string]*treeChange) *aclpb.RawChange { - rec := m[changeId] - - 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!") - } - - 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 := &aclpb.RawChange{ - Payload: aclMarshaled, - Signature: signature, - Id: changeId, - } - return transformedRec -} - -func (t *TreeStorageBuilder) Parse(tree *YMLTree) { - // 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(&tree.Keys) - t.treeId = t.parseTreeId(tree.Description) - for _, ch := range tree.Changes { - newChange := t.parseChange(ch) - t.allChanges[newChange.id] = newChange - } - - t.parseGraph(tree) - t.parseOrphans(tree) - t.parseHeader(tree) - t.parseUpdates(tree.Updates) -} - -func (t *TreeStorageBuilder) parseChange(ch *Change) *treeChange { - newChange := &treeChange{ - id: ch.Id, - } - k := t.keychain.GetKey(ch.ReadKey).(*SymKey) - newChange.readKey = k - newChange.signKey = t.keychain.SigningKeys[ch.Identity] - aclChange := &aclpb.ACLChange{} - aclChange.Identity = t.keychain.GetIdentity(ch.Identity) - if len(ch.AclChanges) > 0 || ch.AclSnapshot != nil { - aclChange.AclData = &aclpb.ACLChangeACLData{} - if ch.AclSnapshot != nil { - aclChange.AclData.AclSnapshot = t.parseACLSnapshot(ch.AclSnapshot) - } - if ch.AclChanges != nil { - var aclChangeContents []*aclpb.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 := &testpb.PlainTextChangeData{} - if ch.Snapshot != nil { - changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) - } - if len(ch.Changes) > 0 { - var changeContents []*testpb.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 *TreeStorageBuilder) parseTreeId(description *TreeDescription) string { - if description == nil { - panic("no author in tree") - } - return description.Author + ".tree.id" -} - -func (t *TreeStorageBuilder) parseChangeSnapshot(s *PlainTextSnapshot) *testpb.PlainTextChangeSnapshot { - return &testpb.PlainTextChangeSnapshot{ - Text: s.Text, - } -} - -func (t *TreeStorageBuilder) parseACLSnapshot(s *ACLSnapshot) *aclpb.ACLChangeACLSnapshot { - newState := &aclpb.ACLChangeACLState{} - for _, state := range s.UserStates { - aclUserState := &aclpb.ACLChangeUserState{} - aclUserState.Identity = t.keychain.GetIdentity(state.Identity) - - encKey := t.keychain. - GetKey(state.EncryptionKey).(encryptionkey.PrivKey) - 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 &aclpb.ACLChangeACLSnapshot{ - AclState: newState, - } -} - -func (t *TreeStorageBuilder) parseDocumentChange(ch *PlainTextChange) (convCh *testpb.PlainTextChangeContent) { - switch { - case ch.TextAppend != nil: - convCh = &testpb.PlainTextChangeContent{ - Value: &testpb.PlainTextChangeContentValueOfTextAppend{ - TextAppend: &testpb.PlainTextChangeTextAppend{ - Text: ch.TextAppend.Text, - }, - }, - } - } - if convCh == nil { - panic("cannot have empty document change") - } - - return convCh -} - -func (t *TreeStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACLChangeACLContentValue) { - switch { - case ch.UserAdd != nil: - add := ch.UserAdd - - encKey := t.keychain. - GetKey(add.EncryptionKey).(encryptionkey.PrivKey) - rawKey, _ := encKey.GetPublic().Raw() - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserAdd{ - UserAdd: &aclpb.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).(encryptionkey.PrivKey) - rawKey, _ := encKey.GetPublic().Raw() - - idKey, _ := t.keychain.SigningKeys[join.Identity].GetPublic().Raw() - signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey) - signature, err := signKey.Sign(idKey) - if err != nil { - panic(err) - } - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserJoin{ - UserJoin: &aclpb.ACLChangeUserJoin{ - Identity: t.keychain.GetIdentity(join.Identity), - EncryptionKey: rawKey, - AcceptSignature: signature, - UserInviteId: join.InviteId, - EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), - }, - }, - } - case ch.UserInvite != nil: - invite := ch.UserInvite - rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(signingkey.PrivKey).GetPublic().Raw() - encKey := t.keychain. - GetKey(invite.EncryptionKey).(encryptionkey.PrivKey) - rawEncKey, _ := encKey.GetPublic().Raw() - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserInvite{ - UserInvite: &aclpb.ACLChangeUserInvite{ - AcceptPublicKey: rawAcceptKey, - EncryptPublicKey: rawEncKey, - EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey), - Permissions: t.convertPermission(invite.Permissions), - InviteId: invite.InviteId, - }, - }, - } - case ch.UserConfirm != nil: - confirm := ch.UserConfirm - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserConfirm{ - UserConfirm: &aclpb.ACLChangeUserConfirm{ - Identity: t.keychain.GetIdentity(confirm.Identity), - UserAddId: confirm.UserAddId, - }, - }, - } - case ch.UserPermissionChange != nil: - permissionChange := ch.UserPermissionChange - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserPermissionChange{ - UserPermissionChange: &aclpb.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 []*aclpb.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, &aclpb.ACLChangeReadKeyReplace{ - Identity: identity, - EncryptionKey: rawEncKey, - EncryptedReadKey: encReadKey, - }) - } - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserRemove{ - UserRemove: &aclpb.ACLChangeUserRemove{ - Identity: t.keychain.GetIdentity(remove.RemovedIdentity), - ReadKeyReplaces: replaces, - }, - }, - } - } - if convCh == nil { - panic("cannot have empty acl change") - } - - return convCh -} - -func (t *TreeStorageBuilder) encryptReadKeys(keys []string, encKey encryptionkey.PrivKey) (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 *TreeStorageBuilder) convertPermission(perm string) aclpb.ACLChangeUserPermissions { - switch perm { - case "admin": - return aclpb.ACLChange_Admin - case "writer": - return aclpb.ACLChange_Writer - case "reader": - return aclpb.ACLChange_Reader - default: - panic(fmt.Sprintf("incorrect permission: %s", perm)) - } -} - -func (t *TreeStorageBuilder) traverseFromHeads(f func(t *treeChange) error) error { - uniqMap := map[string]struct{}{} - 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] - if _, exists := uniqMap[id]; exists { - continue - } - - ch := t.allChanges[id] - uniqMap[id] = struct{}{} - if err := f(ch); err != nil { - return err - } - - for _, prev := range ch.ACLChange.TreeHeadIds { - stack = append(stack, prev) - } - } - return nil -} - -func (t *TreeStorageBuilder) parseUpdates(updates []*Update) { - for _, update := range updates { - useCase := &updateUseCase{ - changes: map[string]*treeChange{}, - } - 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 *TreeStorageBuilder) parseGraph(tree *YMLTree) { - for _, node := range tree.Graph { - rec := t.allChanges[node.Id] - rec.AclHeadIds = node.ACLHeads - rec.TreeHeadIds = node.TreeHeads - rec.SnapshotBaseId = node.BaseSnapshot - } -} - -func (t *TreeStorageBuilder) parseOrphans(tree *YMLTree) { - t.orphans = tree.Orphans -} - -func (t *TreeStorageBuilder) parseHeader(tree *YMLTree) { - t.header = &storagepb.TreeHeader{ - FirstChangeId: tree.Header.FirstChangeId, - IsWorkspace: tree.Header.IsWorkspace, - } -} diff --git a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go b/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go deleted file mode 100644 index 935fd711..00000000 --- a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go +++ /dev/null @@ -1,162 +0,0 @@ -//go:build (linux || darwin) && !android && !ios && !nographviz && (amd64 || arm64) -// +build linux darwin -// +build !android -// +build !ios -// +build !nographviz -// +build amd64 arm64 - -package treestoragebuilder - -import ( - "fmt" - testpb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - - "github.com/gogo/protobuf/proto" - "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 *TreeStorageBuilder) 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 *treeChange) error { - // TODO: revisit function after checking - - style := "solid" - if r.GetAclData() != nil { - style = "filled" - } else if r.changesDataDecrypted != nil { - style = "dashed" - } - - var chSymbs []string - if r.changesDataDecrypted != nil { - res := &testpb.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 := "" - 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 *treeChange) 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/pkg/acl/testutils/treestoragebuilder/ymlentities.go b/pkg/acl/testutils/treestoragebuilder/ymlentities.go deleted file mode 100644 index 0a873d99..00000000 --- a/pkg/acl/testutils/treestoragebuilder/ymlentities.go +++ /dev/null @@ -1,117 +0,0 @@ -package treestoragebuilder - -type TreeDescription 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 PlainTextSnapshot struct { - Text string `yaml:"text"` -} - -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"` - InviteId string `yaml:"inviteId"` - } `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 PlainTextChange struct { - TextAppend *struct { - Text string `yaml:"text"` - } `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 Header struct { - FirstChangeId string `yaml:"firstChangeId"` - IsWorkspace bool `yaml:"isWorkspace"` -} - -type Update struct { - UseCase string `yaml:"useCase"` - Changes []*Change `yaml:"changes"` - Graph []*GraphNode `yaml:"graph"` -} - -type YMLTree struct { - Description *TreeDescription `yaml:"tree"` - Changes []*Change `yaml:"changes"` - Updates []*Update `yaml:"updates"` - - Keys Keys `yaml:"keys"` - - Graph []*GraphNode `yaml:"graph"` - - Heads []string `yaml:"heads"` - Orphans []string `yaml:"orphans"` - Header *Header `yaml:"header"` -} diff --git a/pkg/acl/testutils/treestoragebuilder/ymlentities_test.go b/pkg/acl/testutils/treestoragebuilder/ymlentities_test.go deleted file mode 100644 index 2ae2e716..00000000 --- a/pkg/acl/testutils/treestoragebuilder/ymlentities_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package treestoragebuilder - -import ( - "fmt" - "testing" -) - -func Test_YamlParse(t *testing.T) { - tb, _ := NewTreeStorageBuilderWithTestName("userjoinexampleupdate.yml") - gr, _ := tb.Graph() - fmt.Println(gr) -} diff --git a/pkg/acl/testutils/yamltests/invalidsnapshotexample.yml b/pkg/acl/testutils/yamltests/invalidsnapshotexample.yml deleted file mode 100644 index f3eaf00c..00000000 --- a/pkg/acl/testutils/yamltests/invalidsnapshotexample.yml +++ /dev/null @@ -1,126 +0,0 @@ -tree: - author: A -changes: - - id: A.1.1 - 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 - 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 - - 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: - 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 - 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 - - 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] -header: - firstChangeId: A.1.1 - isWorkspace: false -orphans: - - A.1.3 - - B.1.2 - diff --git a/pkg/acl/testutils/yamltests/userjoinexample.yml b/pkg/acl/testutils/yamltests/userjoinexample.yml index ff15f1f6..014c9349 100644 --- a/pkg/acl/testutils/yamltests/userjoinexample.yml +++ b/pkg/acl/testutils/yamltests/userjoinexample.yml @@ -1,28 +1,13 @@ -tree: - 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" +records: + - identity: A 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 + - identity: A aclChanges: - userInvite: acceptKey: key.Sign.Onetime1 @@ -34,16 +19,9 @@ changes: identity: C permission: reader encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] + 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 + - identity: B aclChanges: - userJoin: identity: B @@ -52,56 +30,25 @@ changes: 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 - - Onetime1 + - name: A + value: generated + - name: B + value: generated + - name: C + value: generated + - name: Onetime1 + value: generated Sign: - - A - - B - - C - - Onetime1 + - name: A + value: generated + - name: B + value: generated + - name: C + value: generated + - name: Onetime1 + value: generated 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] -header: - firstChangeId: A.1.1 - isWorkspace: false -orphans: - - "A.1.3" + - name: 1 + value: generated diff --git a/pkg/acl/testutils/yamltests/userjoinexampleupdate.yml b/pkg/acl/testutils/yamltests/userjoinexampleupdate.yml deleted file mode 100644 index 1e7d2b01..00000000 --- a/pkg/acl/testutils/yamltests/userjoinexampleupdate.yml +++ /dev/null @@ -1,152 +0,0 @@ -tree: - 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 - inviteId: A.1.2 - - 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] -header: - firstChangeId: A.1.1 - isWorkspace: false -orphans: - - "A.1.3" -updates: - - useCase: append - changes: - - 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 ] - 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 ] - - 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 ] diff --git a/pkg/acl/testutils/yamltests/userremovebeforeexample.yml b/pkg/acl/testutils/yamltests/userremovebeforeexample.yml deleted file mode 100644 index 2cd58df4..00000000 --- a/pkg/acl/testutils/yamltests/userremovebeforeexample.yml +++ /dev/null @@ -1,109 +0,0 @@ -tree: - author: A -changes: - - id: A.1.1 - 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 - 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 - 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 - - 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] -orphans: - - "A.1.3" - - "B.1.2" -header: - firstChangeId: A.1.1 - isWorkspace: false diff --git a/pkg/acl/testutils/yamltests/userremoveexample.yml b/pkg/acl/testutils/yamltests/userremoveexample.yml index c3da9fe2..9d90468c 100644 --- a/pkg/acl/testutils/yamltests/userremoveexample.yml +++ b/pkg/acl/testutils/yamltests/userremoveexample.yml @@ -1,28 +1,13 @@ -tree: - 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" +records: + - identity: A 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 + - identity: A aclChanges: - userInvite: acceptKey: key.Sign.Onetime1 @@ -30,23 +15,13 @@ changes: encryptedReadKeys: [key.Read.1] permissions: writer inviteId: A.1.2 + - userAdd: + identity: C + permission: reader + encryptionKey: key.Enc.C + encryptedReadKeys: [key.Read.1] 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 + - identity: B aclChanges: - userJoin: identity: B @@ -55,56 +30,34 @@ changes: 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 + - identity: A + aclChanges: + - userRemove: + removedIdentity: B + newReadKey: key.Read.2 + identitiesLeft: [A, C] + readKey: key.Read.2 keys: Enc: - - A - - B - - Onetime1 + - name: A + value: generated + - name: B + value: generated + - name: C + value: generated + - name: Onetime1 + value: generated Sign: - - A - - B - - Onetime1 + - name: A + value: generated + - name: B + value: generated + - name: C + value: generated + - name: Onetime1 + value: generated 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] -orphans: - - "A.1.4" - - "B.1.2" -header: - firstChangeId: A.1.1 - isWorkspace: false + - name: 1 + value: generated + - name: 2 + value: generated diff --git a/pkg/acl/testutils/yamltests/validsnapshotexample.yml b/pkg/acl/testutils/yamltests/validsnapshotexample.yml deleted file mode 100644 index c75cda0a..00000000 --- a/pkg/acl/testutils/yamltests/validsnapshotexample.yml +++ /dev/null @@ -1,133 +0,0 @@ -tree: - author: A -changes: - - id: A.1.1 - 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 - 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 - 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 - - 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] -orphans: - - "A.1.3" - - "B.1.2" -header: - firstChangeId: A.1.1 - isWorkspace: false - diff --git a/pkg/acl/tree/change.go b/pkg/acl/tree/change.go new file mode 100644 index 00000000..e89cbae6 --- /dev/null +++ b/pkg/acl/tree/change.go @@ -0,0 +1,117 @@ +package tree + +import ( + "errors" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/gogo/protobuf/proto" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" +) + +var ( + ErrIncorrectSignature = errors.New("change has incorrect signature") + ErrIncorrectCID = errors.New("change has incorrect CID") +) + +type ChangeContent struct { + ChangesData proto.Marshaler + ACLData *aclpb.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 + PreviousIds []string + Id string + SnapshotId string + IsSnapshot bool + DecryptedChange []byte // TODO: check if we need it + ParsedModel interface{} // TODO: check if we need it + + // iterator helpers + visited bool + branchesFinished bool + + Content *aclpb.Change + Sign []byte +} + +func (ch *Change) ProtoChange() proto.Marshaler { + return ch.Content +} + +func (ch *Change) DecryptContents(key *symmetric.Key) error { + // if the document is already decrypted + if ch.Content.CurrentReadKeyHash == 0 { + return nil + } + decrypted, err := key.Decrypt(ch.Content.ChangesData) + if err != nil { + return fmt.Errorf("failed to decrypt changes data: %w", err) + } + + ch.DecryptedChange = decrypted + return nil +} + +func NewChangeFromRaw(rawChange *aclpb.RawChange) (*Change, error) { + unmarshalled := &aclpb.Change{} + err := proto.Unmarshal(rawChange.Payload, unmarshalled) + if err != nil { + return nil, err + } + + ch := NewChange(rawChange.Id, unmarshalled, rawChange.Signature) + return ch, nil +} + +func newVerifiedChangeFromRaw( + rawChange *aclpb.RawChange, + kch *keychain) (*Change, error) { + unmarshalled := &aclpb.Change{} + ch, err := NewChangeFromRaw(rawChange) + if err != nil { + return nil, err + } + + identityKey, err := kch.getOrAdd(unmarshalled.Identity) + if err != nil { + return nil, err + } + + res, err := identityKey.Verify(rawChange.Payload, rawChange.Signature) + if err != nil { + return nil, err + } + if !res { + return nil, ErrIncorrectSignature + } + + return ch, nil +} + +func NewChange(id string, ch *aclpb.Change, signature []byte) *Change { + return &Change{ + Next: nil, + PreviousIds: ch.TreeHeadIds, + Id: id, + Content: ch, + SnapshotId: ch.SnapshotBaseId, + IsSnapshot: ch.IsSnapshot, + Sign: signature, + } +} + +func (ch *Change) DecryptedChangeContent() []byte { + return ch.DecryptedChange +} + +func (ch *Change) Signature() []byte { + return ch.Sign +} + +func (ch *Change) CID() string { + return ch.Id +} diff --git a/pkg/acl/tree/changebuilder.go b/pkg/acl/tree/changebuilder.go new file mode 100644 index 00000000..bdba5382 --- /dev/null +++ b/pkg/acl/tree/changebuilder.go @@ -0,0 +1,126 @@ +package tree + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" + "github.com/gogo/protobuf/proto" + "time" +) + +const componentBuilder = "tree.changebuilder" + +type BuilderContent struct { + treeHeadIds []string + aclHeadId string + snapshotBaseId string + currentReadKeyHash uint64 + identity string + isSnapshot bool + signingKey signingkey.PrivKey + readKey *symmetric.Key + content proto.Marshaler +} + +type ChangeBuilder interface { + ConvertFromRaw(rawChange *aclpb.RawChange) (ch *Change, err error) + ConvertFromRawAndVerify(rawChange *aclpb.RawChange) (ch *Change, err error) + BuildContent(payload BuilderContent) (ch *Change, raw *aclpb.RawChange, err error) +} + +type changeBuilder struct { + keys *keychain +} + +func newChangeBuilder(keys *keychain) *changeBuilder { + return &changeBuilder{keys: keys} +} + +func (c *changeBuilder) ConvertFromRaw(rawChange *aclpb.RawChange) (ch *Change, err error) { + unmarshalled := &aclpb.Change{} + err = proto.Unmarshal(rawChange.Payload, unmarshalled) + if err != nil { + return nil, err + } + + ch = NewChange(rawChange.Id, unmarshalled, rawChange.Signature) + return +} + +func (c *changeBuilder) ConvertFromRawAndVerify(rawChange *aclpb.RawChange) (ch *Change, err error) { + unmarshalled := &aclpb.Change{} + ch, err = c.ConvertFromRaw(rawChange) + if err != nil { + return nil, err + } + + identityKey, err := c.keys.getOrAdd(unmarshalled.Identity) + if err != nil { + return + } + + // verifying signature + res, err := identityKey.Verify(rawChange.Payload, rawChange.Signature) + if err != nil { + return + } + if !res { + err = ErrIncorrectSignature + return + } + + // verifying ID + if !cid.VerifyCID(rawChange.Payload, rawChange.Id) { + err = ErrIncorrectCID + } + + return +} + +func (c *changeBuilder) BuildContent(payload BuilderContent) (ch *Change, raw *aclpb.RawChange, err error) { + aclChange := &aclpb.Change{ + TreeHeadIds: payload.treeHeadIds, + AclHeadId: payload.aclHeadId, + SnapshotBaseId: payload.snapshotBaseId, + CurrentReadKeyHash: payload.currentReadKeyHash, + Timestamp: int64(time.Now().Nanosecond()), + Identity: payload.identity, + IsSnapshot: payload.isSnapshot, + } + marshalledData, err := payload.content.Marshal() + if err != nil { + return + } + + encrypted, err := payload.readKey.Encrypt(marshalledData) + if err != nil { + return + } + aclChange.ChangesData = encrypted + + fullMarshalledChange, err := proto.Marshal(aclChange) + if err != nil { + return + } + + signature, err := payload.signingKey.Sign(fullMarshalledChange) + if err != nil { + return + } + + id, err := cid.NewCIDFromBytes(fullMarshalledChange) + if err != nil { + return + } + + ch = NewChange(id, aclChange, signature) + ch.ParsedModel = payload.content + + raw = &aclpb.RawChange{ + Payload: fullMarshalledChange, + Signature: signature, + Id: id, + } + return +} diff --git a/pkg/acl/tree/changevalidator.go b/pkg/acl/tree/changevalidator.go new file mode 100644 index 00000000..f0dfbdc7 --- /dev/null +++ b/pkg/acl/tree/changevalidator.go @@ -0,0 +1,58 @@ +package tree + +import ( + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" +) + +type ObjectTreeValidator interface { + // ValidateTree should always be entered while holding a read lock on ACLList + ValidateTree(tree *Tree, aclList list.ACLList) error +} + +type objectTreeValidator struct{} + +func newTreeValidator() ObjectTreeValidator { + return &objectTreeValidator{} +} + +func (v *objectTreeValidator) ValidateTree(tree *Tree, aclList list.ACLList) (err error) { + + var ( + perm list.UserPermissionPair + state = aclList.ACLState() + ) + + tree.Iterate(tree.RootId(), func(c *Change) (isContinue bool) { + // checking if the user could write + perm, err = state.PermissionsAtRecord(c.Content.AclHeadId, c.Content.Identity) + if err != nil { + return false + } + + if perm.Permission != aclpb.ACLChange_Writer && perm.Permission != aclpb.ACLChange_Admin { + err = list.ErrInsufficientPermissions + return false + } + + // checking if the change refers to later acl heads than its previous ids + for _, id := range c.PreviousIds { + prevChange := tree.attached[id] + if prevChange.Content.AclHeadId == c.Content.AclHeadId { + continue + } + var after bool + after, err = aclList.IsAfter(c.Content.AclHeadId, prevChange.Content.AclHeadId) + if err != nil { + return false + } + if !after { + err = fmt.Errorf("current acl head id (%s) should be after each of the previous ones (%s)", c.Content.AclHeadId, prevChange.Content.AclHeadId) + return false + } + } + return true + }) + return err +} diff --git a/pkg/acl/tree/descriptionparser.go b/pkg/acl/tree/descriptionparser.go new file mode 100644 index 00000000..c7ad3bfe --- /dev/null +++ b/pkg/acl/tree/descriptionparser.go @@ -0,0 +1,13 @@ +package tree + +type DescriptionParser interface { + ParseChange(*Change) ([]string, error) +} + +var NoOpDescriptionParser = noopDescriptionParser{} + +type noopDescriptionParser struct{} + +func (n noopDescriptionParser) ParseChange(change *Change) ([]string, error) { + return []string{"DOC"}, nil +} diff --git a/pkg/acl/tree/keychain.go b/pkg/acl/tree/keychain.go new file mode 100644 index 00000000..582a8102 --- /dev/null +++ b/pkg/acl/tree/keychain.go @@ -0,0 +1,31 @@ +package tree + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" +) + +type keychain struct { + decoder keys.Decoder + keys map[string]signingkey.PubKey +} + +func newKeychain() *keychain { + return &keychain{ + decoder: signingkey.NewEDPubKeyDecoder(), + keys: make(map[string]signingkey.PubKey), + } +} + +func (k *keychain) getOrAdd(identity string) (signingkey.PubKey, error) { + if key, exists := k.keys[identity]; exists { + return key, nil + } + res, err := k.decoder.DecodeFromString(identity) + if err != nil { + return nil, err + } + + k.keys[identity] = res.(signingkey.PubKey) + return res.(signingkey.PubKey), nil +} diff --git a/pkg/acl/tree/objecttree.go b/pkg/acl/tree/objecttree.go new file mode 100644 index 00000000..2fc0eaa5 --- /dev/null +++ b/pkg/acl/tree/objecttree.go @@ -0,0 +1,571 @@ +package tree + +import ( + "context" + "errors" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" + "go.uber.org/zap" + "sync" +) + +type ObjectTreeUpdateListener interface { + Update(tree ObjectTree) + Rebuild(tree ObjectTree) +} + +type RWLocker interface { + sync.Locker + RLock() + RUnlock() +} + +var ( + ErrHasInvalidChanges = errors.New("the change is invalid") + ErrNoCommonSnapshot = errors.New("trees doesn't have a common snapshot") +) + +type AddResultSummary int + +const ( + AddResultSummaryNothing AddResultSummary = iota + AddResultSummaryAppend + AddResultSummaryRebuild +) + +type AddResult struct { + OldHeads []string + Heads []string + Added []*aclpb.RawChange + + Summary AddResultSummary +} + +type ChangeIterateFunc = func(change *Change) bool +type ChangeConvertFunc = func(decrypted []byte) (any, error) + +type ObjectTree interface { + RWLocker + + ID() string + Header() *aclpb.Header + Heads() []string + Root() *Change + HasChange(string) bool + + Iterate(convert ChangeConvertFunc, iterate ChangeIterateFunc) error + IterateFrom(id string, convert ChangeConvertFunc, iterate ChangeIterateFunc) error + + SnapshotPath() []string + ChangesAfterCommonSnapshot(snapshotPath, heads []string) ([]*aclpb.RawChange, error) + + Storage() storage.TreeStorage + DebugDump() (string, error) + + AddContent(ctx context.Context, content SignableChangeContent) (*aclpb.RawChange, error) + AddRawChanges(ctx context.Context, changes ...*aclpb.RawChange) (AddResult, error) + + Close() error +} + +type objectTree struct { + treeStorage storage.TreeStorage + changeBuilder ChangeBuilder + updateListener ObjectTreeUpdateListener + validator ObjectTreeValidator + rawChangeLoader *rawChangeLoader + treeBuilder *treeBuilder + aclList list.ACLList + + id string + header *aclpb.Header + tree *Tree + + keys map[uint64]*symmetric.Key + + // buffers + difSnapshotBuf []*aclpb.RawChange + tmpChangesBuf []*Change + newSnapshotsBuf []*Change + notSeenIdxBuf []int + + snapshotPath []string + + sync.RWMutex +} + +type objectTreeDeps struct { + changeBuilder ChangeBuilder + treeBuilder *treeBuilder + treeStorage storage.TreeStorage + updateListener ObjectTreeUpdateListener + validator ObjectTreeValidator + rawChangeLoader *rawChangeLoader + aclList list.ACLList +} + +func defaultObjectTreeDeps( + treeStorage storage.TreeStorage, + listener ObjectTreeUpdateListener, + aclList list.ACLList) objectTreeDeps { + + keychain := newKeychain() + changeBuilder := newChangeBuilder(keychain) + treeBuilder := newTreeBuilder(treeStorage, changeBuilder) + return objectTreeDeps{ + changeBuilder: changeBuilder, + treeBuilder: treeBuilder, + treeStorage: treeStorage, + updateListener: listener, + validator: newTreeValidator(), + rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder), + aclList: aclList, + } +} + +func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) { + objTree := &objectTree{ + treeStorage: deps.treeStorage, + updateListener: deps.updateListener, + treeBuilder: deps.treeBuilder, + validator: deps.validator, + aclList: deps.aclList, + changeBuilder: deps.changeBuilder, + rawChangeLoader: deps.rawChangeLoader, + tree: nil, + keys: make(map[uint64]*symmetric.Key), + tmpChangesBuf: make([]*Change, 0, 10), + difSnapshotBuf: make([]*aclpb.RawChange, 0, 10), + notSeenIdxBuf: make([]int, 0, 10), + newSnapshotsBuf: make([]*Change, 0, 10), + } + + err := objTree.rebuildFromStorage(nil) + if err != nil { + return nil, err + } + storageHeads, err := objTree.treeStorage.Heads() + if err != nil { + return nil, err + } + + // comparing rebuilt heads with heads in storage + // in theory it can happen that we didn't set heads because the process has crashed + // therefore we want to set them later + if !slice.UnsortedEquals(storageHeads, objTree.tree.Heads()) { + log.With(zap.Strings("storage", storageHeads), zap.Strings("rebuilt", objTree.tree.Heads())). + Errorf("the heads in storage and objTree are different") + err = objTree.treeStorage.SetHeads(objTree.tree.Heads()) + if err != nil { + return nil, err + } + } + + objTree.id, err = objTree.treeStorage.ID() + if err != nil { + return nil, err + } + objTree.header, err = objTree.treeStorage.Header() + if err != nil { + return nil, err + } + + if objTree.updateListener != nil { + objTree.updateListener.Rebuild(objTree) + } + + return objTree, nil +} + +func BuildObjectTree(treeStorage storage.TreeStorage, listener ObjectTreeUpdateListener, aclList list.ACLList) (ObjectTree, error) { + deps := defaultObjectTreeDeps(treeStorage, listener, aclList) + return buildObjectTree(deps) +} + +func (ot *objectTree) rebuildFromStorage(newChanges []*Change) (err error) { + ot.treeBuilder.Reset() + + ot.tree, err = ot.treeBuilder.Build(newChanges) + if err != nil { + return + } + + // during building the tree we may have marked some changes as possible roots, + // but obviously they are not roots, because of the way how we construct the tree + ot.tree.clearPossibleRoots() + + return ot.validateTree() +} + +func (ot *objectTree) ID() string { + return ot.id +} + +func (ot *objectTree) Header() *aclpb.Header { + return ot.header +} + +func (ot *objectTree) Storage() storage.TreeStorage { + return ot.treeStorage +} + +func (ot *objectTree) AddContent(ctx context.Context, content SignableChangeContent) (rawChange *aclpb.RawChange, err error) { + defer func() { + if err == nil && ot.updateListener != nil { + ot.updateListener.Update(ot) + } + }() + + payload, err := ot.prepareBuilderContent(content) + if err != nil { + return + } + + objChange, rawChange, err := ot.changeBuilder.BuildContent(payload) + if content.IsSnapshot { + // clearing tree, because we already fixed everything in the last snapshot + ot.tree = &Tree{} + } + err = ot.tree.AddMergedHead(objChange) + if err != nil { + panic(err) + } + + err = ot.treeStorage.AddRawChange(rawChange) + if err != nil { + return + } + + err = ot.treeStorage.SetHeads([]string{objChange.Id}) + return +} + +func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt BuilderContent, err error) { + ot.aclList.RLock() + defer ot.aclList.RUnlock() + + state := ot.aclList.ACLState() // special method for own keys + readKey, err := state.CurrentReadKey() + if err != nil { + return + } + cnt = BuilderContent{ + treeHeadIds: ot.tree.Heads(), + aclHeadId: ot.aclList.Head().Id, + snapshotBaseId: ot.tree.RootId(), + currentReadKeyHash: state.CurrentReadKeyHash(), + identity: content.Identity, + isSnapshot: content.IsSnapshot, + signingKey: content.Key, + readKey: readKey, + content: content.Proto, + } + return +} + +func (ot *objectTree) AddRawChanges(ctx context.Context, rawChanges ...*aclpb.RawChange) (addResult AddResult, err error) { + var mode Mode + mode, addResult, err = ot.addRawChanges(ctx, rawChanges...) + if err != nil { + return + } + + // reducing tree if we have new roots + ot.tree.reduceTree() + + // adding to database all the added changes only after they are good + for _, ch := range addResult.Added { + err = ot.treeStorage.AddRawChange(ch) + if err != nil { + return + } + } + + // setting heads + err = ot.treeStorage.SetHeads(ot.tree.Heads()) + if err != nil { + return + } + + if ot.updateListener == nil { + return + } + + switch mode { + case Append: + ot.updateListener.Update(ot) + case Rebuild: + ot.updateListener.Rebuild(ot) + default: + break + } + return +} + +func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*aclpb.RawChange) (mode Mode, addResult AddResult, err error) { + // resetting buffers + ot.tmpChangesBuf = ot.tmpChangesBuf[:0] + ot.notSeenIdxBuf = ot.notSeenIdxBuf[:0] + ot.difSnapshotBuf = ot.difSnapshotBuf[:0] + ot.newSnapshotsBuf = ot.newSnapshotsBuf[:0] + + headsCopy := func() []string { + newHeads := make([]string, 0, len(ot.tree.Heads())) + newHeads = append(newHeads, ot.tree.Heads()...) + return newHeads + } + + // this will be returned to client, so we shouldn't use buffer here + prevHeadsCopy := headsCopy() + + // filtering changes, verifying and unmarshalling them + for idx, ch := range rawChanges { + if ot.HasChange(ch.Id) { + continue + } + + var change *Change + change, err = ot.changeBuilder.ConvertFromRawAndVerify(ch) + if err != nil { + return + } + + if change.IsSnapshot { + ot.newSnapshotsBuf = append(ot.newSnapshotsBuf, change) + } + ot.tmpChangesBuf = append(ot.tmpChangesBuf, change) + ot.notSeenIdxBuf = append(ot.notSeenIdxBuf, idx) + } + + // if no new changes, then returning + if len(ot.notSeenIdxBuf) == 0 { + addResult = AddResult{ + OldHeads: prevHeadsCopy, + Heads: prevHeadsCopy, + Summary: AddResultSummaryNothing, + } + return + } + + // returns changes that we added to the tree + getAddedChanges := func() []*aclpb.RawChange { + var added []*aclpb.RawChange + for _, idx := range ot.notSeenIdxBuf { + rawChange := rawChanges[idx] + if _, exists := ot.tree.attached[rawChange.Id]; exists { + added = append(added, rawChange) + } + } + return added + } + + rollback := func() { + for _, ch := range ot.tmpChangesBuf { + if _, exists := ot.tree.attached[ch.Id]; exists { + delete(ot.tree.attached, ch.Id) + } else if _, exists := ot.tree.unAttached[ch.Id]; exists { + delete(ot.tree.unAttached, ch.Id) + } + } + } + + // checks if we need to go to database + isOldSnapshot := func(ch *Change) bool { + if ch.SnapshotId == ot.tree.RootId() { + return false + } + for _, sn := range ot.newSnapshotsBuf { + // if change refers to newly received snapshot + if ch.SnapshotId == sn.Id { + return false + } + } + return true + } + + // checking if we have some changes with different snapshot and then rebuilding + for _, ch := range ot.tmpChangesBuf { + if isOldSnapshot(ch) { + err = ot.rebuildFromStorage(ot.tmpChangesBuf) + if err != nil { + // rebuilding without new changes + ot.rebuildFromStorage(nil) + return + } + + addResult = AddResult{ + OldHeads: prevHeadsCopy, + Heads: headsCopy(), + Added: getAddedChanges(), + Summary: AddResultSummaryRebuild, + } + return + } + } + + // normal mode of operation, where we don't need to rebuild from database + mode = ot.tree.Add(ot.tmpChangesBuf...) + switch mode { + case Nothing: + addResult = AddResult{ + OldHeads: prevHeadsCopy, + Heads: prevHeadsCopy, + Summary: AddResultSummaryNothing, + } + return + + default: + // just rebuilding the state from start without reloading everything from tree storage + // as an optimization we could've started from current heads, but I didn't implement that + err = ot.validateTree() + if err != nil { + rollback() + err = ErrHasInvalidChanges + return + } + + addResult = AddResult{ + OldHeads: prevHeadsCopy, + Heads: headsCopy(), + Added: getAddedChanges(), + Summary: AddResultSummaryAppend, + } + } + return +} + +func (ot *objectTree) Iterate(convert ChangeConvertFunc, iterate ChangeIterateFunc) (err error) { + return ot.IterateFrom(ot.tree.RootId(), convert, iterate) +} + +func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate ChangeIterateFunc) (err error) { + if convert == nil { + ot.tree.Iterate(id, iterate) + return + } + + ot.tree.Iterate(ot.tree.RootId(), func(c *Change) (isContinue bool) { + var model any + if c.ParsedModel != nil { + return iterate(c) + } + readKey, exists := ot.keys[c.Content.CurrentReadKeyHash] + if !exists { + err = list.ErrNoReadKey + return false + } + + var decrypted []byte + decrypted, err = readKey.Decrypt(c.Content.GetChangesData()) + if err != nil { + return false + } + + model, err = convert(decrypted) + if err != nil { + return false + } + + c.ParsedModel = model + return iterate(c) + }) + return +} + +func (ot *objectTree) HasChange(s string) bool { + _, attachedExists := ot.tree.attached[s] + _, unattachedExists := ot.tree.unAttached[s] + return attachedExists || unattachedExists +} + +func (ot *objectTree) Heads() []string { + return ot.tree.Heads() +} + +func (ot *objectTree) Root() *Change { + return ot.tree.Root() +} + +func (ot *objectTree) Close() error { + return nil +} + +func (ot *objectTree) SnapshotPath() []string { + // TODO: Add error as return parameter + if ot.snapshotPathIsActual() { + return ot.snapshotPath + } + + var path []string + // TODO: think that the user may have not all of the snapshots locally + currentSnapshotId := ot.tree.RootId() + for currentSnapshotId != "" { + sn, err := ot.treeBuilder.loadChange(currentSnapshotId) + if err != nil { + break + } + path = append(path, currentSnapshotId) + currentSnapshotId = sn.SnapshotId + } + ot.snapshotPath = path + + return path +} + +func (ot *objectTree) ChangesAfterCommonSnapshot(theirPath, theirHeads []string) ([]*aclpb.RawChange, error) { + var ( + needFullDocument = len(theirPath) == 0 + ourPath = ot.SnapshotPath() + // by default returning everything we have from start + commonSnapshot = ourPath[len(ourPath)-1] + err error + ) + + // if this is non-empty request + if !needFullDocument { + commonSnapshot, err = commonSnapshotForTwoPaths(ourPath, theirPath) + if err != nil { + return nil, err + } + } + + if commonSnapshot == ot.tree.RootId() { + return ot.getChangesFromTree(theirHeads) + } else { + return ot.getChangesFromDB(commonSnapshot, theirHeads) + } +} + +func (ot *objectTree) getChangesFromTree(theirHeads []string) (rawChanges []*aclpb.RawChange, err error) { + return ot.rawChangeLoader.LoadFromTree(ot.tree, theirHeads) +} + +func (ot *objectTree) getChangesFromDB(commonSnapshot string, theirHeads []string) (rawChanges []*aclpb.RawChange, err error) { + return ot.rawChangeLoader.LoadFromStorage(commonSnapshot, ot.tree.headIds, theirHeads) +} + +func (ot *objectTree) snapshotPathIsActual() bool { + return len(ot.snapshotPath) != 0 && ot.snapshotPath[0] == ot.tree.RootId() +} + +func (ot *objectTree) validateTree() error { + ot.aclList.RLock() + defer ot.aclList.RUnlock() + state := ot.aclList.ACLState() + + // just not to take lock many times, updating the key map from aclList + if len(ot.keys) != len(state.UserReadKeys()) { + for key, value := range state.UserReadKeys() { + ot.keys[key] = value + } + } + + return ot.validator.ValidateTree(ot.tree, ot.aclList) +} + +func (ot *objectTree) DebugDump() (string, error) { + return ot.tree.Graph(NoOpDescriptionParser) +} diff --git a/pkg/acl/tree/objecttree_test.go b/pkg/acl/tree/objecttree_test.go new file mode 100644 index 00000000..a27c9cc1 --- /dev/null +++ b/pkg/acl/tree/objecttree_test.go @@ -0,0 +1,476 @@ +package tree + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +type mockChangeCreator struct{} + +func (c *mockChangeCreator) createRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *aclpb.RawChange { + aclChange := &aclpb.Change{ + TreeHeadIds: prevIds, + AclHeadId: aclId, + SnapshotBaseId: snapshotId, + ChangesData: nil, + IsSnapshot: isSnapshot, + } + res, _ := aclChange.Marshal() + return &aclpb.RawChange{ + Payload: res, + Signature: nil, + Id: id, + } +} + +func (c *mockChangeCreator) createNewTreeStorage(treeId, aclListId, aclHeadId, firstChangeId string) storage.TreeStorage { + firstChange := c.createRaw(firstChangeId, aclHeadId, "", true) + header := &aclpb.Header{ + FirstId: firstChangeId, + AclListId: aclListId, + WorkspaceId: "", + DocType: aclpb.Header_DocTree, + } + treeStorage, _ := storage.NewInMemoryTreeStorage(treeId, header, []string{firstChangeId}, []*aclpb.RawChange{firstChange}) + return treeStorage +} + +type mockChangeBuilder struct{} + +func (c *mockChangeBuilder) ConvertFromRaw(rawChange *aclpb.RawChange) (ch *Change, err error) { + unmarshalled := &aclpb.Change{} + err = proto.Unmarshal(rawChange.Payload, unmarshalled) + if err != nil { + return nil, err + } + + ch = NewChange(rawChange.Id, unmarshalled, rawChange.Signature) + return +} +func (c *mockChangeBuilder) ConvertFromRawAndVerify(rawChange *aclpb.RawChange) (ch *Change, err error) { + return c.ConvertFromRaw(rawChange) +} + +func (c *mockChangeBuilder) BuildContent(payload BuilderContent) (ch *Change, raw *aclpb.RawChange, err error) { + panic("implement me") +} + +type mockChangeValidator struct{} + +func (m *mockChangeValidator) ValidateTree(tree *Tree, aclList list.ACLList) error { + return nil +} + +type testTreeContext struct { + aclList list.ACLList + treeStorage storage.TreeStorage + changeBuilder *mockChangeBuilder + changeCreator *mockChangeCreator + objTree ObjectTree +} + +func prepareACLList(t *testing.T) list.ACLList { + st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml") + require.NoError(t, err, "building storage should not result in error") + + aclList, err := list.BuildACLList(signingkey.NewEDPubKeyDecoder(), st) + require.NoError(t, err, "building acl list should be without error") + + return aclList +} + +func prepareTreeContext(t *testing.T, aclList list.ACLList) testTreeContext { + changeCreator := &mockChangeCreator{} + treeStorage := changeCreator.createNewTreeStorage("treeId", aclList.ID(), aclList.Head().Id, "0") + changeBuilder := &mockChangeBuilder{} + deps := objectTreeDeps{ + changeBuilder: changeBuilder, + treeBuilder: newTreeBuilder(treeStorage, changeBuilder), + treeStorage: treeStorage, + updateListener: nil, + rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder), + validator: &mockChangeValidator{}, + aclList: aclList, + } + + // check build + objTree, err := buildObjectTree(deps) + require.NoError(t, err, "building tree should be without error") + + // check tree iterate + var iterChangesId []string + err = objTree.Iterate(nil, func(change *Change) bool { + iterChangesId = append(iterChangesId, change.Id) + return true + }) + require.NoError(t, err, "iterate should be without error") + assert.Equal(t, []string{"0"}, iterChangesId) + return testTreeContext{ + aclList: aclList, + treeStorage: treeStorage, + changeBuilder: changeBuilder, + changeCreator: changeCreator, + objTree: objTree, + } +} + +func TestObjectTree(t *testing.T) { + aclList := prepareACLList(t) + + t.Run("add simple", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + treeStorage := ctx.treeStorage + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + } + res, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + // check result + assert.Equal(t, []string{"0"}, res.OldHeads) + assert.Equal(t, []string{"2"}, res.Heads) + assert.Equal(t, len(rawChanges), len(res.Added)) + assert.Equal(t, AddResultSummaryAppend, res.Summary) + + // check tree heads + assert.Equal(t, []string{"2"}, objTree.Heads()) + + // check tree iterate + var iterChangesId []string + err = objTree.Iterate(nil, func(change *Change) bool { + iterChangesId = append(iterChangesId, change.Id) + return true + }) + require.NoError(t, err, "iterate should be without error") + assert.Equal(t, []string{"0", "1", "2"}, iterChangesId) + + // check storage + heads, _ := treeStorage.Heads() + assert.Equal(t, []string{"2"}, heads) + + for _, ch := range rawChanges { + raw, err := treeStorage.GetRawChange(context.Background(), ch.Id) + assert.NoError(t, err, "storage should have all the changes") + assert.Equal(t, ch, raw, "the changes in the storage should be the same") + } + }) + + t.Run("add no new changes", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("0", aclList.Head().Id, "", true, ""), + } + res, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + // check result + assert.Equal(t, []string{"0"}, res.OldHeads) + assert.Equal(t, []string{"0"}, res.Heads) + assert.Equal(t, 0, len(res.Added)) + + // check tree heads + assert.Equal(t, []string{"0"}, objTree.Heads()) + }) + + t.Run("add unattachable changes", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + } + res, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + // check result + assert.Equal(t, []string{"0"}, res.OldHeads) + assert.Equal(t, []string{"0"}, res.Heads) + assert.Equal(t, 0, len(res.Added)) + assert.Equal(t, AddResultSummaryNothing, res.Summary) + + // check tree heads + assert.Equal(t, []string{"0"}, objTree.Heads()) + }) + + t.Run("add new snapshot simple", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + treeStorage := ctx.treeStorage + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + changeCreator.createRaw("4", aclList.Head().Id, "3", false, "3"), + } + res, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + // check result + assert.Equal(t, []string{"0"}, res.OldHeads) + assert.Equal(t, []string{"4"}, res.Heads) + assert.Equal(t, len(rawChanges), len(res.Added)) + assert.Equal(t, AddResultSummaryAppend, res.Summary) + + // check tree heads + assert.Equal(t, []string{"4"}, objTree.Heads()) + + // check tree iterate + var iterChangesId []string + err = objTree.Iterate(nil, func(change *Change) bool { + iterChangesId = append(iterChangesId, change.Id) + return true + }) + require.NoError(t, err, "iterate should be without error") + assert.Equal(t, []string{"3", "4"}, iterChangesId) + assert.Equal(t, "3", objTree.Root().Id) + + // check storage + heads, _ := treeStorage.Heads() + assert.Equal(t, []string{"4"}, heads) + + for _, ch := range rawChanges { + raw, err := treeStorage.GetRawChange(context.Background(), ch.Id) + assert.NoError(t, err, "storage should have all the changes") + assert.Equal(t, ch, raw, "the changes in the storage should be the same") + } + }) + + t.Run("snapshot path", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + } + _, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + snapshotPath := objTree.SnapshotPath() + assert.Equal(t, []string{"3", "0"}, snapshotPath) + + assert.Equal(t, true, objTree.(*objectTree).snapshotPathIsActual()) + }) + + t.Run("changes from tree after common snapshot complex", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"), + changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"), + } + + _, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + require.Equal(t, "0", objTree.Root().Id) + + t.Run("all changes from tree", func(t *testing.T) { + changes, err := objTree.ChangesAfterCommonSnapshot([]string{"3", "0"}, []string{}) + require.NoError(t, err, "changes after common snapshot should be without error") + + changeIds := make(map[string]struct{}) + for _, ch := range changes { + changeIds[ch.Id] = struct{}{} + } + + for _, raw := range rawChanges { + _, ok := changeIds[raw.Id] + assert.Equal(t, true, ok) + } + _, ok := changeIds["0"] + assert.Equal(t, true, ok) + }) + + t.Run("changes from tree after 1", func(t *testing.T) { + changes, err := objTree.ChangesAfterCommonSnapshot([]string{"3", "0"}, []string{"1"}) + require.NoError(t, err, "changes after common snapshot should be without error") + + changeIds := make(map[string]struct{}) + for _, ch := range changes { + changeIds[ch.Id] = struct{}{} + } + + for _, id := range []string{"2", "3", "4", "5", "6"} { + _, ok := changeIds[id] + assert.Equal(t, true, ok) + } + for _, id := range []string{"0", "1"} { + _, ok := changeIds[id] + assert.Equal(t, false, ok) + } + }) + + t.Run("changes from tree after 5", func(t *testing.T) { + changes, err := objTree.ChangesAfterCommonSnapshot([]string{"3", "0"}, []string{"5"}) + require.NoError(t, err, "changes after common snapshot should be without error") + + changeIds := make(map[string]struct{}) + for _, ch := range changes { + changeIds[ch.Id] = struct{}{} + } + + for _, id := range []string{"2", "3", "4", "6"} { + _, ok := changeIds[id] + assert.Equal(t, true, ok) + } + for _, id := range []string{"0", "1", "5"} { + _, ok := changeIds[id] + assert.Equal(t, false, ok) + } + }) + }) + + t.Run("changes after common snapshot db complex", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"), + changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"), + // main difference from tree example + changeCreator.createRaw("6", aclList.Head().Id, "0", true, "3", "4", "5"), + } + + _, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + require.Equal(t, "6", objTree.Root().Id) + + t.Run("all changes from db", func(t *testing.T) { + changes, err := objTree.ChangesAfterCommonSnapshot([]string{"3", "0"}, []string{}) + require.NoError(t, err, "changes after common snapshot should be without error") + + changeIds := make(map[string]struct{}) + for _, ch := range changes { + changeIds[ch.Id] = struct{}{} + } + + for _, raw := range rawChanges { + _, ok := changeIds[raw.Id] + assert.Equal(t, true, ok) + } + _, ok := changeIds["0"] + assert.Equal(t, true, ok) + }) + + t.Run("changes from tree db 1", func(t *testing.T) { + changes, err := objTree.ChangesAfterCommonSnapshot([]string{"3", "0"}, []string{"1"}) + require.NoError(t, err, "changes after common snapshot should be without error") + + changeIds := make(map[string]struct{}) + for _, ch := range changes { + changeIds[ch.Id] = struct{}{} + } + + for _, id := range []string{"2", "3", "4", "5", "6"} { + _, ok := changeIds[id] + assert.Equal(t, true, ok) + } + for _, id := range []string{"0", "1"} { + _, ok := changeIds[id] + assert.Equal(t, false, ok) + } + }) + + t.Run("changes from tree db 5", func(t *testing.T) { + changes, err := objTree.ChangesAfterCommonSnapshot([]string{"3", "0"}, []string{"5"}) + require.NoError(t, err, "changes after common snapshot should be without error") + + changeIds := make(map[string]struct{}) + for _, ch := range changes { + changeIds[ch.Id] = struct{}{} + } + + for _, id := range []string{"2", "3", "4", "6"} { + _, ok := changeIds[id] + assert.Equal(t, true, ok) + } + for _, id := range []string{"0", "1", "5"} { + _, ok := changeIds[id] + assert.Equal(t, false, ok) + } + }) + }) + + t.Run("add new changes related to previous snapshot", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + treeStorage := ctx.treeStorage + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + } + res, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + require.Equal(t, "3", objTree.Root().Id) + + rawChanges = []*aclpb.RawChange{ + changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"), + changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"), + } + res, err = objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + // check result + assert.Equal(t, []string{"3"}, res.OldHeads) + assert.Equal(t, []string{"6"}, res.Heads) + assert.Equal(t, len(rawChanges), len(res.Added)) + assert.Equal(t, AddResultSummaryRebuild, res.Summary) + + // check tree heads + assert.Equal(t, []string{"6"}, objTree.Heads()) + + // check tree iterate + var iterChangesId []string + err = objTree.Iterate(nil, func(change *Change) bool { + iterChangesId = append(iterChangesId, change.Id) + return true + }) + require.NoError(t, err, "iterate should be without error") + assert.Equal(t, []string{"0", "1", "2", "3", "4", "5", "6"}, iterChangesId) + assert.Equal(t, "0", objTree.Root().Id) + + // check storage + heads, _ := treeStorage.Heads() + assert.Equal(t, []string{"6"}, heads) + + for _, ch := range rawChanges { + raw, err := treeStorage.GetRawChange(context.Background(), ch.Id) + assert.NoError(t, err, "storage should have all the changes") + assert.Equal(t, ch, raw, "the changes in the storage should be the same") + } + }) +} diff --git a/pkg/acl/tree/rawloader.go b/pkg/acl/tree/rawloader.go new file mode 100644 index 00000000..43854a29 --- /dev/null +++ b/pkg/acl/tree/rawloader.go @@ -0,0 +1,255 @@ +package tree + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "time" +) + +type rawChangeLoader struct { + treeStorage storage.TreeStorage + changeBuilder ChangeBuilder + + // buffers + idStack []string + cache map[string]rawCacheEntry +} + +type rawCacheEntry struct { + change *Change + rawChange *aclpb.RawChange + position int +} + +func newRawChangeLoader(treeStorage storage.TreeStorage, changeBuilder ChangeBuilder) *rawChangeLoader { + return &rawChangeLoader{ + treeStorage: treeStorage, + changeBuilder: changeBuilder, + } +} + +func (r *rawChangeLoader) LoadFromTree(t *Tree, breakpoints []string) ([]*aclpb.RawChange, error) { + var stack []*Change + for _, h := range t.headIds { + stack = append(stack, t.attached[h]) + } + + convert := func(chs []*Change) (rawChanges []*aclpb.RawChange, err error) { + for _, ch := range chs { + var marshalled []byte + marshalled, err = ch.Content.Marshal() + if err != nil { + return + } + + raw := &aclpb.RawChange{ + Payload: marshalled, + Signature: ch.Signature(), + Id: ch.Id, + } + rawChanges = append(rawChanges, raw) + } + return + } + + // getting all changes that we visit + var results []*Change + rootVisited := false + t.dfsPrev( + stack, + breakpoints, + func(ch *Change) bool { + results = append(results, ch) + return true + }, + func(visited []*Change) { + if t.root.visited { + rootVisited = true + } + }, + ) + + // if we stopped at breakpoints or there are no breakpoints + if !rootVisited || len(breakpoints) == 0 { + // in this case we will add root if there are no breakpoints + return convert(results) + } + + // now starting from breakpoints + stack = stack[:0] + for _, h := range breakpoints { + stack = append(stack, t.attached[h]) + } + + // doing another dfs to get all changes before breakpoints, we need to exclude them from results + // if we don't have some breakpoints we will just ignore them + t.dfsPrev( + stack, + []string{}, + func(ch *Change) bool { + return true + }, + func(visited []*Change) { + results = discardFromSlice(results, func(change *Change) bool { + return change.visited + }) + }, + ) + + // otherwise we want to exclude everything that wasn't in breakpoints + return convert(results) +} + +func (r *rawChangeLoader) LoadFromStorage(commonSnapshot string, heads, breakpoints []string) ([]*aclpb.RawChange, error) { + // resetting cache + r.cache = make(map[string]rawCacheEntry) + defer func() { + r.cache = nil + }() + + existingBreakpoints := make([]string, 0, len(breakpoints)) + for _, b := range breakpoints { + entry, err := r.loadEntry(b) + if err != nil { + continue + } + entry.position = -1 + r.cache[b] = entry + existingBreakpoints = append(existingBreakpoints, b) + } + r.cache[commonSnapshot] = rawCacheEntry{position: -1} + + dfs := func( + commonSnapshot string, + heads []string, + startCounter int, + shouldVisit func(counter int, mapExists bool) bool, + visit func(entry rawCacheEntry) rawCacheEntry) bool { + + // resetting stack + r.idStack = r.idStack[:0] + r.idStack = append(r.idStack, heads...) + + commonSnapshotVisited := false + var err error + for len(r.idStack) > 0 { + id := r.idStack[len(r.idStack)-1] + r.idStack = r.idStack[:len(r.idStack)-1] + + entry, exists := r.cache[id] + if !shouldVisit(entry.position, exists) { + continue + } + if !exists { + entry, err = r.loadEntry(id) + if err != nil { + continue + } + } + + // setting the counter when we visit + r.cache[id] = visit(entry) + + for _, prev := range entry.change.PreviousIds { + if prev == commonSnapshot { + commonSnapshotVisited = true + break + } + entry, exists = r.cache[prev] + if !shouldVisit(entry.position, exists) { + continue + } + r.idStack = append(r.idStack, prev) + } + } + return commonSnapshotVisited + } + + // preparing first pass + r.idStack = append(r.idStack, heads...) + var buffer []*aclpb.RawChange + + rootVisited := dfs(commonSnapshot, heads, 0, + func(counter int, mapExists bool) bool { + return !mapExists + }, + func(entry rawCacheEntry) rawCacheEntry { + buffer = append(buffer, entry.rawChange) + entry.position = len(buffer) - 1 + return entry + }) + + // checking if we stopped at breakpoints + if !rootVisited { + return buffer, nil + } + + // if there are no breakpoints then we should load root also + if len(breakpoints) == 0 { + common, err := r.loadEntry(commonSnapshot) + if err != nil { + return nil, err + } + buffer = append(buffer, common.rawChange) + return buffer, nil + } + + // marking all visited as nil + dfs(commonSnapshot, existingBreakpoints, len(buffer), + func(counter int, mapExists bool) bool { + return !mapExists || counter < len(buffer) + }, + func(entry rawCacheEntry) rawCacheEntry { + if entry.position != -1 { + buffer[entry.position] = nil + } + entry.position = len(buffer) + 1 + return entry + }) + + // discarding visited + buffer = discardFromSlice(buffer, func(change *aclpb.RawChange) bool { + return change == nil + }) + + return buffer, nil +} + +func (r *rawChangeLoader) loadEntry(id string) (entry rawCacheEntry, err error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + rawChange, err := r.treeStorage.GetRawChange(ctx, id) + if err != nil { + return + } + + change, err := r.changeBuilder.ConvertFromRaw(rawChange) + if err != nil { + return + } + entry = rawCacheEntry{ + change: change, + rawChange: rawChange, + } + return +} + +func discardFromSlice[T any](elements []T, isDiscarded func(T) bool) []T { + var ( + finishedIdx = 0 + currentIdx = 0 + ) + for currentIdx < len(elements) { + if !isDiscarded(elements[currentIdx]) { + if finishedIdx != currentIdx { + elements[finishedIdx] = elements[currentIdx] + } + finishedIdx++ + } + currentIdx++ + } + elements = elements[:finishedIdx] + return elements +} diff --git a/pkg/acl/tree/signablecontent.go b/pkg/acl/tree/signablecontent.go new file mode 100644 index 00000000..f97ed44a --- /dev/null +++ b/pkg/acl/tree/signablecontent.go @@ -0,0 +1,13 @@ +package tree + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" + "github.com/gogo/protobuf/proto" +) + +type SignableChangeContent struct { + Proto proto.Marshaler + Key signingkey.PrivKey + Identity string + IsSnapshot bool +} diff --git a/pkg/acl/acltree/tree.go b/pkg/acl/tree/tree.go similarity index 67% rename from pkg/acl/acltree/tree.go rename to pkg/acl/tree/tree.go index e47ed212..549d7f84 100644 --- a/pkg/acl/acltree/tree.go +++ b/pkg/acl/tree/tree.go @@ -1,10 +1,9 @@ -package acltree +package tree import ( "bytes" "crypto/md5" "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "sort" ) @@ -16,7 +15,6 @@ const ( Nothing ) -// TODO: consider abstracting into separate package with iterator, remove type Tree struct { root *Change headIds []string @@ -26,18 +24,15 @@ type Tree struct { // missed id -> list of dependency ids waitList map[string][]string invalidChanges map[string]struct{} + possibleRoots []*Change // bufs - iterCompBuf []*Change - iterQueue []*Change + visitedBuf []*Change + stackBuf []*Change duplicateEvents int } -func (t *Tree) GetUnattachedChanges(changes ...*Change) []*Change { - return nil -} - func (t *Tree) RootId() string { if t.root != nil { return t.root.Id @@ -62,6 +57,30 @@ func (t *Tree) AddFast(changes ...*Change) { t.updateHeads() } +func (t *Tree) AddMergedHead(c *Change) error { + // check that it was not inserted previously + if _, ok := t.attached[c.Id]; ok { + return fmt.Errorf("change already exists") // TODO: named error + } else if _, ok := t.unAttached[c.Id]; ok { + return fmt.Errorf("change already exists") + } + + // check that it was attached after adding + if !t.add(c) { + return fmt.Errorf("change is not attached") + } + + // check that previous heads have new change as next + for _, prevHead := range t.headIds { + head := t.attached[prevHead] + if len(head.Next) != 1 || head.Next[0].Id != c.Id { + return fmt.Errorf("this is not a new head") + } + } + t.headIds = []string{c.Id} + return nil +} + func (t *Tree) Add(changes ...*Change) (mode Mode) { var beforeHeadIds = t.headIds var attached bool @@ -96,6 +115,7 @@ func (t *Tree) Add(changes ...*Change) (mode Mode) { return Append } +// RemoveInvalidChange removes all the changes that are descendants of id func (t *Tree) RemoveInvalidChange(id string) { stack := []string{id} // removing all children of this id (either next or unattached) @@ -112,6 +132,7 @@ func (t *Tree) RemoveInvalidChange(id string) { t.invalidChanges[top] = struct{}{} if rem, exists = t.unAttached[top]; exists { delete(t.unAttached, top) + // TODO: delete waitlist, this can only help for memory/performance } else if rem, exists = t.attached[top]; exists { // remove from all prev changes for _, id := range rem.PreviousIds { @@ -129,9 +150,6 @@ func (t *Tree) RemoveInvalidChange(id string) { } delete(t.attached, top) } - for _, el := range rem.Unattached { - stack = append(stack, el.Id) - } for _, el := range rem.Next { stack = append(stack, el.Id) } @@ -155,6 +173,7 @@ func (t *Tree) add(c *Change) (attached bool) { t.unAttached = make(map[string]*Change) t.waitList = make(map[string][]string) t.invalidChanges = make(map[string]struct{}) + t.possibleRoots = make([]*Change, 0, 10) return true } if len(c.PreviousIds) > 1 { @@ -163,15 +182,11 @@ func (t *Tree) add(c *Change) (attached bool) { // 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) + if _, ok := t.attached[pid]; ok { continue } attached = false - if prev, ok := t.unAttached[pid]; ok { - prev.Unattached = append(prev.Unattached, c) - continue - } + // updating wait list for either unseen or unAttached changes wl := t.waitList[pid] wl = append(wl, c.Id) t.waitList[pid] = wl @@ -179,11 +194,6 @@ func (t *Tree) add(c *Change) (attached bool) { 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 @@ -197,6 +207,7 @@ func (t *Tree) canAttach(c *Change) (attach bool) { for _, id := range c.PreviousIds { if _, exists := t.attached[id]; !exists { attach = false + break } } return @@ -207,40 +218,46 @@ func (t *Tree) attach(c *Change, newEl bool) { if !newEl { delete(t.unAttached, c.Id) } + if c.IsSnapshot { + t.possibleRoots = append(t.possibleRoots, c) + } // add next to all prev changes for _, id := range c.PreviousIds { - // prev id must be attached if we attach this id + // prev id must already be attached if we attach this id, so we don't need to check if it exists 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 + // appending c to next changes of all previous changes + if len(prev.Next) == 0 || prev.Next[len(prev.Next)-1].Id <= c.Id { + prev.Next = append(prev.Next, c) + } else { + // inserting in correct position, before the change which is greater or equal + insertIdx := 0 + for idx, el := range prev.Next { + if el.Id >= c.Id { + insertIdx = idx + break + } } + prev.Next = append(prev.Next[:insertIdx+1], prev.Next[insertIdx:]...) + prev.Next[insertIdx] = c } } + // TODO: as a future optimization we can actually sort next later after we finished building the tree // clearing wait list if waitIds, ok := t.waitList[c.Id]; ok { for _, wid := range waitIds { + // next can only be in unAttached, because if next is attached then previous (we) are attached + // which is obviously not true, because we are attaching previous only now next := t.unAttached[wid] if t.canAttach(next) { t.attach(next, false) } + // if we can't attach next that means that some other change will trigger attachment later, + // so we don't care about those changes } 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) { @@ -254,29 +271,48 @@ func (t *Tree) after(id1, id2 string) (found bool) { 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{} +func (t *Tree) dfsPrev(stack []*Change, breakpoints []string, visit func(ch *Change) (isContinue bool), afterVisit func([]*Change)) { + t.visitedBuf = t.visitedBuf[:0] + + // setting breakpoints as visited + for _, breakpoint := range breakpoints { + if ch, ok := t.attached[breakpoint]; ok { + ch.visited = true + t.visitedBuf = append(t.visitedBuf, ch) + } + } + + defer func() { + afterVisit(t.visitedBuf) + for _, ch := range t.visitedBuf { + ch.visited = false + } + }() for len(stack) > 0 { ch := stack[len(stack)-1] stack = stack[:len(stack)-1] - if _, exists := uniqMap[ch.Id]; exists { + if ch.visited { continue } - uniqMap[ch.Id] = ch + ch.visited = true + t.visitedBuf = append(t.visitedBuf, ch) - for _, prev := range ch.PreviousIds { - stack = append(stack, t.attached[prev]) + for _, prevId := range ch.PreviousIds { + prevCh := t.attached[prevId] + if !prevCh.visited { + stack = append(stack, prevCh) + } + } + if !visit(ch) { + return } } - return uniqMap } func (t *Tree) updateHeads() { - var newHeadIds, newMetaHeadIds []string + var newHeadIds []string t.iterate(t.root, func(c *Change) (isContinue bool) { if len(c.Next) == 0 { newHeadIds = append(newHeadIds, c.Id) @@ -284,37 +320,7 @@ func (t *Tree) updateHeads() { return true }) t.headIds = newHeadIds - t.metaHeadIds = newMetaHeadIds sort.Strings(t.headIds) - 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)) { @@ -381,6 +387,14 @@ func (t *Tree) Heads() []string { return t.headIds } +func (t *Tree) HeadsChanges() []*Change { + var heads []*Change + for _, head := range t.headIds { + heads = append(heads, t.attached[head]) + } + return heads +} + func (t *Tree) String() string { var buf = bytes.NewBuffer(nil) t.Iterate(t.RootId(), func(c *Change) (isContinue bool) { @@ -400,17 +414,3 @@ func (t *Tree) String() 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/pkg/acl/tree/tree_test.go b/pkg/acl/tree/tree_test.go new file mode 100644 index 00000000..ff228e43 --- /dev/null +++ b/pkg/acl/tree/tree_test.go @@ -0,0 +1,325 @@ +package tree + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "math/rand" + "testing" + "time" +) + +func newChange(id string, snapshotId string, prevIds ...string) *Change { + return &Change{ + PreviousIds: prevIds, + Id: id, + SnapshotId: snapshotId, + IsSnapshot: false, + } +} + +func newSnapshot(id, snapshotId string, prevIds ...string) *Change { + return &Change{ + PreviousIds: prevIds, + Id: id, + SnapshotId: snapshotId, + IsSnapshot: true, + } +} + +func TestTree_Add(t *testing.T) { + t.Run("add first el", func(t *testing.T) { + tr := new(Tree) + assert.Equal(t, Rebuild, tr.Add(newSnapshot("root", ""))) + assert.Equal(t, tr.root.Id, "root") + assert.Equal(t, []string{"root"}, tr.Heads()) + }) + t.Run("linear add", func(t *testing.T) { + tr := new(Tree) + assert.Equal(t, Rebuild, tr.Add( + newSnapshot("root", ""), + newChange("one", "root", "root"), + newChange("two", "root", "one"), + )) + assert.Equal(t, []string{"two"}, tr.Heads()) + assert.Equal(t, Append, tr.Add(newChange("three", "root", "two"))) + el := tr.root + var ids []string + for el != nil { + ids = append(ids, el.Id) + if len(el.Next) > 0 { + el = el.Next[0] + } else { + el = nil + } + } + assert.Equal(t, []string{"root", "one", "two", "three"}, ids) + assert.Equal(t, []string{"three"}, tr.Heads()) + }) + t.Run("branch", func(t *testing.T) { + tr := new(Tree) + assert.Equal(t, Rebuild, tr.Add( + newSnapshot("root", ""), + newChange("1", "root", "root"), + newChange("2", "root", "1"), + )) + assert.Equal(t, []string{"2"}, tr.Heads()) + assert.Equal(t, Rebuild, tr.Add( + newChange("1.2", "root", "1.1"), + newChange("1.3", "root", "1.2"), + newChange("1.1", "root", "1"), + )) + assert.Len(t, tr.attached["1"].Next, 2) + assert.Len(t, tr.unAttached, 0) + assert.Len(t, tr.attached, 6) + assert.Equal(t, []string{"1.3", "2"}, tr.Heads()) + }) + t.Run("branch union", func(t *testing.T) { + tr := new(Tree) + assert.Equal(t, Rebuild, tr.Add( + newSnapshot("root", ""), + newChange("1", "root", "root"), + newChange("2", "root", "1"), + newChange("1.2", "root", "1.1"), + newChange("1.3", "root", "1.2"), + newChange("1.1", "root", "1"), + newChange("3", "root", "2", "1.3"), + newChange("4", "root", "3"), + )) + assert.Len(t, tr.unAttached, 0) + assert.Len(t, tr.attached, 8) + assert.Equal(t, []string{"4"}, tr.Heads()) + }) + t.Run("big set", func(t *testing.T) { + tr := new(Tree) + tr.Add(newSnapshot("root", "")) + var changes []*Change + for i := 0; i < 10000; i++ { + if i == 0 { + changes = append(changes, newChange(fmt.Sprint(i), "root", "root")) + } else { + changes = append(changes, newChange(fmt.Sprint(i), "root", fmt.Sprint(i-1))) + } + } + st := time.Now() + tr.AddFast(changes...) + t.Log(time.Since(st)) + assert.Equal(t, []string{"9999"}, tr.Heads()) + }) + // TODO: add my tests +} + +func TestTree_Hash(t *testing.T) { + tr := new(Tree) + tr.Add(newSnapshot("root", "")) + hash1 := tr.Hash() + assert.Equal(t, tr.Hash(), hash1) + tr.Add(newChange("1", "root", "root")) + assert.NotEqual(t, tr.Hash(), hash1) + assert.Equal(t, tr.Hash(), tr.Hash()) +} + +func TestTree_AddFuzzy(t *testing.T) { + rand.Seed(time.Now().UnixNano()) + getChanges := func() []*Change { + changes := []*Change{ + newChange("1", "root", "root"), + newChange("2", "root", "1"), + newChange("1.2", "root", "1.1"), + newChange("1.3", "root", "1.2"), + newChange("1.1", "root", "1"), + newChange("3", "root", "2", "1.3"), + } + rand.Shuffle(len(changes), func(i, j int) { + changes[i], changes[j] = changes[j], changes[i] + }) + return changes + } + var phash string + for i := 0; i < 100; i++ { + tr := new(Tree) + tr.Add(newSnapshot("root", "")) + tr.Add(getChanges()...) + assert.Len(t, tr.unAttached, 0) + assert.Len(t, tr.attached, 7) + hash := tr.Hash() + if phash != "" { + assert.Equal(t, phash, hash) + } + phash = hash + assert.Equal(t, []string{"3"}, tr.Heads()) + } +} + +func TestTree_CheckRootReduce(t *testing.T) { + t.Run("check root once", func(t *testing.T) { + tr := new(Tree) + tr.Add( + newSnapshot("0", ""), + newChange("1", "0", "0"), + newChange("1.1", "0", "1"), + newChange("1.2", "0", "1"), + newChange("1.4", "0", "1.2"), + newChange("1.3", "0", "1"), + newChange("1.3.1", "0", "1.3"), + newChange("1.2+3", "0", "1.4", "1.3.1"), + newChange("1.2+3.1", "0", "1.2+3"), + newSnapshot("10", "0", "1.2+3.1", "1.1"), + newChange("last", "10", "10"), + ) + t.Run("check root", func(t *testing.T) { + total := tr.checkRoot(tr.attached["10"]) + assert.Equal(t, 1, total) + }) + t.Run("reduce", func(t *testing.T) { + tr.reduceTree() + assert.Equal(t, "10", tr.RootId()) + var res []string + tr.Iterate(tr.RootId(), func(c *Change) (isContinue bool) { + res = append(res, c.Id) + return true + }) + assert.Equal(t, []string{"10", "last"}, res) + }) + }) + t.Run("check root many", func(t *testing.T) { + tr := new(Tree) + tr.Add( + newSnapshot("0", ""), + newSnapshot("1", "0", "0"), + newChange("1.2", "0", "1"), + newChange("1.3", "0", "1"), + newChange("1.3.1", "0", "1.3"), + newSnapshot("1.2+3", "1", "1.2", "1.3.1"), + newChange("1.2+3.1", "1", "1.2+3"), + newChange("1.2+3.2", "1", "1.2+3"), + newSnapshot("10", "1.2+3", "1.2+3.1", "1.2+3.2"), + newChange("last", "10", "10"), + ) + t.Run("check root", func(t *testing.T) { + total := tr.checkRoot(tr.attached["10"]) + assert.Equal(t, 1, total) + + total = tr.checkRoot(tr.attached["1.2+3"]) + assert.Equal(t, 4, total) + + total = tr.checkRoot(tr.attached["1"]) + assert.Equal(t, 8, total) + }) + t.Run("reduce", func(t *testing.T) { + tr.reduceTree() + assert.Equal(t, "10", tr.RootId()) + var res []string + tr.Iterate(tr.RootId(), func(c *Change) (isContinue bool) { + res = append(res, c.Id) + return true + }) + assert.Equal(t, []string{"10", "last"}, res) + }) + }) + t.Run("check root incorrect", func(t *testing.T) { + tr := new(Tree) + tr.Add( + newSnapshot("0", ""), + newChange("1", "0", "0"), + newChange("1.1", "0", "1"), + newChange("1.2", "0", "1"), + newChange("1.4", "0", "1.2"), + newChange("1.3", "0", "1"), + newSnapshot("1.3.1", "0", "1.3"), + newChange("1.2+3", "0", "1.4", "1.3.1"), + newChange("1.2+3.1", "0", "1.2+3"), + newChange("10", "0", "1.2+3.1", "1.1"), + newChange("last", "10", "10"), + ) + t.Run("check root", func(t *testing.T) { + total := tr.checkRoot(tr.attached["1.3.1"]) + assert.Equal(t, -1, total) + }) + t.Run("reduce", func(t *testing.T) { + tr.reduceTree() + assert.Equal(t, "0", tr.RootId()) + assert.Equal(t, 0, len(tr.possibleRoots)) + }) + }) +} + +func TestTree_Iterate(t *testing.T) { + t.Run("complex tree", func(t *testing.T) { + tr := new(Tree) + tr.Add( + newSnapshot("0", ""), + newChange("1", "0", "0"), + newChange("1.1", "0", "1"), + newChange("1.2", "0", "1"), + newChange("1.4", "0", "1.2"), + newChange("1.3", "0", "1"), + newChange("1.3.1", "0", "1.3"), + newChange("1.2+3", "0", "1.4", "1.3.1"), + newChange("1.2+3.1", "0", "1.2+3"), + newChange("10", "0", "1.2+3.1", "1.1"), + newChange("last", "0", "10"), + ) + var res []string + tr.Iterate("0", func(c *Change) (isContinue bool) { + res = append(res, c.Id) + return true + }) + res = res[:0] + tr.Iterate("0", func(c *Change) (isContinue bool) { + res = append(res, c.Id) + return true + }) + assert.Equal(t, []string{"0", "1", "1.1", "1.2", "1.4", "1.3", "1.3.1", "1.2+3", "1.2+3.1", "10", "last"}, res) + }) +} + +func BenchmarkTree_Add(b *testing.B) { + getChanges := func() []*Change { + return []*Change{ + newChange("1", "root", "root"), + newChange("2", "root", "1"), + newChange("1.2", "root", "1.1"), + newChange("1.3", "root", "1.2"), + newChange("1.1", "root", "1"), + newChange("3", "root", "2", "1.3"), + } + } + b.Run("by one", func(b *testing.B) { + tr := new(Tree) + tr.Add(newSnapshot("root", "")) + tr.Add(getChanges()...) + for i := 0; i < b.N; i++ { + tr.Add(newChange(fmt.Sprint(i+4), "root", fmt.Sprint(i+3))) + } + }) + b.Run("add", func(b *testing.B) { + for i := 0; i < b.N; i++ { + tr := new(Tree) + tr.Add(newSnapshot("root", "")) + tr.Add(getChanges()...) + } + }) + b.Run("add fast", func(b *testing.B) { + for i := 0; i < b.N; i++ { + tr := new(Tree) + tr.AddFast(newSnapshot("root", "")) + tr.AddFast(getChanges()...) + } + }) +} + +func BenchmarkTree_IterateLinear(b *testing.B) { + // prepare linear tree + tr := new(Tree) + tr.AddFast(newSnapshot("0", "")) + for j := 0; j < 10000; j++ { + tr.Add(newChange(fmt.Sprint(j+1), "0", fmt.Sprint(j))) + } + b.Run("add linear", func(b *testing.B) { + for i := 0; i < b.N; i++ { + tr.Iterate("0", func(c *Change) (isContinue bool) { + return true + }) + } + }) +} diff --git a/pkg/acl/acltree/treebuilder.go b/pkg/acl/tree/treebuilder.go similarity index 59% rename from pkg/acl/acltree/treebuilder.go rename to pkg/acl/tree/treebuilder.go index 9bb9a4b8..fdf69873 100644 --- a/pkg/acl/acltree/treebuilder.go +++ b/pkg/acl/tree/treebuilder.go @@ -1,12 +1,14 @@ -package acltree +package tree import ( + "context" "errors" "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" + "go.uber.org/zap" + "time" ) var ( @@ -15,87 +17,83 @@ var ( ) type treeBuilder struct { - cache map[string]*Change - identityKeys map[string]signingkey.PubKey - signingPubKeyDecoder signingkey.PubKeyDecoder - tree *Tree - treeStorage treestorage.TreeStorage + treeStorage storage.TreeStorage + builder ChangeBuilder - *changeLoader + cache map[string]*Change + tree *Tree + + // buffers + idStack []string + loadBuffer []*Change } -func newTreeBuilder(t treestorage.TreeStorage, decoder signingkey.PubKeyDecoder) *treeBuilder { +func newTreeBuilder(storage storage.TreeStorage, builder ChangeBuilder) *treeBuilder { return &treeBuilder{ - signingPubKeyDecoder: decoder, - treeStorage: t, - changeLoader: newChangeLoader( - t, - decoder, - NewChange), + treeStorage: storage, + builder: builder, } } -func (tb *treeBuilder) Init() { +func (tb *treeBuilder) Reset() { tb.cache = make(map[string]*Change) - tb.identityKeys = make(map[string]signingkey.PubKey) tb.tree = &Tree{} - tb.changeLoader.Init(tb.cache, tb.identityKeys) } -func (tb *treeBuilder) Build(fromStart bool) (*Tree, error) { - var headsAndOrphans []string - orphans, err := tb.treeStorage.Orphans() - if err != nil { - return nil, err - } +func (tb *treeBuilder) Build(newChanges []*Change) (*Tree, error) { + var headsAndNewChanges []string heads, err := tb.treeStorage.Heads() if err != nil { return nil, err } - headsAndOrphans = append(headsAndOrphans, orphans...) - headsAndOrphans = append(headsAndOrphans, heads...) - if fromStart { - if err := tb.buildTreeFromStart(headsAndOrphans); err != nil { - return nil, fmt.Errorf("buildTree error: %v", err) - } - } else { - breakpoint, err := tb.findBreakpoint(headsAndOrphans) - if err != nil { - return nil, fmt.Errorf("findBreakpoint error: %v", err) - } - - if err = tb.buildTree(headsAndOrphans, breakpoint); err != nil { - return nil, fmt.Errorf("buildTree error: %v", err) - } + headsAndNewChanges = append(headsAndNewChanges, heads...) + tb.cache = make(map[string]*Change) + for _, ch := range newChanges { + headsAndNewChanges = append(headsAndNewChanges, ch.Id) + tb.cache[ch.Id] = ch } - tb.cache = nil + log.With(zap.Strings("heads", heads)).Debug("building tree") + breakpoint, err := tb.findBreakpoint(headsAndNewChanges) + if err != nil { + return nil, fmt.Errorf("findBreakpoint error: %v", err) + } + + if err = tb.buildTree(headsAndNewChanges, breakpoint); err != nil { + return nil, fmt.Errorf("buildTree error: %v", err) + } return tb.tree, nil } -func (tb *treeBuilder) buildTreeFromStart(heads []string) (err error) { - changes, root, err := tb.dfsFromStart(heads) +func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error) { + ch, err := tb.loadChange(breakpoint) if err != nil { - return err + return } + tb.tree.AddFast(ch) + changes, err := tb.dfs(heads, breakpoint) - tb.tree.AddFast(root) tb.tree.AddFast(changes...) return } -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) +func (tb *treeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, err error) { + // initializing buffers + tb.idStack = tb.idStack[:0] + tb.loadBuffer = tb.loadBuffer[:0] - 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] + // updating map + uniqMap := map[string]struct{}{breakpoint: {}} + + // preparing dfs + tb.idStack = append(tb.idStack, heads...) + + // dfs + for len(tb.idStack) > 0 { + id := tb.idStack[len(tb.idStack)-1] + tb.idStack = tb.idStack[:len(tb.idStack)-1] if _, exists := uniqMap[id]; exists { continue } @@ -106,69 +104,38 @@ func (tb *treeBuilder) dfsFromStart(heads []string) (buf []*Change, root *Change } uniqMap[id] = struct{}{} - buf = append(buf, ch) + tb.loadBuffer = append(tb.loadBuffer, ch) for _, prev := range ch.PreviousIds { - stack = append(stack, prev) - } - if len(ch.PreviousIds) == 0 { - possibleRoots = append(possibleRoots, ch) + if _, exists := uniqMap[prev]; exists { + continue + } + tb.idStack = append(tb.idStack, prev) } } - header, err := tb.treeStorage.Header() - if err != nil { - return nil, nil, err - } - for _, r := range possibleRoots { - if r.Id == header.FirstChangeId { - return buf, r, nil - } - } - - return nil, nil, fmt.Errorf("could not find root change") + return tb.loadBuffer, nil } -func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error) { - ch, err := tb.loadChange(breakpoint) +func (tb *treeBuilder) loadChange(id string) (ch *Change, err error) { + if ch, ok := tb.cache[id]; ok { + return ch, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + change, err := tb.treeStorage.GetRawChange(ctx, id) if err != nil { - return + return nil, err } - tb.tree.AddFast(ch) - changes, err := tb.dfs(heads, breakpoint, tb.loadChange) - tb.tree.AddFast(changes...) - return -} - -func (tb *treeBuilder) dfs( - heads []string, - breakpoint string, - load func(string) (*Change, error)) (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 { - id := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if _, exists := uniqMap[id]; exists { - continue - } - - ch, err := load(id) - if err != nil { - continue - } - - uniqMap[id] = struct{}{} - buf = append(buf, ch) - - for _, prev := range ch.PreviousIds { - stack = append(stack, prev) - } + ch, err = tb.builder.ConvertFromRawAndVerify(change) + if err != nil { + return nil, err } - return buf, nil + + tb.cache[id] = ch + return ch, nil } func (tb *treeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) { @@ -281,11 +248,9 @@ func (tb *treeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e } 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) @@ -295,7 +260,6 @@ func (tb *treeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e 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) diff --git a/pkg/acl/acltree/treegraph.go b/pkg/acl/tree/treegraph.go similarity index 68% rename from pkg/acl/acltree/treegraph.go rename to pkg/acl/tree/treegraph.go index 3b37fa2e..67cd42bd 100644 --- a/pkg/acl/acltree/treegraph.go +++ b/pkg/acl/tree/treegraph.go @@ -2,10 +2,10 @@ // +build !linux,!darwin android ios nographviz // +build !amd64 -package acltree +package tree import "fmt" -func (t *Tree) Graph() (data string, err error) { +func (t *Tree) Graph(parser DescriptionParser) (data []string, err error) { return "", fmt.Errorf("not supported") } diff --git a/pkg/acl/acltree/treegraph_nix.go b/pkg/acl/tree/treegraph_nix.go similarity index 66% rename from pkg/acl/acltree/treegraph_nix.go rename to pkg/acl/tree/treegraph_nix.go index 4d798939..c506c9b3 100644 --- a/pkg/acl/acltree/treegraph_nix.go +++ b/pkg/acl/tree/treegraph_nix.go @@ -5,20 +5,18 @@ // +build !nographviz // +build amd64 arm64 -package acltree +package tree import ( "bytes" "fmt" - "strings" - "time" - "unicode" - "github.com/goccy/go-graphviz" "github.com/goccy/go-graphviz/cgraph" + "strings" + "time" ) -func (t *Tree) Graph() (data string, err error) { +func (t *Tree) Graph(parser DescriptionParser) (data string, err error) { var order = make(map[string]string) var seq = 0 t.Iterate(t.RootId(), func(c *Change) (isContinue bool) { @@ -46,44 +44,15 @@ func (t *Tree) Graph() (data string, err error) { if e != nil { return e } - if c.Content.GetAclData() != nil { - n.SetStyle(cgraph.FilledNodeStyle) - } else if c.IsSnapshot { - n.SetStyle(cgraph.DashedNodeStyle) - } + n.SetStyle(cgraph.FilledNodeStyle) 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 { - // 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) - //} - chSymbs = append(chSymbs, "DEC") + chSymbs, err := parser.ParseChange(c) + if err != nil { + return err } shortId := c.Id diff --git a/pkg/acl/tree/treeiterator.go b/pkg/acl/tree/treeiterator.go new file mode 100644 index 00000000..eb42f7b4 --- /dev/null +++ b/pkg/acl/tree/treeiterator.go @@ -0,0 +1,93 @@ +package tree + +import ( + "sync" +) + +var itPool = &sync.Pool{ + New: func() interface{} { + return &iterator{ + stack: make([]*Change, 0, 100), + resBuf: make([]*Change, 0, 100), + } + }, +} + +func newIterator() *iterator { + return itPool.Get().(*iterator) +} + +func freeIterator(i *iterator) { + itPool.Put(i) +} + +type iterator struct { + resBuf []*Change + stack []*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) topSort(start *Change) { + stack := i.stack + stack = append(stack, start) + + for len(stack) > 0 { + ch := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + // this looks a bit clumsy, but the idea is that we will go through the change again as soon as we finished + // going through its branches + if ch.branchesFinished { + i.resBuf = append(i.resBuf, ch) + ch.branchesFinished = false + continue + } + + // in theory, it may be the case that we add the change two times + // but probably due to the way how we build the tree, we won't need it + if ch.visited { + continue + } + + stack = append(stack, ch) + ch.visited = true + ch.branchesFinished = true + + for j := 0; j < len(ch.Next); j++ { + if !ch.Next[j].visited { + stack = append(stack, ch.Next[j]) + } + } + } + for _, ch := range i.resBuf { + ch.visited = false + } +} + +func (i *iterator) iterate(start *Change, f func(c *Change) (isContinue bool)) { + if start == nil { + return + } + // reset + i.resBuf = i.resBuf[:0] + i.stack = i.stack[:0] + i.f = f + + i.topSort(start) + for idx := len(i.resBuf) - 1; idx >= 0; idx-- { + if !f(i.resBuf[idx]) { + return + } + } +} diff --git a/pkg/acl/tree/treereduce.go b/pkg/acl/tree/treereduce.go new file mode 100644 index 00000000..98532339 --- /dev/null +++ b/pkg/acl/tree/treereduce.go @@ -0,0 +1,92 @@ +package tree + +import "math" + +// clearPossibleRoots force removes any snapshots which can further be deemed as roots +func (t *Tree) clearPossibleRoots() { + t.possibleRoots = t.possibleRoots[:0] +} + +// checkRoot checks if a change can be a new root for the tree +// it returns total changes which were discovered during dfsPrev from heads +func (t *Tree) checkRoot(change *Change) (total int) { + t.stackBuf = t.stackBuf[:0] + stack := t.stackBuf + + // starting with heads + for _, h := range t.headIds { + stack = append(stack, t.attached[h]) + } + + t.dfsPrev( + stack, + []string{change.Id}, + func(ch *Change) bool { + total += 1 + return true + }, + func(changes []*Change) { + if t.root.visited { + total = -1 + } + }, + ) + + return +} + +// makeRootAndRemove removes all changes before start and makes start the root +func (t *Tree) makeRootAndRemove(start *Change) { + t.stackBuf = t.stackBuf[:0] + stack := t.stackBuf + for _, prev := range start.PreviousIds { + stack = append(stack, t.attached[prev]) + } + + t.dfsPrev( + stack, + []string{}, + func(ch *Change) bool { + return true + }, + func(changes []*Change) { + for _, ch := range changes { + delete(t.unAttached, ch.Id) + } + }, + ) + + // removing unattached because they may refer to previous root + t.unAttached = make(map[string]*Change) + t.root = start +} + +// reduceTree tries to reduce the tree to one of possible tree roots +func (t *Tree) reduceTree() (res bool) { + if len(t.possibleRoots) == 0 { + return + } + var ( + minRoot *Change + minTotal = math.MaxInt + ) + + // checking if we can reduce tree to other root + for _, root := range t.possibleRoots { + totalChanges := t.checkRoot(root) + // we prefer new root with min amount of total changes + if totalChanges != -1 && totalChanges < minTotal { + minRoot = root + minTotal = totalChanges + } + } + + t.clearPossibleRoots() + if minRoot == nil { + return + } + + t.makeRootAndRemove(minRoot) + res = true + return +} diff --git a/pkg/acl/tree/treestorage.go b/pkg/acl/tree/treestorage.go new file mode 100644 index 00000000..27fce1d5 --- /dev/null +++ b/pkg/acl/tree/treestorage.go @@ -0,0 +1,91 @@ +package tree + +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" + "github.com/gogo/protobuf/proto" + "time" +) + +func CreateNewTreeStorage( + acc *account.AccountData, + aclList list.ACLList, + content proto.Marshaler, + create storage.TreeStorageCreatorFunc) (thr storage.TreeStorage, err error) { + + state := aclList.ACLState() + change := &aclpb.Change{ + AclHeadId: aclList.Head().Id, + CurrentReadKeyHash: state.CurrentReadKeyHash(), + Timestamp: int64(time.Now().Nanosecond()), + Identity: acc.Identity, + IsSnapshot: true, + } + + marshalledData, err := content.Marshal() + if err != nil { + return + } + + readKey, err := state.CurrentReadKey() + if err != nil { + return + } + + encrypted, err := readKey.Encrypt(marshalledData) + if err != nil { + return + } + + change.ChangesData = encrypted + + fullMarshalledChange, err := proto.Marshal(change) + if err != nil { + return + } + + signature, err := acc.SignKey.Sign(fullMarshalledChange) + if err != nil { + return + } + + changeId, err := cid.NewCIDFromBytes(fullMarshalledChange) + if err != nil { + return + } + + rawChange := &aclpb.RawChange{ + Payload: fullMarshalledChange, + Signature: signature, + Id: changeId, + } + header, treeId, err := createTreeHeaderAndId(rawChange, aclpb.Header_DocTree, aclList.ID()) + if err != nil { + return + } + + return create(storage.TreeStorageCreatePayload{ + TreeId: treeId, + Header: header, + Changes: []*aclpb.RawChange{rawChange}, + Heads: []string{rawChange.Id}, + }) +} + +func createTreeHeaderAndId(change *aclpb.RawChange, treeType aclpb.HeaderDocType, aclListId string) (header *aclpb.Header, treeId string, err error) { + header = &aclpb.Header{ + FirstId: change.Id, + DocType: treeType, + AclListId: aclListId, + } + marshalledHeader, err := proto.Marshal(header) + if err != nil { + return + } + + treeId, err = cid.NewCIDFromBytes(marshalledHeader) + return +} diff --git a/pkg/acl/tree/util.go b/pkg/acl/tree/util.go new file mode 100644 index 00000000..0e6cc7cd --- /dev/null +++ b/pkg/acl/tree/util.go @@ -0,0 +1,29 @@ +package tree + +func commonSnapshotForTwoPaths(ourPath []string, theirPath []string) (string, error) { + var i int + var j int +OuterLoop: + // find starting point from the right + for i = len(ourPath) - 1; i >= 0; i-- { + for j = len(theirPath) - 1; j >= 0; j-- { + // most likely there would be only one comparison, because mostly the snapshot path will start from the root for nodes + if ourPath[i] == theirPath[j] { + break OuterLoop + } + } + } + if i < 0 || j < 0 { + return "", ErrNoCommonSnapshot + } + // find last common element of the sequence moving from right to left + for i >= 0 && j >= 0 { + if ourPath[i] == theirPath[j] { + i-- + j-- + } else { + break + } + } + return ourPath[i+1], nil +} diff --git a/pkg/acl/treestorage/inmemory.go b/pkg/acl/treestorage/inmemory.go deleted file mode 100644 index adfa9af8..00000000 --- a/pkg/acl/treestorage/inmemory.go +++ /dev/null @@ -1,163 +0,0 @@ -package treestorage - -import ( - "context" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "github.com/gogo/protobuf/proto" - "sync" -) - -type inMemoryTreeStorage struct { - id string - header *treepb.TreeHeader - heads []string - orphans []string - changes map[string]*aclpb.RawChange - - sync.RWMutex -} - -type CreatorFunc = func(string, *treepb.TreeHeader, []*aclpb.RawChange) (TreeStorage, error) - -func NewInMemoryTreeStorage( - treeId string, - header *treepb.TreeHeader, - changes []*aclpb.RawChange) (TreeStorage, error) { - allChanges := make(map[string]*aclpb.RawChange) - var orphans []string - for _, ch := range changes { - allChanges[ch.Id] = ch - orphans = append(orphans, ch.Id) - } - - return &inMemoryTreeStorage{ - id: treeId, - header: header, - heads: nil, - orphans: orphans, - changes: allChanges, - RWMutex: sync.RWMutex{}, - }, nil -} - -func (t *inMemoryTreeStorage) TreeID() (string, error) { - t.RLock() - defer t.RUnlock() - return t.id, nil -} - -func (t *inMemoryTreeStorage) Header() (*treepb.TreeHeader, error) { - t.RLock() - defer t.RUnlock() - return t.header, nil -} - -func (t *inMemoryTreeStorage) Heads() ([]string, error) { - t.RLock() - defer t.RUnlock() - return t.heads, nil -} - -func (t *inMemoryTreeStorage) Orphans() ([]string, error) { - t.RLock() - defer t.RUnlock() - return t.orphans, nil -} - -func (t *inMemoryTreeStorage) SetHeads(heads []string) error { - t.Lock() - defer t.Unlock() - t.heads = t.heads[:0] - - for _, h := range heads { - t.heads = append(t.heads, h) - } - return nil -} - -func (t *inMemoryTreeStorage) RemoveOrphans(orphans ...string) error { - t.Lock() - defer t.Unlock() - t.orphans = slice.Difference(t.orphans, orphans) - return nil -} - -func (t *inMemoryTreeStorage) AddOrphans(orphans ...string) error { - t.Lock() - defer t.Unlock() - t.orphans = append(t.orphans, orphans...) - return nil -} - -func (t *inMemoryTreeStorage) AddRawChange(change *aclpb.RawChange) error { - t.Lock() - defer t.Unlock() - // TODO: better to do deep copy - t.changes[change.Id] = change - return nil -} - -func (t *inMemoryTreeStorage) AddChange(change aclchanges.Change) error { - t.Lock() - defer t.Unlock() - signature := change.Signature() - id := change.CID() - aclChange := change.ProtoChange() - - fullMarshalledChange, err := proto.Marshal(aclChange) - if err != nil { - return err - } - rawChange := &aclpb.RawChange{ - Payload: fullMarshalledChange, - Signature: signature, - Id: id, - } - t.changes[id] = rawChange - return nil -} - -func (t *inMemoryTreeStorage) GetChange(ctx context.Context, changeId string) (*aclpb.RawChange, error) { - t.RLock() - defer t.RUnlock() - if res, exists := t.changes[changeId]; exists { - return res, nil - } - return nil, fmt.Errorf("could not get change with id: %s", changeId) -} - -type inMemoryTreeStorageProvider struct { - trees map[string]TreeStorage - sync.RWMutex -} - -func (i *inMemoryTreeStorageProvider) TreeStorage(treeId string) (TreeStorage, error) { - i.RLock() - defer i.RUnlock() - if tree, exists := i.trees[treeId]; exists { - return tree, nil - } - return nil, ErrUnknownTreeId -} - -func (i *inMemoryTreeStorageProvider) CreateTreeStorage(treeId string, header *treepb.TreeHeader, changes []*aclpb.RawChange) (TreeStorage, error) { - i.Lock() - defer i.Unlock() - res, err := NewInMemoryTreeStorage(treeId, header, changes) - if err != nil { - return nil, err - } - - i.trees[treeId] = res - return res, nil -} - -func NewInMemoryTreeStorageProvider() Provider { - return &inMemoryTreeStorageProvider{ - trees: make(map[string]TreeStorage), - } -} diff --git a/pkg/acl/treestorage/provider.go b/pkg/acl/treestorage/provider.go deleted file mode 100644 index 41b472e9..00000000 --- a/pkg/acl/treestorage/provider.go +++ /dev/null @@ -1,14 +0,0 @@ -package treestorage - -import ( - "errors" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" -) - -var ErrUnknownTreeId = errors.New("tree does not exist") - -type Provider interface { - TreeStorage(treeId string) (TreeStorage, error) - CreateTreeStorage(treeId string, header *treepb.TreeHeader, changes []*aclpb.RawChange) (TreeStorage, error) -} diff --git a/pkg/acl/treestorage/storage.go b/pkg/acl/treestorage/storage.go deleted file mode 100644 index df47d809..00000000 --- a/pkg/acl/treestorage/storage.go +++ /dev/null @@ -1,25 +0,0 @@ -package treestorage - -import ( - "context" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" -) - -type TreeStorage interface { - TreeID() (string, error) - - Header() (*treepb.TreeHeader, error) - Heads() ([]string, error) - Orphans() ([]string, error) - SetHeads(heads []string) error - RemoveOrphans(orphan ...string) error - AddOrphans(orphan ...string) error - - AddRawChange(change *aclpb.RawChange) error - AddChange(change aclchanges.Change) error - - // TODO: have methods with raw changes also - GetChange(ctx context.Context, recordID string) (*aclpb.RawChange, error) -} diff --git a/pkg/acl/treestorage/treepb/protos/tree.proto b/pkg/acl/treestorage/treepb/protos/tree.proto deleted file mode 100644 index eb342673..00000000 --- a/pkg/acl/treestorage/treepb/protos/tree.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto3"; -package tree; -option go_package = "treepb"; - -message TreeHeader { - string firstChangeId = 1; - bool isWorkspace = 2; - // TODO: add user identity, signature and nano timestamp -} \ No newline at end of file diff --git a/pkg/acl/treestorage/treepb/tree.pb.go b/pkg/acl/treestorage/treepb/tree.pb.go deleted file mode 100644 index 7baa874c..00000000 --- a/pkg/acl/treestorage/treepb/tree.pb.go +++ /dev/null @@ -1,358 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: pkg/acl/treestorage/treepb/protos/tree.proto - -package treepb - -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 TreeHeader struct { - FirstChangeId string `protobuf:"bytes,1,opt,name=firstChangeId,proto3" json:"firstChangeId,omitempty"` - IsWorkspace bool `protobuf:"varint,2,opt,name=isWorkspace,proto3" json:"isWorkspace,omitempty"` -} - -func (m *TreeHeader) Reset() { *m = TreeHeader{} } -func (m *TreeHeader) String() string { return proto.CompactTextString(m) } -func (*TreeHeader) ProtoMessage() {} -func (*TreeHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_e7d760b855878644, []int{0} -} -func (m *TreeHeader) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TreeHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TreeHeader.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 *TreeHeader) XXX_Merge(src proto.Message) { - xxx_messageInfo_TreeHeader.Merge(m, src) -} -func (m *TreeHeader) XXX_Size() int { - return m.Size() -} -func (m *TreeHeader) XXX_DiscardUnknown() { - xxx_messageInfo_TreeHeader.DiscardUnknown(m) -} - -var xxx_messageInfo_TreeHeader proto.InternalMessageInfo - -func (m *TreeHeader) GetFirstChangeId() string { - if m != nil { - return m.FirstChangeId - } - return "" -} - -func (m *TreeHeader) GetIsWorkspace() bool { - if m != nil { - return m.IsWorkspace - } - return false -} - -func init() { - proto.RegisterType((*TreeHeader)(nil), "tree.TreeHeader") -} - -func init() { - proto.RegisterFile("pkg/acl/treestorage/treepb/protos/tree.proto", fileDescriptor_e7d760b855878644) -} - -var fileDescriptor_e7d760b855878644 = []byte{ - // 165 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x29, 0xc8, 0x4e, 0xd7, - 0x4f, 0x4c, 0xce, 0xd1, 0x2f, 0x29, 0x4a, 0x4d, 0x2d, 0x2e, 0xc9, 0x2f, 0x4a, 0x4c, 0x4f, 0x05, - 0xb3, 0x0b, 0x92, 0xf4, 0x0b, 0x8a, 0xf2, 0x4b, 0xf2, 0x8b, 0xc1, 0x3c, 0x3d, 0x30, 0x5b, 0x88, - 0x05, 0xc4, 0x56, 0x0a, 0xe1, 0xe2, 0x0a, 0x29, 0x4a, 0x4d, 0xf5, 0x48, 0x4d, 0x4c, 0x49, 0x2d, - 0x12, 0x52, 0xe1, 0xe2, 0x4d, 0xcb, 0x2c, 0x2a, 0x2e, 0x71, 0xce, 0x48, 0xcc, 0x4b, 0x4f, 0xf5, - 0x4c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x42, 0x15, 0x14, 0x52, 0xe0, 0xe2, 0xce, 0x2c, - 0x0e, 0xcf, 0x2f, 0xca, 0x2e, 0x2e, 0x48, 0x4c, 0x4e, 0x95, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x08, - 0x42, 0x16, 0x72, 0x52, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, - 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0x36, - 0x88, 0x7b, 0x92, 0xd8, 0xc0, 0x8e, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x09, 0x4f, 0xc6, - 0xec, 0xb4, 0x00, 0x00, 0x00, -} - -func (m *TreeHeader) 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 *TreeHeader) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *TreeHeader) 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.FirstChangeId) > 0 { - i -= len(m.FirstChangeId) - copy(dAtA[i:], m.FirstChangeId) - i = encodeVarintTree(dAtA, i, uint64(len(m.FirstChangeId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func encodeVarintTree(dAtA []byte, offset int, v uint64) int { - offset -= sovTree(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *TreeHeader) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.FirstChangeId) - if l > 0 { - n += 1 + l + sovTree(uint64(l)) - } - if m.IsWorkspace { - n += 2 - } - return n -} - -func sovTree(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozTree(x uint64) (n int) { - return sovTree(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *TreeHeader) 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 ErrIntOverflowTree - } - 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: TreeHeader: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TreeHeader: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FirstChangeId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTree - } - 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 ErrInvalidLengthTree - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTree - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FirstChangeId = 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 ErrIntOverflowTree - } - 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 := skipTree(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTree - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipTree(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, ErrIntOverflowTree - } - 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, ErrIntOverflowTree - } - 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, ErrIntOverflowTree - } - 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, ErrInvalidLengthTree - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupTree - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthTree - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthTree = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTree = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupTree = fmt.Errorf("proto: unexpected end of group") -) diff --git a/service/account/service.go b/service/account/service.go index ed66fe1e..f617fdd4 100644 --- a/service/account/service.go +++ b/service/account/service.go @@ -61,7 +61,7 @@ func (s *service) Init(ctx context.Context, a *app.App) (err error) { Identity: identity, SignKey: signKey, EncKey: decodedEncryptionKey.(encryptionkey.PrivKey), - Decoder: signingkey.NewEd25519PubKeyDecoder(), + Decoder: signingkey.NewEDPubKeyDecoder(), } s.peerId = acc.PeerId diff --git a/service/api/service.go b/service/api/service.go index 8ab7eb26..f1373e28 100644 --- a/service/api/service.go +++ b/service/api/service.go @@ -6,8 +6,8 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/config" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/document" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" + "github.com/anytypeio/go-anytype-infrastructure-experiments/service/document" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache" "go.uber.org/zap" "io" @@ -53,7 +53,7 @@ func (s *service) Run(ctx context.Context) (err error) { } mux := http.NewServeMux() mux.HandleFunc("/treeDump", s.treeDump) - mux.HandleFunc("/createDocument", s.createDocument) + mux.HandleFunc("/createDocumentTree", s.createDocumentTree) mux.HandleFunc("/appendDocument", s.appendDocument) s.srv.Handler = mux @@ -79,8 +79,9 @@ func (s *service) treeDump(w http.ResponseWriter, req *http.Request) { dump string err error ) - err = s.treeCache.Do(context.Background(), treeId, func(tree acltree.ACLTree) error { - dump, err = tree.DebugDump() + err = s.treeCache.Do(context.Background(), treeId, func(obj interface{}) error { + t := obj.(tree.ObjectTree) + dump, err = t.DebugDump() if err != nil { return err } @@ -93,13 +94,14 @@ func (s *service) treeDump(w http.ResponseWriter, req *http.Request) { sendText(w, http.StatusOK, dump) } -func (s *service) createDocument(w http.ResponseWriter, req *http.Request) { +func (s *service) createDocumentTree(w http.ResponseWriter, req *http.Request) { var ( - query = req.URL.Query() - text = query.Get("text") + query = req.URL.Query() + text = query.Get("text") + aclListId = query.Get("aclListId") ) timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30) - treeId, err := s.documentService.CreateDocument(timeoutCtx, fmt.Sprintf("created document with id: %s", text)) + treeId, err := s.documentService.CreateDocumentTree(timeoutCtx, aclListId, text) cancel() if err != nil { sendText(w, http.StatusInternalServerError, err.Error()) @@ -115,7 +117,7 @@ func (s *service) appendDocument(w http.ResponseWriter, req *http.Request) { treeId = query.Get("treeId") ) timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30) - err := s.documentService.UpdateDocument(timeoutCtx, treeId, text) + err := s.documentService.UpdateDocumentTree(timeoutCtx, treeId, text) cancel() if err != nil { sendText(w, http.StatusInternalServerError, err.Error()) diff --git a/service/sync/document/service.go b/service/document/service.go similarity index 50% rename from service/sync/document/service.go rename to service/document/service.go index b684e64b..2ae8e239 100644 --- a/service/sync/document/service.go +++ b/service/document/service.go @@ -2,14 +2,16 @@ package document import ( "context" + "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" + testchanges "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/proto" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/node" + "github.com/anytypeio/go-anytype-infrastructure-experiments/service/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/message" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache" "github.com/anytypeio/go-anytype-infrastructure-experiments/syncproto" @@ -25,13 +27,14 @@ type service struct { messageService message.Service treeCache treecache.Service account account.Service + storage storage.Service // to create new documents we need to know all nodes nodes []*node.Node } type Service interface { - UpdateDocument(ctx context.Context, id, text string) error - CreateDocument(ctx context.Context, text string) (string, error) + UpdateDocumentTree(ctx context.Context, id, text string) error + CreateDocumentTree(ctx context.Context, aclTreeId string, text string) (id string, err error) } func New() app.Component { @@ -42,6 +45,7 @@ func (s *service) Init(ctx context.Context, a *app.App) (err error) { s.account = a.MustComponent(account.CName).(account.Service) s.messageService = a.MustComponent(message.CName).(message.Service) s.treeCache = a.MustComponent(treecache.CName).(treecache.Service) + s.storage = a.MustComponent(storage.CName).(storage.Service) nodesService := a.MustComponent(node.CName).(node.Service) s.nodes = nodesService.Nodes() @@ -54,41 +58,65 @@ func (s *service) Name() (name string) { } func (s *service) Run(ctx context.Context) (err error) { - return nil + syncData := s.storage.ImportedACLSyncData() + + // we could have added a timeout or some additional logic, + // but let's just use the ACL id of the latest started node :-) + return s.messageService.SendToSpaceAsync("", syncproto.WrapACLList( + &syncproto.SyncACLList{Records: syncData.Records}, + syncData.Header, + syncData.Id, + )) } func (s *service) Close(ctx context.Context) (err error) { return nil } -func (s *service) UpdateDocument(ctx context.Context, id, text string) (err error) { +func (s *service) UpdateDocumentTree(ctx context.Context, id, text string) (err error) { var ( ch *aclpb.RawChange - header *treepb.TreeHeader + header *aclpb.Header snapshotPath []string heads []string ) log.With(zap.String("id", id), zap.String("text", text)). Debug("updating document") - err = s.treeCache.Do(ctx, id, func(tree acltree.ACLTree) error { - ch, err = tree.AddContent(ctx, func(builder acltree.ChangeBuilder) error { - builder.AddChangeContent( - &testchangepb.PlainTextChangeData{ - Content: []*testchangepb.PlainTextChangeContent{ - createAppendTextChangeContent(text), - }, - }) + err = s.treeCache.Do(ctx, id, func(obj interface{}) error { + docTree, ok := obj.(tree.ObjectTree) + if !ok { + return fmt.Errorf("can't update acl trees with text") + } + + docTree.Lock() + defer docTree.Unlock() + err = s.treeCache.Do(ctx, docTree.Header().AclListId, func(obj interface{}) error { + aclTree := obj.(list.ACLList) + aclTree.RLock() + defer aclTree.RUnlock() + + content := createAppendTextChange(text) + signable := tree.SignableChangeContent{ + Proto: content, + Key: s.account.Account().SignKey, + Identity: s.account.Account().Identity, + IsSnapshot: false, + } + ch, err = docTree.AddContent(ctx, signable) + if err != nil { + return err + } return nil }) if err != nil { return err } - id = tree.ID() - heads = tree.Heads() - header = tree.Header() - snapshotPath = tree.SnapshotPath() + id = docTree.ID() + heads = docTree.Heads() + header = docTree.Header() + snapshotPath = docTree.SnapshotPath() return nil }) if err != nil { @@ -103,84 +131,87 @@ func (s *service) UpdateDocument(ctx context.Context, id, text string) (err erro return s.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(&syncproto.SyncHeadUpdate{ Heads: heads, Changes: []*aclpb.RawChange{ch}, - TreeId: id, SnapshotPath: snapshotPath, - TreeHeader: header, - })) + }, header, id)) } -func (s *service) CreateDocument(ctx context.Context, text string) (id string, err error) { +func (s *service) CreateDocumentTree(ctx context.Context, aclListId string, text string) (id string, err error) { acc := s.account.Account() var ( ch *aclpb.RawChange - header *treepb.TreeHeader + header *aclpb.Header snapshotPath []string heads []string ) + err = s.treeCache.Do(ctx, aclListId, func(obj interface{}) error { + t := obj.(list.ACLList) + t.RLock() + defer t.RUnlock() - err = s.treeCache.Create(ctx, func(builder acltree.ChangeBuilder) error { - err := builder.UserAdd(acc.Identity, acc.EncKey.GetPublic(), aclpb.ACLChange_Admin) + content := createInitialTextChange(text) + doc, err := tree.CreateNewTreeStorage(acc, t, content, s.storage.CreateTreeStorage) if err != nil { return err } - // adding all predefined nodes to the document as admins - for _, n := range s.nodes { - err = builder.UserAdd(n.SigningKeyString, n.EncryptionKey, aclpb.ACLChange_Admin) - if err != nil { - return err - } - } - builder.AddChangeContent(createInitialChangeContent(text)) - return nil - }, func(tree acltree.ACLTree) error { - id = tree.ID() - heads = tree.Heads() - header = tree.Header() - snapshotPath = tree.SnapshotPath() - ch, err = tree.Storage().GetChange(ctx, heads[0]) + id, err = doc.ID() if err != nil { return err } - log.With( - zap.String("id", id), - zap.Strings("heads", heads), - zap.String("header", header.String())). - Debug("document created in the database") + + header, err = doc.Header() + if err != nil { + return err + } + + heads = []string{header.FirstId} + snapshotPath = []string{header.FirstId} + ch, err = doc.GetRawChange(ctx, header.FirstId) + if err != nil { + return err + } + return nil }) if err != nil { return "", err } + log.With(zap.String("id", id), zap.String("text", text)). Debug("creating document") err = s.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(&syncproto.SyncHeadUpdate{ Heads: heads, Changes: []*aclpb.RawChange{ch}, - TreeId: id, SnapshotPath: snapshotPath, - TreeHeader: header, - })) + }, header, id)) if err != nil { return "", err } return id, err } -func createInitialChangeContent(text string) proto.Marshaler { - return &testchangepb.PlainTextChangeData{ - Content: []*testchangepb.PlainTextChangeContent{ +func createInitialTextChange(text string) proto.Marshaler { + return &testchanges.PlainTextChangeData{ + Content: []*testchanges.PlainTextChangeContent{ createAppendTextChangeContent(text), }, - Snapshot: &testchangepb.PlainTextChangeSnapshot{Text: text}, + Snapshot: &testchanges.PlainTextChangeSnapshot{Text: text}, } } -func createAppendTextChangeContent(text string) *testchangepb.PlainTextChangeContent { - return &testchangepb.PlainTextChangeContent{ - Value: &testchangepb.PlainTextChangeContentValueOfTextAppend{ - TextAppend: &testchangepb.PlainTextChangeTextAppend{ +func createAppendTextChange(text string) proto.Marshaler { + return &testchanges.PlainTextChangeData{ + Content: []*testchanges.PlainTextChangeContent{ + createAppendTextChangeContent(text), + }, + } +} + +func createAppendTextChangeContent(text string) *testchanges.PlainTextChangeContent { + return &testchanges.PlainTextChangeContent{ + Value: &testchanges.PlainTextChangeContentValueOfTextAppend{ + TextAppend: &testchanges.PlainTextChangeTextAppend{ Text: text, }, }, diff --git a/service/space/service.go b/service/space/service.go index 364ce91d..7b826607 100644 --- a/service/space/service.go +++ b/service/space/service.go @@ -78,7 +78,8 @@ func (s *service) Handle(ctx context.Context, data []byte) (resp proto.Marshaler return } if spaceReq.SpaceId != "" { - sp, err := s.get(ctx, spaceReq.SpaceId) + var sp Space + sp, err = s.get(ctx, spaceReq.SpaceId) if err != nil { return } diff --git a/service/space/space.go b/service/space/space.go index 776df945..424ba883 100644 --- a/service/space/space.go +++ b/service/space/space.go @@ -19,8 +19,6 @@ type Space interface { Close() error } -// - type space struct { id string conf configuration.Configuration diff --git a/service/storage/service.go b/service/storage/service.go new file mode 100644 index 00000000..3df447e4 --- /dev/null +++ b/service/storage/service.go @@ -0,0 +1,136 @@ +package storage + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/app" + "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" + "github.com/anytypeio/go-anytype-infrastructure-experiments/etc" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/service/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/service/node" +) + +var CName = "storage" + +var log = logger.NewNamed("storage").Sugar() + +type ImportedACLSyncData struct { + Id string + Header *aclpb.Header + Records []*aclpb.RawRecord +} + +type Service interface { + storage.Provider + ImportedACLSyncData() ImportedACLSyncData +} + +func New() app.Component { + return &service{} +} + +type service struct { + storageProvider storage.Provider + importedACLSyncData ImportedACLSyncData +} + +func (s *service) Init(ctx context.Context, a *app.App) (err error) { + s.storageProvider = storage.NewInMemoryTreeStorageProvider() + // importing hardcoded acl list, check that the keys there are correct + return s.importACLList(a) +} + +func (s *service) Storage(treeId string) (storage.Storage, error) { + return s.storageProvider.Storage(treeId) +} + +func (s *service) AddStorage(id string, st storage.Storage) error { + return s.storageProvider.AddStorage(id, st) +} + +func (s *service) CreateTreeStorage(payload storage.TreeStorageCreatePayload) (storage.TreeStorage, error) { + return s.storageProvider.CreateTreeStorage(payload) +} + +func (s *service) CreateACLListStorage(payload storage.ACLListStorageCreatePayload) (storage.ListStorage, error) { + return s.storageProvider.CreateACLListStorage(payload) +} + +func (s *service) Name() (name string) { + return CName +} + +func (s *service) ImportedACLSyncData() ImportedACLSyncData { + return s.importedACLSyncData +} + +func (s *service) Run(ctx context.Context) (err error) { + return nil +} + +func (s service) Close(ctx context.Context) (err error) { + return nil +} + +func (s *service) importACLList(a *app.App) (err error) { + path := fmt.Sprintf("%s/%s", etc.Path(), "acl.yml") + st, err := acllistbuilder.NewACLListStorageBuilderFromFile(path) + if err != nil { + return err + } + + id, err := st.ID() + if err != nil { + return err + } + + header, err := st.Header() + if err != nil { + return err + } + + // checking that acl list contains all the needed permissions for all our nodes + err = s.checkActualNodesPermissions(st, a) + if err != nil { + return err + } + + s.importedACLSyncData = ImportedACLSyncData{ + Id: id, + Header: header, + Records: st.GetRawRecords(), + } + + log.Infof("imported ACLList with id %s", id) + return s.storageProvider.AddStorage(id, st) +} + +func (s *service) checkActualNodesPermissions(st *acllistbuilder.ACLListStorageBuilder, a *app.App) error { + nodes := a.MustComponent(node.CName).(node.Service) + acc := a.MustComponent(account.CName).(account.Service) + + aclList, err := list.BuildACLListWithIdentity(acc.Account(), st) + if err != nil { + return err + } + + state := aclList.ACLState() + + // checking own state + if state.GetUserStates()[acc.Account().Identity].Permissions != aclpb.ACLChange_Admin { + return fmt.Errorf("own node with signing key %s should be admin", acc.Account().Identity) + } + + // checking other nodes' states + for _, n := range nodes.Nodes() { + if state.GetUserStates()[n.SigningKeyString].Permissions != aclpb.ACLChange_Admin { + return fmt.Errorf("other node with signing key %s should be admin", n.SigningKeyString) + } + } + + return nil +} diff --git a/service/sync/message/service.go b/service/sync/message/service.go index efe76900..716fc4b2 100644 --- a/service/sync/message/service.go +++ b/service/sync/message/service.go @@ -2,6 +2,7 @@ package message import ( "context" + "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/pool" @@ -85,7 +86,7 @@ func (s *service) SendMessageAsync(peerId string, msg *syncproto.Sync) (err erro return } - go s.sendAsync(peerId, msgType(msg), marshalled) + go s.sendAsync(peerId, msgInfo(msg), marshalled) return } @@ -108,15 +109,16 @@ func (s *service) sendAsync(peerId string, msgTypeStr string, marshalled []byte) }) } -func msgType(content *syncproto.Sync) string { +func msgInfo(content *syncproto.Sync) (syncMethod string) { msg := content.GetMessage() switch { case msg.GetFullSyncRequest() != nil: - return "FullSyncRequest" + syncMethod = "FullSyncRequest" case msg.GetFullSyncResponse() != nil: - return "FullSyncResponse" + syncMethod = "FullSyncResponse" case msg.GetHeadUpdate() != nil: - return "HeadUpdate" + syncMethod = "HeadUpdate" } - return "UnknownMessage" + syncMethod = fmt.Sprintf("method: %s, treeType: %s", syncMethod, content.TreeHeader.DocType.String()) + return } diff --git a/service/sync/requesthandler/requesthandler.go b/service/sync/requesthandler/requesthandler.go index 2eb70551..eeeb0d52 100644 --- a/service/sync/requesthandler/requesthandler.go +++ b/service/sync/requesthandler/requesthandler.go @@ -5,9 +5,8 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache" "github.com/anytypeio/go-anytype-infrastructure-experiments/syncproto" @@ -61,142 +60,147 @@ func (r *requestHandler) HandleSyncMessage(ctx context.Context, senderId string, msg := content.GetMessage() switch { case msg.GetFullSyncRequest() != nil: - return r.HandleFullSyncRequest(ctx, senderId, msg.GetFullSyncRequest()) + return r.HandleFullSyncRequest(ctx, senderId, msg.GetFullSyncRequest(), content.GetTreeHeader(), content.GetTreeId()) case msg.GetFullSyncResponse() != nil: - return r.HandleFullSyncResponse(ctx, senderId, msg.GetFullSyncResponse()) + return r.HandleFullSyncResponse(ctx, senderId, msg.GetFullSyncResponse(), content.GetTreeHeader(), content.GetTreeId()) case msg.GetHeadUpdate() != nil: - return r.HandleHeadUpdate(ctx, senderId, msg.GetHeadUpdate()) + return r.HandleHeadUpdate(ctx, senderId, msg.GetHeadUpdate(), content.GetTreeHeader(), content.GetTreeId()) + case msg.GetAclList() != nil: + return r.HandleACLList(ctx, senderId, msg.GetAclList(), content.GetTreeHeader(), content.GetTreeId()) } return nil } -func (r *requestHandler) HandleHeadUpdate(ctx context.Context, senderId string, update *syncproto.SyncHeadUpdate) (err error) { +func (r *requestHandler) HandleHeadUpdate( + ctx context.Context, + senderId string, + update *syncproto.SyncHeadUpdate, + header *aclpb.Header, + treeId string) (err error) { + var ( fullRequest *syncproto.SyncFullRequest snapshotPath []string - result acltree.AddResult + result tree.AddResult ) - log.With(zap.String("peerId", senderId), zap.String("treeId", update.TreeId)). + log.With(zap.String("peerId", senderId), zap.String("treeId", treeId)). Debug("processing head update") - err = r.treeCache.Do(ctx, update.TreeId, func(tree acltree.ACLTree) error { - // TODO: check if we already have those changes - result, err = tree.AddRawChanges(ctx, update.Changes...) + err = r.treeCache.Do(ctx, treeId, func(obj any) error { + objTree := obj.(tree.ObjectTree) + objTree.Lock() + defer objTree.Unlock() + + if slice.UnsortedEquals(update.Heads, objTree.Heads()) { + return nil + } + + result, err = objTree.AddRawChanges(ctx, update.Changes...) if err != nil { return err } - log.With(zap.Strings("update heads", update.Heads), zap.Strings("tree heads", tree.Heads())). - Debug("comparing heads after head update") - shouldFullSync := !slice.UnsortedEquals(update.Heads, tree.Heads()) - snapshotPath = tree.SnapshotPath() + + // if we couldn't add all the changes + shouldFullSync := len(update.Changes) != len(result.Added) + snapshotPath = objTree.SnapshotPath() if shouldFullSync { - fullRequest, err = r.prepareFullSyncRequest(update.TreeId, update.TreeHeader, update.SnapshotPath, tree) + fullRequest, err = r.prepareFullSyncRequest(objTree) if err != nil { return err } } return nil }) + // if there are no such tree - if err == treestorage.ErrUnknownTreeId { - // TODO: maybe we can optimize this by sending the header and stuff right away, so when the tree is created we are able to add it on first request - fullRequest = &syncproto.SyncFullRequest{ - TreeId: update.TreeId, - TreeHeader: update.TreeHeader, - } + if err == storage.ErrUnknownTreeId { + fullRequest = &syncproto.SyncFullRequest{} } // if we have incompatible heads, or we haven't seen the tree at all if fullRequest != nil { - return r.messageService.SendMessageAsync(senderId, syncproto.WrapFullRequest(fullRequest)) + return r.messageService.SendMessageAsync(senderId, syncproto.WrapFullRequest(fullRequest, header, treeId)) } // if error or nothing has changed if err != nil || len(result.Added) == 0 { return err } + // otherwise sending heads update message newUpdate := &syncproto.SyncHeadUpdate{ Heads: result.Heads, Changes: result.Added, SnapshotPath: snapshotPath, - TreeId: update.TreeId, - TreeHeader: update.TreeHeader, } - return r.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(newUpdate)) + return r.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(newUpdate, header, treeId)) } -func (r *requestHandler) HandleFullSyncRequest(ctx context.Context, senderId string, request *syncproto.SyncFullRequest) (err error) { - var ( - fullResponse *syncproto.SyncFullResponse - snapshotPath []string - result acltree.AddResult - ) - log.With(zap.String("peerId", senderId), zap.String("treeId", request.TreeId)). - Debug("processing full sync request") +func (r *requestHandler) HandleFullSyncRequest( + ctx context.Context, + senderId string, + request *syncproto.SyncFullRequest, + header *aclpb.Header, + treeId string) (err error) { - err = r.treeCache.Do(ctx, request.TreeId, func(tree acltree.ACLTree) error { - // TODO: check if we already have those changes - // if we have non-empty request - if len(request.Heads) != 0 { - result, err = tree.AddRawChanges(ctx, request.Changes...) - if err != nil { - return err - } - } - snapshotPath = tree.SnapshotPath() - fullResponse, err = r.prepareFullSyncResponse(request.TreeId, request.SnapshotPath, request.Changes, tree) + var fullResponse *syncproto.SyncFullResponse + err = r.treeCache.Do(ctx, treeId, func(obj any) error { + objTree := obj.(tree.ObjectTree) + objTree.Lock() + defer objTree.Unlock() + + fullResponse, err = r.prepareFullSyncResponse(treeId, request.SnapshotPath, request.Heads, objTree) if err != nil { return err } return nil }) + if err != nil { return err } - err = r.messageService.SendMessageAsync(senderId, syncproto.WrapFullResponse(fullResponse)) - // if error or nothing has changed - if err != nil || len(result.Added) == 0 { - return err - } - - // otherwise sending heads update message - newUpdate := &syncproto.SyncHeadUpdate{ - Heads: result.Heads, - Changes: result.Added, - SnapshotPath: snapshotPath, - TreeId: request.TreeId, - TreeHeader: request.TreeHeader, - } - return r.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(newUpdate)) + return r.messageService.SendMessageAsync(senderId, syncproto.WrapFullResponse(fullResponse, header, treeId)) } -func (r *requestHandler) HandleFullSyncResponse(ctx context.Context, senderId string, response *syncproto.SyncFullResponse) (err error) { +func (r *requestHandler) HandleFullSyncResponse( + ctx context.Context, + senderId string, + response *syncproto.SyncFullResponse, + header *aclpb.Header, + treeId string) (err error) { + var ( snapshotPath []string - result acltree.AddResult + result tree.AddResult ) - log.With(zap.String("peerId", senderId), zap.String("treeId", response.TreeId)). - Debug("processing full sync response") - err = r.treeCache.Do(ctx, response.TreeId, func(tree acltree.ACLTree) error { - // TODO: check if we already have those changes - result, err = tree.AddRawChanges(ctx, response.Changes...) + err = r.treeCache.Do(ctx, treeId, func(obj interface{}) error { + objTree := obj.(tree.ObjectTree) + objTree.Lock() + defer objTree.Unlock() + + // if we already have the heads for whatever reason + if slice.UnsortedEquals(response.Heads, objTree.Heads()) { + return nil + } + + result, err = objTree.AddRawChanges(ctx, response.Changes...) if err != nil { return err } - snapshotPath = tree.SnapshotPath() + snapshotPath = objTree.SnapshotPath() return nil }) + // if error or nothing has changed - if (err != nil || len(result.Added) == 0) && err != treestorage.ErrUnknownTreeId { + if (err != nil || len(result.Added) == 0) && err != storage.ErrUnknownTreeId { return err } // if we have a new tree - if err == treestorage.ErrUnknownTreeId { - err = r.createTree(ctx, response) + if err == storage.ErrUnknownTreeId { + err = r.createTree(ctx, response, header, treeId) if err != nil { return err } - result = acltree.AddResult{ + result = tree.AddResult{ OldHeads: []string{}, Heads: response.Heads, Added: response.Changes, @@ -207,66 +211,83 @@ func (r *requestHandler) HandleFullSyncResponse(ctx context.Context, senderId st Heads: result.Heads, Changes: result.Added, SnapshotPath: snapshotPath, - TreeId: response.TreeId, } - return r.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(newUpdate)) + return r.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(newUpdate, header, treeId)) } -func (r *requestHandler) prepareFullSyncRequest(treeId string, header *treepb.TreeHeader, theirPath []string, tree acltree.ACLTree) (*syncproto.SyncFullRequest, error) { - ourChanges, err := tree.ChangesAfterCommonSnapshot(theirPath) - if err != nil { - return nil, err +func (r *requestHandler) HandleACLList( + ctx context.Context, + senderId string, + req *syncproto.SyncACLList, + header *aclpb.Header, + id string) (err error) { + + err = r.treeCache.Do(ctx, id, func(obj interface{}) error { + return nil + }) + // do nothing if already added + if err == nil { + return nil } + // if not found then add to storage + if err == storage.ErrUnknownTreeId { + return r.createACLList(ctx, req, header, id) + } + return err +} + +func (r *requestHandler) prepareFullSyncRequest(t tree.ObjectTree) (*syncproto.SyncFullRequest, error) { return &syncproto.SyncFullRequest{ - Heads: tree.Heads(), - Changes: ourChanges, - TreeId: treeId, - SnapshotPath: tree.SnapshotPath(), - TreeHeader: header, + Heads: t.Heads(), + SnapshotPath: t.SnapshotPath(), }, nil } func (r *requestHandler) prepareFullSyncResponse( treeId string, - theirPath []string, - theirChanges []*aclpb.RawChange, - tree acltree.ACLTree) (*syncproto.SyncFullResponse, error) { - // TODO: we can probably use the common snapshot calculated on the request step from previous peer - ourChanges, err := tree.ChangesAfterCommonSnapshot(theirPath) + theirPath, theirHeads []string, + t tree.ObjectTree) (*syncproto.SyncFullResponse, error) { + ourChanges, err := t.ChangesAfterCommonSnapshot(theirPath, theirHeads) if err != nil { return nil, err } - theirMap := make(map[string]struct{}) - for _, ch := range theirChanges { - theirMap[ch.Id] = struct{}{} - } - - // filtering our changes, so we will not send the same changes back - var final []*aclpb.RawChange - for _, ch := range ourChanges { - if _, exists := theirMap[ch.Id]; !exists { - final = append(final, ch) - } - } - log.With(zap.Int("len(changes)", len(final)), zap.String("id", treeId)). - Debug("preparing changes for tree") return &syncproto.SyncFullResponse{ - Heads: tree.Heads(), - Changes: final, - TreeId: treeId, - SnapshotPath: tree.SnapshotPath(), - TreeHeader: tree.Header(), + Heads: t.Heads(), + Changes: ourChanges, + SnapshotPath: t.SnapshotPath(), }, nil } -func (r *requestHandler) createTree(ctx context.Context, response *syncproto.SyncFullResponse) error { +func (r *requestHandler) createTree( + ctx context.Context, + response *syncproto.SyncFullResponse, + header *aclpb.Header, + treeId string) error { + return r.treeCache.Add( ctx, - response.TreeId, - response.TreeHeader, - response.Changes, - func(tree acltree.ACLTree) error { - return nil + treeId, + storage.TreeStorageCreatePayload{ + TreeId: treeId, + Header: header, + Changes: response.Changes, + Heads: response.Heads, + }) +} + +func (r *requestHandler) createACLList( + ctx context.Context, + req *syncproto.SyncACLList, + header *aclpb.Header, + treeId string) error { + + return r.treeCache.Add( + ctx, + treeId, + storage.ACLListStorageCreatePayload{ + ListId: treeId, + Header: header, + Records: req.Records, }) } diff --git a/service/treecache/service.go b/service/treecache/service.go index 687300f0..8a440107 100644 --- a/service/treecache/service.go +++ b/service/treecache/service.go @@ -2,88 +2,82 @@ package treecache import ( "context" + "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" + aclstorage "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ocache" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/service/storage" "go.uber.org/zap" ) const CName = "treecache" -// TODO: add context -type ACLTreeFunc = func(tree acltree.ACLTree) error -type ChangeBuildFunc = func(builder acltree.ChangeBuilder) error +type ObjFunc = func(obj interface{}) error var log = logger.NewNamed("treecache") type Service interface { - Do(ctx context.Context, treeId string, f ACLTreeFunc) error - Add(ctx context.Context, treeId string, header *treepb.TreeHeader, changes []*aclpb.RawChange, f ACLTreeFunc) error - Create(ctx context.Context, build ChangeBuildFunc, f ACLTreeFunc) error + Do(ctx context.Context, id string, f ObjFunc) error + Add(ctx context.Context, id string, payload any) error } type service struct { - treeProvider treestorage.Provider - account account.Service - cache ocache.OCache + storage storage.Service + account account.Service + cache ocache.OCache } func New() app.ComponentRunnable { return &service{} } -func (s *service) Create(ctx context.Context, build ChangeBuildFunc, f ACLTreeFunc) error { - acc := s.account.Account() - st, err := acltree.CreateNewTreeStorageWithACL(acc, build, s.treeProvider.CreateTreeStorage) - if err != nil { - return err - } - - id, err := st.TreeID() - if err != nil { - return err - } - - return s.Do(ctx, id, f) -} - -func (s *service) Do(ctx context.Context, treeId string, f ACLTreeFunc) error { +func (s *service) Do(ctx context.Context, treeId string, f ObjFunc) error { log. With(zap.String("treeId", treeId)). Debug("requesting tree from cache to perform operation") - tree, err := s.cache.Get(ctx, treeId) + t, err := s.cache.Get(ctx, treeId) defer s.cache.Release(treeId) if err != nil { return err } - aclTree := tree.(acltree.ACLTree) - aclTree.Lock() - defer aclTree.Unlock() - return f(tree.(acltree.ACLTree)) + return f(t) } -func (s *service) Add(ctx context.Context, treeId string, header *treepb.TreeHeader, changes []*aclpb.RawChange, f ACLTreeFunc) error { - log. - With(zap.String("treeId", treeId), zap.Int("len(changes)", len(changes))). - Debug("adding tree with changes") +func (s *service) Add(ctx context.Context, treeId string, payload any) error { + switch pl := payload.(type) { + case aclstorage.TreeStorageCreatePayload: + log. + With(zap.String("treeId", treeId), zap.Int("len(changes)", len(pl.Changes))). + Debug("adding Tree with changes") + + _, err := s.storage.CreateTreeStorage(payload.(aclstorage.TreeStorageCreatePayload)) + if err != nil { + return err + } + case aclstorage.ACLListStorageCreatePayload: + log. + With(zap.String("treeId", treeId), zap.Int("len(changes)", len(pl.Records))). + Debug("adding ACLList with records") + + _, err := s.storage.CreateACLListStorage(payload.(aclstorage.ACLListStorageCreatePayload)) + if err != nil { + return err + } - _, err := s.treeProvider.CreateTreeStorage(treeId, header, changes) - if err != nil { - return err } - return s.Do(ctx, treeId, f) + return nil } func (s *service) Init(ctx context.Context, a *app.App) (err error) { s.cache = ocache.New(s.loadTree) s.account = a.MustComponent(account.CName).(account.Service) - s.treeProvider = treestorage.NewInMemoryTreeStorageProvider() + s.storage = a.MustComponent(storage.CName).(storage.Service) // TODO: for test we should load some predefined keys return nil } @@ -101,11 +95,33 @@ func (s *service) Close(ctx context.Context) (err error) { } func (s *service) loadTree(ctx context.Context, id string) (ocache.Object, error) { - tree, err := s.treeProvider.TreeStorage(id) + t, err := s.storage.Storage(id) if err != nil { return nil, err } - // TODO: should probably accept nil listeners - aclTree, err := acltree.BuildACLTree(tree, s.account.Account(), acltree.NoOpListener{}) - return aclTree, err + header, err := t.Header() + if err != nil { + return nil, err + } + + switch header.DocType { // handler + case aclpb.Header_ACL: + return list.BuildACLListWithIdentity(s.account.Account(), t.(aclstorage.ListStorage)) + case aclpb.Header_DocTree: + break + default: + return nil, fmt.Errorf("incorrect type") + } + log.Info("got header", zap.String("header", header.String())) + var objTree tree.ObjectTree + err = s.Do(ctx, header.AclListId, func(obj interface{}) error { + aclList := obj.(list.ACLList) + objTree, err = tree.BuildObjectTree(t.(aclstorage.TreeStorage), nil, aclList) + if err != nil { + return err + } + return nil + }) + + return objTree, err } diff --git a/syncproto/helpers.go b/syncproto/helpers.go index e3059bda..fbbf11dc 100644 --- a/syncproto/helpers.go +++ b/syncproto/helpers.go @@ -1,19 +1,45 @@ package syncproto -func WrapHeadUpdate(update *SyncHeadUpdate) *Sync { - return &Sync{Message: &SyncContentValue{ - Value: &SyncContentValueValueOfHeadUpdate{HeadUpdate: update}, - }} +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" +) + +func WrapHeadUpdate(update *SyncHeadUpdate, header *aclpb.Header, treeId string) *Sync { + return &Sync{ + Message: &SyncContentValue{ + Value: &SyncContentValueValueOfHeadUpdate{HeadUpdate: update}, + }, + TreeHeader: header, + TreeId: treeId, + } } -func WrapFullRequest(request *SyncFullRequest) *Sync { - return &Sync{Message: &SyncContentValue{ - Value: &SyncContentValueValueOfFullSyncRequest{FullSyncRequest: request}, - }} +func WrapFullRequest(request *SyncFullRequest, header *aclpb.Header, treeId string) *Sync { + return &Sync{ + Message: &SyncContentValue{ + Value: &SyncContentValueValueOfFullSyncRequest{FullSyncRequest: request}, + }, + TreeHeader: header, + TreeId: treeId, + } } -func WrapFullResponse(response *SyncFullResponse) *Sync { - return &Sync{Message: &SyncContentValue{ - Value: &SyncContentValueValueOfFullSyncResponse{FullSyncResponse: response}, - }} +func WrapFullResponse(response *SyncFullResponse, header *aclpb.Header, treeId string) *Sync { + return &Sync{ + Message: &SyncContentValue{ + Value: &SyncContentValueValueOfFullSyncResponse{FullSyncResponse: response}, + }, + TreeHeader: header, + TreeId: treeId, + } +} + +func WrapACLList(aclList *SyncACLList, header *aclpb.Header, id string) *Sync { + return &Sync{ + Message: &SyncContentValue{ + Value: &SyncContentValueValueOfAclList{AclList: aclList}, + }, + TreeHeader: header, + TreeId: id, + } } diff --git a/syncproto/proto/sync.proto b/syncproto/proto/sync.proto index dba2a079..d6cc1ddd 100644 --- a/syncproto/proto/sync.proto +++ b/syncproto/proto/sync.proto @@ -3,7 +3,6 @@ package anytype; option go_package = "/syncproto"; import "pkg/acl/aclchanges/aclpb/protos/aclchanges.proto"; -import "pkg/acl/treestorage/treepb/protos/tree.proto"; message Message { Header header = 1; @@ -52,21 +51,26 @@ message System { message Sync { string spaceId = 1; ContentValue message = 2; + acl.Header treeHeader = 3; + string treeId = 4; message ContentValue { oneof value { HeadUpdate headUpdate = 1; Full.Request fullSyncRequest = 2; Full.Response fullSyncResponse = 3; + ACLList aclList = 4; } } + message ACLList { + repeated acl.RawRecord records = 1; + } + message HeadUpdate { repeated string heads = 1; repeated acl.RawChange changes = 2; - string treeId = 3; - repeated string snapshotPath = 4; - tree.TreeHeader treeHeader = 5; + repeated string snapshotPath = 3; } message Full { @@ -74,17 +78,13 @@ message Sync { message Request { repeated string heads = 1; repeated acl.RawChange changes = 2; - string treeId = 3; - repeated string snapshotPath = 4; - tree.TreeHeader treeHeader = 5; + repeated string snapshotPath = 3; } message Response { repeated string heads = 1; repeated acl.RawChange changes = 2; - string treeId = 3; - repeated string snapshotPath = 4; - tree.TreeHeader treeHeader = 5; + repeated string snapshotPath = 3; } } } diff --git a/syncproto/sync.pb.go b/syncproto/sync.pb.go index aa69618b..0f8d273b 100644 --- a/syncproto/sync.pb.go +++ b/syncproto/sync.pb.go @@ -6,7 +6,6 @@ package syncproto import ( fmt "fmt" aclpb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - treepb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" proto "github.com/gogo/protobuf/proto" io "io" math "math" @@ -450,8 +449,10 @@ func (m *SystemError) GetDescription() string { } type Sync struct { - SpaceId string `protobuf:"bytes,1,opt,name=spaceId,proto3" json:"spaceId,omitempty"` - Message *SyncContentValue `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + SpaceId string `protobuf:"bytes,1,opt,name=spaceId,proto3" json:"spaceId,omitempty"` + Message *SyncContentValue `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + TreeHeader *aclpb.Header `protobuf:"bytes,3,opt,name=treeHeader,proto3" json:"treeHeader,omitempty"` + TreeId string `protobuf:"bytes,4,opt,name=treeId,proto3" json:"treeId,omitempty"` } func (m *Sync) Reset() { *m = Sync{} } @@ -501,11 +502,26 @@ func (m *Sync) GetMessage() *SyncContentValue { return nil } +func (m *Sync) GetTreeHeader() *aclpb.Header { + if m != nil { + return m.TreeHeader + } + return nil +} + +func (m *Sync) GetTreeId() string { + if m != nil { + return m.TreeId + } + return "" +} + type SyncContentValue struct { // Types that are valid to be assigned to Value: // *SyncContentValueValueOfHeadUpdate // *SyncContentValueValueOfFullSyncRequest // *SyncContentValueValueOfFullSyncResponse + // *SyncContentValueValueOfAclList Value IsSyncContentValueValue `protobuf_oneof:"value"` } @@ -557,10 +573,14 @@ type SyncContentValueValueOfFullSyncRequest struct { type SyncContentValueValueOfFullSyncResponse struct { FullSyncResponse *SyncFullResponse `protobuf:"bytes,3,opt,name=fullSyncResponse,proto3,oneof" json:"fullSyncResponse,omitempty"` } +type SyncContentValueValueOfAclList struct { + AclList *SyncACLList `protobuf:"bytes,4,opt,name=aclList,proto3,oneof" json:"aclList,omitempty"` +} func (*SyncContentValueValueOfHeadUpdate) IsSyncContentValueValue() {} func (*SyncContentValueValueOfFullSyncRequest) IsSyncContentValueValue() {} func (*SyncContentValueValueOfFullSyncResponse) IsSyncContentValueValue() {} +func (*SyncContentValueValueOfAclList) IsSyncContentValueValue() {} func (m *SyncContentValue) GetValue() IsSyncContentValueValue { if m != nil { @@ -590,28 +610,78 @@ func (m *SyncContentValue) GetFullSyncResponse() *SyncFullResponse { return nil } +func (m *SyncContentValue) GetAclList() *SyncACLList { + if x, ok := m.GetValue().(*SyncContentValueValueOfAclList); ok { + return x.AclList + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*SyncContentValue) XXX_OneofWrappers() []interface{} { return []interface{}{ (*SyncContentValueValueOfHeadUpdate)(nil), (*SyncContentValueValueOfFullSyncRequest)(nil), (*SyncContentValueValueOfFullSyncResponse)(nil), + (*SyncContentValueValueOfAclList)(nil), } } +type SyncACLList struct { + Records []*aclpb.RawRecord `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` +} + +func (m *SyncACLList) Reset() { *m = SyncACLList{} } +func (m *SyncACLList) String() string { return proto.CompactTextString(m) } +func (*SyncACLList) ProtoMessage() {} +func (*SyncACLList) Descriptor() ([]byte, []int) { + return fileDescriptor_4b28dfdd48a89166, []int{3, 1} +} +func (m *SyncACLList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SyncACLList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SyncACLList.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 *SyncACLList) XXX_Merge(src proto.Message) { + xxx_messageInfo_SyncACLList.Merge(m, src) +} +func (m *SyncACLList) XXX_Size() int { + return m.Size() +} +func (m *SyncACLList) XXX_DiscardUnknown() { + xxx_messageInfo_SyncACLList.DiscardUnknown(m) +} + +var xxx_messageInfo_SyncACLList proto.InternalMessageInfo + +func (m *SyncACLList) GetRecords() []*aclpb.RawRecord { + if m != nil { + return m.Records + } + return nil +} + type SyncHeadUpdate struct { Heads []string `protobuf:"bytes,1,rep,name=heads,proto3" json:"heads,omitempty"` Changes []*aclpb.RawChange `protobuf:"bytes,2,rep,name=changes,proto3" json:"changes,omitempty"` - TreeId string `protobuf:"bytes,3,opt,name=treeId,proto3" json:"treeId,omitempty"` - SnapshotPath []string `protobuf:"bytes,4,rep,name=snapshotPath,proto3" json:"snapshotPath,omitempty"` - TreeHeader *treepb.TreeHeader `protobuf:"bytes,5,opt,name=treeHeader,proto3" json:"treeHeader,omitempty"` + SnapshotPath []string `protobuf:"bytes,3,rep,name=snapshotPath,proto3" json:"snapshotPath,omitempty"` } func (m *SyncHeadUpdate) Reset() { *m = SyncHeadUpdate{} } func (m *SyncHeadUpdate) String() string { return proto.CompactTextString(m) } func (*SyncHeadUpdate) ProtoMessage() {} func (*SyncHeadUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_4b28dfdd48a89166, []int{3, 1} + return fileDescriptor_4b28dfdd48a89166, []int{3, 2} } func (m *SyncHeadUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -654,13 +724,6 @@ func (m *SyncHeadUpdate) GetChanges() []*aclpb.RawChange { return nil } -func (m *SyncHeadUpdate) GetTreeId() string { - if m != nil { - return m.TreeId - } - return "" -} - func (m *SyncHeadUpdate) GetSnapshotPath() []string { if m != nil { return m.SnapshotPath @@ -668,13 +731,6 @@ func (m *SyncHeadUpdate) GetSnapshotPath() []string { return nil } -func (m *SyncHeadUpdate) GetTreeHeader() *treepb.TreeHeader { - if m != nil { - return m.TreeHeader - } - return nil -} - type SyncFull struct { } @@ -682,7 +738,7 @@ func (m *SyncFull) Reset() { *m = SyncFull{} } func (m *SyncFull) String() string { return proto.CompactTextString(m) } func (*SyncFull) ProtoMessage() {} func (*SyncFull) Descriptor() ([]byte, []int) { - return fileDescriptor_4b28dfdd48a89166, []int{3, 2} + return fileDescriptor_4b28dfdd48a89166, []int{3, 3} } func (m *SyncFull) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -715,16 +771,14 @@ var xxx_messageInfo_SyncFull proto.InternalMessageInfo type SyncFullRequest struct { Heads []string `protobuf:"bytes,1,rep,name=heads,proto3" json:"heads,omitempty"` Changes []*aclpb.RawChange `protobuf:"bytes,2,rep,name=changes,proto3" json:"changes,omitempty"` - TreeId string `protobuf:"bytes,3,opt,name=treeId,proto3" json:"treeId,omitempty"` - SnapshotPath []string `protobuf:"bytes,4,rep,name=snapshotPath,proto3" json:"snapshotPath,omitempty"` - TreeHeader *treepb.TreeHeader `protobuf:"bytes,5,opt,name=treeHeader,proto3" json:"treeHeader,omitempty"` + SnapshotPath []string `protobuf:"bytes,3,rep,name=snapshotPath,proto3" json:"snapshotPath,omitempty"` } func (m *SyncFullRequest) Reset() { *m = SyncFullRequest{} } func (m *SyncFullRequest) String() string { return proto.CompactTextString(m) } func (*SyncFullRequest) ProtoMessage() {} func (*SyncFullRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_4b28dfdd48a89166, []int{3, 2, 0} + return fileDescriptor_4b28dfdd48a89166, []int{3, 3, 0} } func (m *SyncFullRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -767,13 +821,6 @@ func (m *SyncFullRequest) GetChanges() []*aclpb.RawChange { return nil } -func (m *SyncFullRequest) GetTreeId() string { - if m != nil { - return m.TreeId - } - return "" -} - func (m *SyncFullRequest) GetSnapshotPath() []string { if m != nil { return m.SnapshotPath @@ -781,26 +828,17 @@ func (m *SyncFullRequest) GetSnapshotPath() []string { return nil } -func (m *SyncFullRequest) GetTreeHeader() *treepb.TreeHeader { - if m != nil { - return m.TreeHeader - } - return nil -} - type SyncFullResponse struct { Heads []string `protobuf:"bytes,1,rep,name=heads,proto3" json:"heads,omitempty"` Changes []*aclpb.RawChange `protobuf:"bytes,2,rep,name=changes,proto3" json:"changes,omitempty"` - TreeId string `protobuf:"bytes,3,opt,name=treeId,proto3" json:"treeId,omitempty"` - SnapshotPath []string `protobuf:"bytes,4,rep,name=snapshotPath,proto3" json:"snapshotPath,omitempty"` - TreeHeader *treepb.TreeHeader `protobuf:"bytes,5,opt,name=treeHeader,proto3" json:"treeHeader,omitempty"` + SnapshotPath []string `protobuf:"bytes,3,rep,name=snapshotPath,proto3" json:"snapshotPath,omitempty"` } func (m *SyncFullResponse) Reset() { *m = SyncFullResponse{} } func (m *SyncFullResponse) String() string { return proto.CompactTextString(m) } func (*SyncFullResponse) ProtoMessage() {} func (*SyncFullResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_4b28dfdd48a89166, []int{3, 2, 1} + return fileDescriptor_4b28dfdd48a89166, []int{3, 3, 1} } func (m *SyncFullResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -843,13 +881,6 @@ func (m *SyncFullResponse) GetChanges() []*aclpb.RawChange { return nil } -func (m *SyncFullResponse) GetTreeId() string { - if m != nil { - return m.TreeId - } - return "" -} - func (m *SyncFullResponse) GetSnapshotPath() []string { if m != nil { return m.SnapshotPath @@ -857,13 +888,6 @@ func (m *SyncFullResponse) GetSnapshotPath() []string { return nil } -func (m *SyncFullResponse) GetTreeHeader() *treepb.TreeHeader { - if m != nil { - return m.TreeHeader - } - return nil -} - func init() { proto.RegisterEnum("anytype.MessageType", MessageType_name, MessageType_value) proto.RegisterEnum("anytype.SystemErrorCode", SystemErrorCode_name, SystemErrorCode_value) @@ -876,6 +900,7 @@ func init() { proto.RegisterType((*SystemError)(nil), "anytype.System.Error") proto.RegisterType((*Sync)(nil), "anytype.Sync") proto.RegisterType((*SyncContentValue)(nil), "anytype.Sync.ContentValue") + proto.RegisterType((*SyncACLList)(nil), "anytype.Sync.ACLList") proto.RegisterType((*SyncHeadUpdate)(nil), "anytype.Sync.HeadUpdate") proto.RegisterType((*SyncFull)(nil), "anytype.Sync.Full") proto.RegisterType((*SyncFullRequest)(nil), "anytype.Sync.Full.Request") @@ -885,57 +910,58 @@ func init() { func init() { proto.RegisterFile("syncproto/proto/sync.proto", fileDescriptor_4b28dfdd48a89166) } var fileDescriptor_4b28dfdd48a89166 = []byte{ - // 799 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x55, 0xd1, 0x6e, 0xe3, 0x44, - 0x14, 0xf5, 0x24, 0x4e, 0xbc, 0xbe, 0xae, 0x5a, 0x33, 0xdb, 0x45, 0xc6, 0xac, 0x22, 0x2b, 0x02, - 0x61, 0x01, 0x72, 0x57, 0x81, 0x15, 0x12, 0x6f, 0xbb, 0xa1, 0x55, 0x22, 0x20, 0x89, 0x26, 0x49, - 0x91, 0x78, 0x59, 0x4d, 0xed, 0xd9, 0x24, 0x8a, 0x3b, 0x36, 0x1e, 0x07, 0xc8, 0x5f, 0xec, 0x37, - 0xf0, 0x0d, 0x48, 0x20, 0xbe, 0x80, 0xc7, 0x7d, 0xe4, 0x11, 0xb5, 0x12, 0x1f, 0x01, 0x2f, 0x68, - 0xc6, 0x76, 0xe2, 0x66, 0xfb, 0x03, 0xfb, 0xd0, 0x66, 0xee, 0xb9, 0xe7, 0x5c, 0x9f, 0x1b, 0xdf, - 0xb9, 0x01, 0x57, 0x6c, 0x79, 0x98, 0x66, 0x49, 0x9e, 0x9c, 0x15, 0xff, 0x65, 0x1c, 0xa8, 0x23, - 0x36, 0x28, 0xdf, 0xe6, 0xdb, 0x94, 0xb9, 0x4f, 0xd2, 0xf5, 0xe2, 0x8c, 0x86, 0xb1, 0xfc, 0x0b, - 0x97, 0x94, 0x2f, 0x98, 0x90, 0xc7, 0xf4, 0xaa, 0xd0, 0x88, 0x1a, 0x5e, 0x48, 0xdd, 0x4f, 0x2b, - 0x45, 0x9e, 0x31, 0x26, 0xf2, 0x24, 0xa3, 0x0b, 0xa6, 0xce, 0x7b, 0x8d, 0x8c, 0x0a, 0x76, 0xf7, - 0x02, 0x8c, 0x6f, 0x99, 0x10, 0x74, 0xc1, 0xf0, 0x47, 0xd0, 0x5e, 0x32, 0x1a, 0xb1, 0xcc, 0x41, - 0x1e, 0xf2, 0xad, 0xde, 0x49, 0x50, 0x9a, 0x08, 0x06, 0x0a, 0x26, 0x65, 0x1a, 0x63, 0xd0, 0x23, - 0x9a, 0x53, 0xa7, 0xe1, 0x21, 0xff, 0x88, 0xa8, 0x73, 0xf7, 0x17, 0x04, 0xed, 0x82, 0x86, 0x1d, - 0x30, 0xf2, 0x8c, 0x86, 0x6c, 0x18, 0xa9, 0x42, 0x47, 0xa4, 0x0a, 0xf1, 0x63, 0x30, 0x33, 0xf6, - 0xc3, 0x86, 0x89, 0x7c, 0x18, 0x29, 0xb5, 0x4e, 0xf6, 0x80, 0xd4, 0x65, 0x2c, 0x8d, 0xb7, 0xc3, - 0xc8, 0x69, 0xaa, 0x5c, 0x15, 0x62, 0x1f, 0x74, 0xe9, 0xc3, 0xd1, 0x3d, 0xe4, 0x1f, 0xf7, 0x4e, - 0x77, 0xbe, 0x4a, 0xe7, 0xb3, 0x6d, 0xca, 0x88, 0x62, 0xc8, 0x27, 0x44, 0xec, 0x6a, 0xb3, 0x18, - 0xf2, 0x97, 0x89, 0xd3, 0xf2, 0x90, 0x6f, 0x92, 0x3d, 0xd0, 0xfd, 0xb5, 0x09, 0xed, 0xe9, 0x56, - 0xe4, 0xec, 0x1a, 0x7f, 0x01, 0xe6, 0x92, 0xf2, 0x48, 0x2c, 0xe9, 0x9a, 0x95, 0xfd, 0xbe, 0xb7, - 0xab, 0x5b, 0x70, 0x82, 0x41, 0x45, 0x20, 0x7b, 0xae, 0xf4, 0x92, 0xae, 0xf8, 0x42, 0xd9, 0xb7, - 0x6a, 0x5e, 0x4a, 0xcd, 0x64, 0xc5, 0x17, 0x44, 0x31, 0xf0, 0x87, 0xd0, 0xa4, 0xe1, 0x5a, 0xf5, - 0x62, 0xf5, 0x1e, 0x1e, 0x12, 0x9f, 0x85, 0x6b, 0x22, 0xf3, 0xee, 0x53, 0x30, 0x07, 0xb5, 0xea, - 0x27, 0xea, 0xbd, 0x84, 0x49, 0x7c, 0xc9, 0x32, 0xb1, 0x4a, 0xb8, 0x32, 0x67, 0x92, 0x43, 0xd8, - 0xed, 0x82, 0x2e, 0x9f, 0x85, 0x5d, 0x78, 0xb0, 0xe1, 0xab, 0x9f, 0x67, 0xab, 0xeb, 0xa2, 0x0f, - 0x9d, 0xec, 0x62, 0xb7, 0x07, 0xcd, 0x67, 0xe1, 0x1a, 0x7f, 0x02, 0x2d, 0x96, 0x65, 0x49, 0x56, - 0x7a, 0x7e, 0x74, 0x68, 0xe5, 0x5c, 0x26, 0x49, 0xc1, 0x71, 0x5f, 0x21, 0x68, 0x29, 0x00, 0x07, - 0xa0, 0x87, 0x49, 0x54, 0x54, 0x3d, 0xee, 0xb9, 0xf7, 0xaa, 0x82, 0x7e, 0x12, 0x31, 0xa2, 0x78, - 0xd8, 0x03, 0x2b, 0x62, 0x22, 0xcc, 0x56, 0x69, 0x2e, 0x7d, 0x37, 0x94, 0xef, 0x3a, 0xd4, 0x7d, - 0x0a, 0xba, 0xe4, 0x63, 0x0b, 0x8c, 0xf9, 0xe8, 0xeb, 0xd1, 0xf8, 0xbb, 0x91, 0xad, 0x61, 0x0f, - 0x1e, 0xcf, 0x47, 0xd3, 0xf9, 0x64, 0x32, 0x26, 0xb3, 0xf3, 0xaf, 0x5e, 0x4c, 0xc8, 0x78, 0x36, - 0xee, 0x8f, 0xbf, 0x79, 0x71, 0x79, 0x4e, 0xa6, 0xc3, 0xf1, 0xc8, 0x86, 0xee, 0xbf, 0x6d, 0xd0, - 0xa7, 0x5b, 0x1e, 0xca, 0x09, 0x11, 0xe9, 0x7e, 0xb2, 0x4c, 0x52, 0x85, 0xf8, 0x73, 0x30, 0xae, - 0x8b, 0x61, 0x28, 0x9b, 0xac, 0xdb, 0xe5, 0x61, 0xd0, 0x4f, 0x78, 0xce, 0x78, 0x7e, 0x49, 0xe3, - 0x0d, 0x23, 0x15, 0xd5, 0xfd, 0x07, 0xc1, 0x51, 0x3d, 0x83, 0xbf, 0x04, 0x90, 0x33, 0x3e, 0x4f, - 0x23, 0x9a, 0x57, 0x63, 0xe1, 0xdc, 0xad, 0x34, 0xd8, 0xe5, 0x07, 0x1a, 0xa9, 0xb1, 0xf1, 0x05, - 0x9c, 0xbc, 0xdc, 0xc4, 0xb1, 0x24, 0x91, 0x62, 0xa6, 0xef, 0xb7, 0x72, 0xb1, 0x89, 0xe3, 0xa0, - 0x64, 0x0c, 0x34, 0x72, 0x28, 0xc2, 0x43, 0xb0, 0xf7, 0x90, 0x48, 0x13, 0x2e, 0x58, 0x39, 0x43, - 0xef, 0xdf, 0x5b, 0xa8, 0xa0, 0x0c, 0x34, 0xf2, 0x86, 0xec, 0xb9, 0x01, 0xad, 0x1f, 0x65, 0x5f, - 0xee, 0x1f, 0x08, 0x60, 0x6f, 0x1c, 0x9f, 0x42, 0x4b, 0x1a, 0x17, 0x0e, 0xf2, 0x9a, 0xbe, 0x49, - 0x8a, 0x00, 0xfb, 0x60, 0x94, 0x9b, 0xc4, 0x69, 0x78, 0x4d, 0xdf, 0xea, 0x1d, 0x07, 0x34, 0x8c, - 0x03, 0x42, 0x7f, 0xea, 0x2b, 0x98, 0x54, 0x69, 0xfc, 0x2e, 0xb4, 0xe5, 0x0a, 0x29, 0x2f, 0xaa, - 0x49, 0xca, 0x08, 0x77, 0xe1, 0x48, 0x70, 0x9a, 0x8a, 0x65, 0x92, 0x4f, 0x68, 0xbe, 0x74, 0x74, - 0x55, 0xfe, 0x0e, 0x86, 0x9f, 0x00, 0x48, 0x76, 0xb1, 0x2b, 0xd4, 0x15, 0xb5, 0x7a, 0x76, 0xa0, - 0x36, 0xd2, 0x6c, 0x87, 0x93, 0x1a, 0xc7, 0xfd, 0xaf, 0x01, 0xba, 0xec, 0xd5, 0xfd, 0x0d, 0x81, - 0x51, 0x7d, 0x4b, 0x6f, 0x57, 0x0b, 0xbf, 0x23, 0x78, 0x50, 0xbd, 0x95, 0xb7, 0xcb, 0xfa, 0xc7, - 0x63, 0xb0, 0x6a, 0x6b, 0x16, 0x3f, 0x82, 0x77, 0x6a, 0x61, 0xb1, 0x0a, 0x6c, 0x0d, 0x9f, 0x82, - 0x5d, 0x87, 0xe5, 0xad, 0xb4, 0x11, 0x7e, 0x08, 0x27, 0x77, 0xc8, 0x3c, 0xb4, 0x1b, 0xcf, 0x3f, - 0xf8, 0xf3, 0xa6, 0x83, 0x5e, 0xdf, 0x74, 0xd0, 0xdf, 0x37, 0x1d, 0xf4, 0xea, 0xb6, 0xa3, 0xbd, - 0xbe, 0xed, 0x68, 0x7f, 0xdd, 0x76, 0xb4, 0xef, 0xe1, 0x6c, 0xf7, 0x8b, 0x78, 0xd5, 0x56, 0x1f, - 0x9f, 0xfd, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x71, 0xf4, 0xb0, 0xb9, 0x25, 0x07, 0x00, 0x00, + // 812 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xc1, 0x8e, 0xe2, 0x46, + 0x10, 0xb5, 0xc1, 0xe0, 0x75, 0x79, 0x34, 0x90, 0xde, 0xd9, 0xc8, 0x71, 0x56, 0x08, 0xa1, 0x44, + 0x41, 0x59, 0xc9, 0x93, 0xb0, 0x59, 0x45, 0xca, 0x6d, 0x96, 0xcc, 0xc8, 0x28, 0x13, 0x40, 0x0d, + 0x4c, 0xa4, 0x5c, 0x56, 0x3d, 0xed, 0x5e, 0x40, 0x98, 0xb6, 0xe3, 0x36, 0x49, 0xf8, 0x8b, 0xbd, + 0xe6, 0x9a, 0x6f, 0xc8, 0x37, 0x44, 0x39, 0xee, 0x31, 0xc7, 0x68, 0x46, 0xf9, 0x8f, 0xa8, 0xdb, + 0x36, 0x18, 0x32, 0xe7, 0x39, 0x00, 0x5d, 0xaf, 0x5e, 0x55, 0xbf, 0x2a, 0x57, 0x19, 0x70, 0xc5, + 0x96, 0xd3, 0x38, 0x89, 0xd2, 0xe8, 0x3c, 0xfb, 0x96, 0xb6, 0xa7, 0x8e, 0xc8, 0x24, 0x7c, 0x9b, + 0x6e, 0x63, 0xe6, 0x7e, 0x11, 0xaf, 0xe6, 0xe7, 0x84, 0x86, 0xf2, 0x43, 0x17, 0x84, 0xcf, 0x99, + 0x90, 0xc7, 0xf8, 0x36, 0x8b, 0x11, 0x25, 0x3c, 0x0b, 0xed, 0x5c, 0x81, 0xf9, 0x3d, 0x13, 0x82, + 0xcc, 0x19, 0xfa, 0x0c, 0xea, 0x0b, 0x46, 0x02, 0x96, 0x38, 0x7a, 0x5b, 0xef, 0xda, 0xbd, 0x86, + 0x97, 0xa7, 0xf5, 0x7c, 0x05, 0xe3, 0xdc, 0x8d, 0x10, 0x18, 0x01, 0x49, 0x89, 0x53, 0x69, 0xeb, + 0xdd, 0x13, 0xac, 0xce, 0x9d, 0xdf, 0x75, 0xa8, 0x67, 0x34, 0xe4, 0x80, 0x99, 0x26, 0x84, 0xb2, + 0x41, 0xa0, 0x12, 0x9d, 0xe0, 0xc2, 0x44, 0xcf, 0xc1, 0x4a, 0xd8, 0x4f, 0x1b, 0x26, 0xd2, 0x41, + 0xa0, 0xa2, 0x0d, 0xbc, 0x07, 0x64, 0x5c, 0xc2, 0xe2, 0x70, 0x3b, 0x08, 0x9c, 0xaa, 0xf2, 0x15, + 0x26, 0xea, 0x82, 0x21, 0x75, 0x38, 0x46, 0x5b, 0xef, 0x9e, 0xf6, 0xce, 0x76, 0xba, 0x72, 0xe5, + 0xd3, 0x6d, 0xcc, 0xb0, 0x62, 0xc8, 0x1b, 0x02, 0x76, 0xbb, 0x99, 0x0f, 0xf8, 0xdb, 0xc8, 0xa9, + 0xb5, 0xf5, 0xae, 0x85, 0xf7, 0x40, 0xe7, 0x8f, 0x2a, 0xd4, 0x27, 0x5b, 0x91, 0xb2, 0x35, 0xfa, + 0x1a, 0xac, 0x05, 0xe1, 0x81, 0x58, 0x90, 0x15, 0xcb, 0xeb, 0xfd, 0x68, 0x97, 0x37, 0xe3, 0x78, + 0x7e, 0x41, 0xc0, 0x7b, 0xae, 0xd4, 0x12, 0x2f, 0xf9, 0x5c, 0xc9, 0xb7, 0x4b, 0x5a, 0xf2, 0x98, + 0xf1, 0x92, 0xcf, 0xb1, 0x62, 0xa0, 0x4f, 0xa1, 0x4a, 0xe8, 0x4a, 0xd5, 0x62, 0xf7, 0x9e, 0x1e, + 0x13, 0x2f, 0xe8, 0x0a, 0x4b, 0xbf, 0xfb, 0x0a, 0x2c, 0xbf, 0x94, 0xbd, 0xa1, 0x9e, 0x0b, 0x8d, + 0xc2, 0x1b, 0x96, 0x88, 0x65, 0xc4, 0x95, 0x38, 0x0b, 0x1f, 0xc3, 0x6e, 0x07, 0x0c, 0x79, 0x17, + 0x72, 0xe1, 0xc9, 0x86, 0x2f, 0x7f, 0x9d, 0x2e, 0xd7, 0x59, 0x1d, 0x06, 0xde, 0xd9, 0x6e, 0x0f, + 0xaa, 0x17, 0x74, 0x85, 0x5e, 0x40, 0x8d, 0x25, 0x49, 0x94, 0xe4, 0x9a, 0x9f, 0x1d, 0x4b, 0xb9, + 0x94, 0x4e, 0x9c, 0x71, 0xdc, 0x77, 0x3a, 0xd4, 0x14, 0x80, 0x3c, 0x30, 0x68, 0x14, 0x64, 0x59, + 0x4f, 0x7b, 0xee, 0x83, 0x51, 0x5e, 0x3f, 0x0a, 0x18, 0x56, 0x3c, 0xd4, 0x06, 0x3b, 0x60, 0x82, + 0x26, 0xcb, 0x38, 0x95, 0xba, 0x2b, 0x4a, 0x77, 0x19, 0xea, 0xbc, 0x02, 0x43, 0xf2, 0x91, 0x0d, + 0xe6, 0x6c, 0xf8, 0xdd, 0x70, 0xf4, 0xc3, 0xb0, 0xa9, 0xa1, 0x36, 0x3c, 0x9f, 0x0d, 0x27, 0xb3, + 0xf1, 0x78, 0x84, 0xa7, 0x97, 0xdf, 0xbe, 0x19, 0xe3, 0xd1, 0x74, 0xd4, 0x1f, 0x5d, 0xbf, 0xb9, + 0xb9, 0xc4, 0x93, 0xc1, 0x68, 0xd8, 0x84, 0xce, 0x9f, 0x75, 0x30, 0x26, 0x5b, 0x4e, 0xe5, 0x84, + 0x88, 0x78, 0x3f, 0x59, 0x16, 0x2e, 0x4c, 0xf4, 0x15, 0x98, 0xeb, 0x6c, 0x18, 0xf2, 0x22, 0xcb, + 0x72, 0x39, 0xf5, 0xfa, 0x11, 0x4f, 0x19, 0x4f, 0x6f, 0x48, 0xb8, 0x61, 0xb8, 0xa0, 0xa2, 0x17, + 0x00, 0x69, 0xc2, 0x58, 0x36, 0xb7, 0xf9, 0x83, 0xb2, 0x3d, 0x42, 0xc3, 0x62, 0xe2, 0x4b, 0x6e, + 0xf4, 0x21, 0xd4, 0xa5, 0x35, 0x08, 0xd4, 0x18, 0x5a, 0x38, 0xb7, 0xdc, 0xdf, 0x2a, 0x70, 0x52, + 0x4e, 0x8f, 0xbe, 0x01, 0x90, 0x8b, 0x32, 0x8b, 0x03, 0x92, 0x16, 0xb3, 0xe5, 0x1c, 0xca, 0xf1, + 0x77, 0x7e, 0x5f, 0xc3, 0x25, 0x36, 0xba, 0x82, 0xc6, 0xdb, 0x4d, 0x18, 0x4a, 0x12, 0xce, 0x16, + 0xe3, 0xe1, 0x7a, 0xae, 0x36, 0x61, 0xe8, 0xe5, 0x0c, 0x5f, 0xc3, 0xc7, 0x41, 0x68, 0x00, 0xcd, + 0x3d, 0x24, 0xe2, 0x88, 0x0b, 0x96, 0xd7, 0xf7, 0xf1, 0x83, 0x89, 0x32, 0x8a, 0xaf, 0xe1, 0xff, + 0x85, 0xa1, 0x2f, 0xc1, 0x24, 0x34, 0xbc, 0x5e, 0x8a, 0x54, 0x15, 0x7e, 0x38, 0x3f, 0x9c, 0x7a, + 0x17, 0xfd, 0x6b, 0xe9, 0xf4, 0x35, 0x5c, 0xf0, 0x5e, 0x9b, 0x50, 0xfb, 0x59, 0xb6, 0xc2, 0x7d, + 0x09, 0x66, 0xee, 0x46, 0x5d, 0xb9, 0xdd, 0x34, 0x4a, 0x02, 0xe1, 0xe8, 0xed, 0x6a, 0xd7, 0xee, + 0x9d, 0xaa, 0x46, 0x63, 0xf2, 0x0b, 0x56, 0x30, 0x2e, 0xdc, 0x6e, 0x0c, 0xb0, 0xef, 0x0f, 0x3a, + 0x83, 0x9a, 0xec, 0x4f, 0x16, 0x65, 0xe1, 0xcc, 0x90, 0xd9, 0xf2, 0xf7, 0x98, 0x53, 0x39, 0xcc, + 0xd6, 0x57, 0x30, 0x2e, 0xdc, 0xa8, 0x03, 0x27, 0x82, 0x93, 0x58, 0x2c, 0xa2, 0x74, 0x4c, 0xd2, + 0x85, 0x53, 0x55, 0x69, 0x0e, 0x30, 0xf7, 0x5f, 0x1d, 0x0c, 0xd9, 0x08, 0x77, 0x0d, 0x66, 0xd1, + 0xc1, 0xc7, 0xb8, 0x97, 0xc3, 0x93, 0x5d, 0x9b, 0x1f, 0xe1, 0xbe, 0xcf, 0x47, 0x60, 0x97, 0x5e, + 0x99, 0xe8, 0x19, 0x7c, 0x50, 0x32, 0xb3, 0xb5, 0x6e, 0x6a, 0xe8, 0x0c, 0x9a, 0x65, 0x58, 0x6e, + 0x58, 0x53, 0x47, 0x4f, 0xa1, 0x71, 0x40, 0xe6, 0xb4, 0x59, 0x79, 0xfd, 0xc9, 0x5f, 0x77, 0x2d, + 0xfd, 0xfd, 0x5d, 0x4b, 0xff, 0xe7, 0xae, 0xa5, 0xbf, 0xbb, 0x6f, 0x69, 0xef, 0xef, 0x5b, 0xda, + 0xdf, 0xf7, 0x2d, 0xed, 0x47, 0x38, 0xdf, 0xfd, 0x5f, 0xdd, 0xd6, 0xd5, 0xcf, 0xcb, 0xff, 0x02, + 0x00, 0x00, 0xff, 0xff, 0x6a, 0xb8, 0x16, 0xd3, 0xc3, 0x06, 0x00, 0x00, } func (m *Message) Marshal() (dAtA []byte, err error) { @@ -1239,6 +1265,25 @@ func (m *Sync) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.TreeId) > 0 { + i -= len(m.TreeId) + copy(dAtA[i:], m.TreeId) + i = encodeVarintSync(dAtA, i, uint64(len(m.TreeId))) + i-- + dAtA[i] = 0x22 + } + if m.TreeHeader != nil { + { + size, err := m.TreeHeader.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSync(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } if m.Message != nil { { size, err := m.Message.MarshalToSizedBuffer(dAtA[:i]) @@ -1356,6 +1401,64 @@ func (m *SyncContentValueValueOfFullSyncResponse) MarshalToSizedBuffer(dAtA []by } return len(dAtA) - i, nil } +func (m *SyncContentValueValueOfAclList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SyncContentValueValueOfAclList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.AclList != nil { + { + size, err := m.AclList.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSync(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + return len(dAtA) - i, nil +} +func (m *SyncACLList) 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 *SyncACLList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SyncACLList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Records) > 0 { + for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSync(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *SyncHeadUpdate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1376,34 +1479,15 @@ func (m *SyncHeadUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.TreeHeader != nil { - { - size, err := m.TreeHeader.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSync(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } if len(m.SnapshotPath) > 0 { for iNdEx := len(m.SnapshotPath) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SnapshotPath[iNdEx]) copy(dAtA[i:], m.SnapshotPath[iNdEx]) i = encodeVarintSync(dAtA, i, uint64(len(m.SnapshotPath[iNdEx]))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x1a } } - if len(m.TreeId) > 0 { - i -= len(m.TreeId) - copy(dAtA[i:], m.TreeId) - i = encodeVarintSync(dAtA, i, uint64(len(m.TreeId))) - i-- - dAtA[i] = 0x1a - } if len(m.Changes) > 0 { for iNdEx := len(m.Changes) - 1; iNdEx >= 0; iNdEx-- { { @@ -1473,34 +1557,15 @@ func (m *SyncFullRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.TreeHeader != nil { - { - size, err := m.TreeHeader.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSync(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } if len(m.SnapshotPath) > 0 { for iNdEx := len(m.SnapshotPath) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SnapshotPath[iNdEx]) copy(dAtA[i:], m.SnapshotPath[iNdEx]) i = encodeVarintSync(dAtA, i, uint64(len(m.SnapshotPath[iNdEx]))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x1a } } - if len(m.TreeId) > 0 { - i -= len(m.TreeId) - copy(dAtA[i:], m.TreeId) - i = encodeVarintSync(dAtA, i, uint64(len(m.TreeId))) - i-- - dAtA[i] = 0x1a - } if len(m.Changes) > 0 { for iNdEx := len(m.Changes) - 1; iNdEx >= 0; iNdEx-- { { @@ -1547,34 +1612,15 @@ func (m *SyncFullResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.TreeHeader != nil { - { - size, err := m.TreeHeader.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintSync(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } if len(m.SnapshotPath) > 0 { for iNdEx := len(m.SnapshotPath) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SnapshotPath[iNdEx]) copy(dAtA[i:], m.SnapshotPath[iNdEx]) i = encodeVarintSync(dAtA, i, uint64(len(m.SnapshotPath[iNdEx]))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x1a } } - if len(m.TreeId) > 0 { - i -= len(m.TreeId) - copy(dAtA[i:], m.TreeId) - i = encodeVarintSync(dAtA, i, uint64(len(m.TreeId))) - i-- - dAtA[i] = 0x1a - } if len(m.Changes) > 0 { for iNdEx := len(m.Changes) - 1; iNdEx >= 0; iNdEx-- { { @@ -1744,6 +1790,14 @@ func (m *Sync) Size() (n int) { l = m.Message.Size() n += 1 + l + sovSync(uint64(l)) } + if m.TreeHeader != nil { + l = m.TreeHeader.Size() + n += 1 + l + sovSync(uint64(l)) + } + l = len(m.TreeId) + if l > 0 { + n += 1 + l + sovSync(uint64(l)) + } return n } @@ -1795,6 +1849,33 @@ func (m *SyncContentValueValueOfFullSyncResponse) Size() (n int) { } return n } +func (m *SyncContentValueValueOfAclList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AclList != nil { + l = m.AclList.Size() + n += 1 + l + sovSync(uint64(l)) + } + return n +} +func (m *SyncACLList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Records) > 0 { + for _, e := range m.Records { + l = e.Size() + n += 1 + l + sovSync(uint64(l)) + } + } + return n +} + func (m *SyncHeadUpdate) Size() (n int) { if m == nil { return 0 @@ -1813,20 +1894,12 @@ func (m *SyncHeadUpdate) Size() (n int) { n += 1 + l + sovSync(uint64(l)) } } - l = len(m.TreeId) - if l > 0 { - n += 1 + l + sovSync(uint64(l)) - } if len(m.SnapshotPath) > 0 { for _, s := range m.SnapshotPath { l = len(s) n += 1 + l + sovSync(uint64(l)) } } - if m.TreeHeader != nil { - l = m.TreeHeader.Size() - n += 1 + l + sovSync(uint64(l)) - } return n } @@ -1857,20 +1930,12 @@ func (m *SyncFullRequest) Size() (n int) { n += 1 + l + sovSync(uint64(l)) } } - l = len(m.TreeId) - if l > 0 { - n += 1 + l + sovSync(uint64(l)) - } if len(m.SnapshotPath) > 0 { for _, s := range m.SnapshotPath { l = len(s) n += 1 + l + sovSync(uint64(l)) } } - if m.TreeHeader != nil { - l = m.TreeHeader.Size() - n += 1 + l + sovSync(uint64(l)) - } return n } @@ -1892,20 +1957,12 @@ func (m *SyncFullResponse) Size() (n int) { n += 1 + l + sovSync(uint64(l)) } } - l = len(m.TreeId) - if l > 0 { - n += 1 + l + sovSync(uint64(l)) - } if len(m.SnapshotPath) > 0 { for _, s := range m.SnapshotPath { l = len(s) n += 1 + l + sovSync(uint64(l)) } } - if m.TreeHeader != nil { - l = m.TreeHeader.Size() - n += 1 + l + sovSync(uint64(l)) - } return n } @@ -2801,6 +2858,74 @@ func (m *Sync) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TreeHeader", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSync + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSync + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSync + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TreeHeader == nil { + m.TreeHeader = &aclpb.Header{} + } + if err := m.TreeHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TreeId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSync + } + 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 ErrInvalidLengthSync + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSync + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TreeId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSync(dAtA[iNdEx:]) @@ -2956,6 +3081,125 @@ func (m *SyncContentValue) Unmarshal(dAtA []byte) error { } m.Value = &SyncContentValueValueOfFullSyncResponse{v} iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclList", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSync + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSync + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSync + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SyncACLList{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &SyncContentValueValueOfAclList{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSync(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSync + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SyncACLList) 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 ErrIntOverflowSync + } + 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: ACLList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ACLList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSync + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSync + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSync + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Records = append(m.Records, &aclpb.RawRecord{}) + if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSync(dAtA[iNdEx:]) @@ -3073,38 +3317,6 @@ func (m *SyncHeadUpdate) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TreeId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSync - } - 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 ErrInvalidLengthSync - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSync - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TreeId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SnapshotPath", wireType) } @@ -3136,42 +3348,6 @@ func (m *SyncHeadUpdate) Unmarshal(dAtA []byte) error { } m.SnapshotPath = append(m.SnapshotPath, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TreeHeader", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSync - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSync - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSync - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.TreeHeader == nil { - m.TreeHeader = &treepb.TreeHeader{} - } - if err := m.TreeHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSync(dAtA[iNdEx:]) @@ -3339,38 +3515,6 @@ func (m *SyncFullRequest) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TreeId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSync - } - 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 ErrInvalidLengthSync - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSync - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TreeId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SnapshotPath", wireType) } @@ -3402,42 +3546,6 @@ func (m *SyncFullRequest) Unmarshal(dAtA []byte) error { } m.SnapshotPath = append(m.SnapshotPath, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TreeHeader", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSync - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSync - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSync - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.TreeHeader == nil { - m.TreeHeader = &treepb.TreeHeader{} - } - if err := m.TreeHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSync(dAtA[iNdEx:]) @@ -3555,38 +3663,6 @@ func (m *SyncFullResponse) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TreeId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSync - } - 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 ErrInvalidLengthSync - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthSync - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TreeId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SnapshotPath", wireType) } @@ -3618,42 +3694,6 @@ func (m *SyncFullResponse) Unmarshal(dAtA []byte) error { } m.SnapshotPath = append(m.SnapshotPath, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TreeHeader", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowSync - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthSync - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthSync - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.TreeHeader == nil { - m.TreeHeader = &treepb.TreeHeader{} - } - if err := m.TreeHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSync(dAtA[iNdEx:])