Start refactoring tree package

This commit is contained in:
mcrakhman 2022-08-09 21:48:14 +02:00 committed by Mikhail Iudin
parent dfa19d2aaf
commit 307f517ee5
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
7 changed files with 1807 additions and 61 deletions

View File

@ -1023,6 +1023,106 @@ func (m *ACLChangeUserPermissionChange) GetPermissions() ACLChangeUserPermission
return ACLChange_Admin
}
type Change struct {
TreeHeadIds []string `protobuf:"bytes,1,rep,name=treeHeadIds,proto3" json:"treeHeadIds,omitempty"`
AclHeadIds []string `protobuf:"bytes,2,rep,name=aclHeadIds,proto3" json:"aclHeadIds,omitempty"`
SnapshotBaseId string `protobuf:"bytes,3,opt,name=snapshotBaseId,proto3" json:"snapshotBaseId,omitempty"`
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{2}
}
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) GetAclHeadIds() []string {
if m != nil {
return m.AclHeadIds
}
return nil
}
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
}
func init() {
proto.RegisterEnum("acl.ACLChangeUserPermissions", ACLChangeUserPermissions_name, ACLChangeUserPermissions_value)
proto.RegisterType((*RawChange)(nil), "acl.RawChange")
@ -1040,6 +1140,7 @@ 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")
}
func init() {
@ -1047,67 +1148,69 @@ 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,
// 987 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4f, 0x8f, 0xdb, 0x44,
0x14, 0xcf, 0x24, 0x9b, 0x75, 0xfc, 0x1c, 0x76, 0xc3, 0x50, 0x51, 0x63, 0x95, 0x10, 0x85, 0x0a,
0x45, 0x08, 0x65, 0xab, 0x54, 0x48, 0x15, 0xa0, 0x15, 0xbb, 0x0b, 0x6a, 0xc2, 0x72, 0xa8, 0x66,
0x55, 0x10, 0xdc, 0x66, 0xed, 0x61, 0xd7, 0xaa, 0x63, 0x1b, 0xcf, 0x64, 0x51, 0x2e, 0x48, 0x9c,
0xb8, 0x72, 0xe6, 0xc0, 0x17, 0xe1, 0x0b, 0xf4, 0xd8, 0x23, 0x37, 0xd0, 0xee, 0x9d, 0x0b, 0x5f,
0x00, 0xcd, 0x1f, 0x3b, 0x8e, 0xe3, 0x46, 0x45, 0xaa, 0x2a, 0x71, 0x88, 0x34, 0xf3, 0x7b, 0xbf,
0x37, 0x79, 0xef, 0xfd, 0xde, 0x3c, 0x0f, 0xdc, 0x4b, 0x9f, 0x5c, 0x1c, 0x50, 0x3f, 0x92, 0x3f,
0xff, 0x92, 0xc6, 0x17, 0x8c, 0xcb, 0x65, 0x7a, 0x7e, 0x90, 0x66, 0x89, 0x48, 0x78, 0x09, 0x1f,
0x2b, 0x04, 0xb7, 0xa8, 0x1f, 0x0d, 0xcf, 0xc0, 0x26, 0xf4, 0x87, 0x13, 0x65, 0xc0, 0x2e, 0x58,
0x29, 0x5d, 0x46, 0x09, 0x0d, 0x5c, 0x34, 0x40, 0xa3, 0x2e, 0xc9, 0xb7, 0xf8, 0x0e, 0xd8, 0x3c,
0xbc, 0x88, 0xa9, 0x58, 0x64, 0xcc, 0x6d, 0x2a, 0xdb, 0x0a, 0xc0, 0x7b, 0xd0, 0x0c, 0x03, 0xb7,
0x35, 0x40, 0x23, 0x9b, 0x34, 0xc3, 0x60, 0xf8, 0xcf, 0x1b, 0x60, 0x1f, 0x9d, 0x7c, 0x69, 0x4e,
0x1d, 0x80, 0x23, 0x32, 0xc6, 0xa6, 0x8c, 0x06, 0xb3, 0x80, 0xbb, 0x68, 0xd0, 0x1a, 0xd9, 0xa4,
0x0c, 0xe1, 0x3e, 0x00, 0xf5, 0xa3, 0x9c, 0xd0, 0x54, 0x84, 0x12, 0x82, 0xdf, 0x83, 0x3d, 0x1e,
0xd3, 0x94, 0x5f, 0x26, 0xe2, 0x98, 0x72, 0x36, 0xcb, 0xff, 0xab, 0x82, 0xe2, 0x7b, 0x60, 0x51,
0x3f, 0xfa, 0x8c, 0x0a, 0xea, 0xee, 0x0c, 0xd0, 0xc8, 0x99, 0xbc, 0x39, 0xa6, 0x7e, 0x34, 0x2e,
0x42, 0x91, 0x2b, 0x69, 0x25, 0x39, 0x4d, 0xc6, 0x66, 0x8a, 0xa2, 0xbc, 0xda, 0x2a, 0xb3, 0x32,
0x84, 0xc7, 0x80, 0xfd, 0x45, 0x96, 0xb1, 0x58, 0x10, 0x46, 0x83, 0x53, 0xb6, 0x9c, 0x52, 0x7e,
0xe9, 0xee, 0x0e, 0xd0, 0x68, 0x87, 0xd4, 0x58, 0x64, 0xa5, 0x44, 0x38, 0x67, 0x5c, 0xd0, 0x79,
0xea, 0x5a, 0x03, 0x34, 0x6a, 0x91, 0x15, 0x80, 0x3d, 0xe8, 0x84, 0x01, 0x8b, 0x45, 0x28, 0x96,
0x6e, 0x47, 0xe5, 0x50, 0xec, 0xbd, 0x5f, 0x5b, 0xb0, 0x2f, 0x43, 0x4d, 0x62, 0xc1, 0x62, 0xf1,
0x15, 0x8d, 0x16, 0x0c, 0x4f, 0xc0, 0x5a, 0x70, 0x96, 0x1d, 0x05, 0x5a, 0x91, 0xcd, 0x8c, 0x1e,
0x6b, 0xeb, 0xb4, 0x41, 0x72, 0x22, 0xfe, 0x18, 0x40, 0x2e, 0x09, 0x9b, 0x27, 0x57, 0x5a, 0x2c,
0x67, 0xf2, 0x56, 0x8d, 0x9b, 0x26, 0x4c, 0x1b, 0xa4, 0x44, 0xc7, 0xdf, 0xc0, 0x2d, 0xb9, 0x7b,
0xc4, 0xb2, 0x79, 0xc8, 0x79, 0x98, 0xc4, 0xda, 0x41, 0x15, 0xdc, 0x99, 0xbc, 0x5b, 0x73, 0x4c,
0x95, 0x3a, 0x6d, 0x90, 0xda, 0x23, 0xf2, 0xb8, 0x66, 0xf1, 0x55, 0x28, 0x98, 0x11, 0xa8, 0x2e,
0x2e, 0x4d, 0xc8, 0xe3, 0xd2, 0x3b, 0xfc, 0x21, 0x74, 0xe4, 0xee, 0x8b, 0x24, 0x8c, 0x95, 0x4a,
0xce, 0xe4, 0x76, 0x8d, 0xab, 0x34, 0x4f, 0x1b, 0xa4, 0xa0, 0xe2, 0x43, 0x70, 0xe4, 0xfa, 0x24,
0x89, 0xbf, 0x0b, 0xb3, 0xb9, 0x92, 0xcd, 0x99, 0x78, 0x35, 0x9e, 0x86, 0x31, 0x6d, 0x90, 0xb2,
0xc3, 0xb1, 0x05, 0xed, 0x2b, 0x29, 0x84, 0xf7, 0x33, 0x02, 0xcb, 0x74, 0x0f, 0xfe, 0x04, 0x1c,
0xea, 0x47, 0x67, 0xa6, 0xf7, 0x8c, 0x30, 0xde, 0x66, 0xab, 0xe5, 0x0c, 0x52, 0xa6, 0xe3, 0x43,
0xd5, 0xec, 0x46, 0x65, 0xd5, 0xec, 0xce, 0xa4, 0xbf, 0xe9, 0x5c, 0x6e, 0x03, 0x52, 0xf2, 0xf0,
0x8e, 0xc1, 0x29, 0x9d, 0x8d, 0xef, 0x43, 0x47, 0x9e, 0x2e, 0xa8, 0x60, 0x26, 0x92, 0xdb, 0x35,
0x91, 0x48, 0x33, 0x29, 0x88, 0xde, 0x4f, 0x4d, 0xe8, 0xe4, 0x30, 0xbe, 0x0b, 0xaf, 0x65, 0xab,
0x06, 0x66, 0xfa, 0x86, 0xee, 0x90, 0x75, 0x10, 0x3f, 0xd0, 0xea, 0x29, 0x17, 0x6e, 0xc2, 0x76,
0x6b, 0x0a, 0xa9, 0xff, 0xaa, 0xc4, 0xc5, 0x87, 0x60, 0x85, 0x4a, 0x44, 0xee, 0xb6, 0x94, 0xdb,
0xdd, 0xe7, 0x04, 0x38, 0xd6, 0x5a, 0xf3, 0xcf, 0x63, 0x91, 0x2d, 0x49, 0xee, 0xe4, 0x3d, 0x86,
0x6e, 0xd9, 0x80, 0x7b, 0xd0, 0x7a, 0xc2, 0x96, 0x2a, 0x59, 0x9b, 0xc8, 0x25, 0x3e, 0x30, 0x2a,
0x6d, 0x69, 0x76, 0x7d, 0x02, 0xd1, 0xbc, 0x8f, 0x9a, 0x0f, 0x90, 0xf7, 0x27, 0x02, 0xbb, 0x08,
0x78, 0xed, 0x62, 0xa2, 0xf5, 0x8b, 0x29, 0x0b, 0xc4, 0x62, 0x3f, 0x5b, 0xa6, 0x22, 0x4c, 0xe2,
0x53, 0xb6, 0x34, 0x03, 0x70, 0x1d, 0xc4, 0x1f, 0xc0, 0xeb, 0x06, 0x60, 0x81, 0x19, 0x08, 0x3a,
0xe1, 0x2e, 0xd9, 0x34, 0xe0, 0x4f, 0xc1, 0x49, 0x8b, 0x0b, 0xc2, 0xd5, 0x6d, 0xd8, 0xdb, 0x68,
0x83, 0xf5, 0xeb, 0xc5, 0x49, 0xd9, 0x45, 0x8e, 0xae, 0x19, 0x37, 0x7d, 0xca, 0x02, 0x75, 0x29,
0x3a, 0xa4, 0x0c, 0x79, 0xbf, 0x23, 0xb0, 0xcc, 0x7c, 0xf8, 0xff, 0xe5, 0xe7, 0x3d, 0x04, 0xa7,
0x74, 0x31, 0xb7, 0x26, 0x70, 0x07, 0x6c, 0x33, 0xfc, 0x66, 0x81, 0x0a, 0xde, 0x26, 0x2b, 0xc0,
0xfb, 0x1b, 0x01, 0xac, 0x5a, 0x00, 0x8f, 0x60, 0x9f, 0xfa, 0x3e, 0x4b, 0xc5, 0xa3, 0xc5, 0x79,
0x14, 0xfa, 0xa7, 0xa6, 0x95, 0xba, 0xa4, 0x0a, 0xe3, 0xf7, 0xa1, 0x67, 0x12, 0x5b, 0x51, 0x75,
0x69, 0x36, 0xf0, 0x57, 0xae, 0xbe, 0x07, 0x1d, 0x9d, 0xcf, 0x4c, 0x4b, 0x6f, 0x93, 0x62, 0xef,
0x3d, 0x45, 0xd0, 0xc9, 0xa7, 0xe1, 0x4b, 0x10, 0xbe, 0x28, 0xd8, 0x59, 0xf1, 0x02, 0x68, 0x95,
0x0b, 0x56, 0xc0, 0x78, 0x08, 0xdd, 0xd5, 0xc8, 0x9e, 0x05, 0x2a, 0x2f, 0x9b, 0xac, 0x61, 0xf5,
0x85, 0x6a, 0x3f, 0xa7, 0x50, 0xde, 0xf7, 0x5a, 0x3a, 0xf3, 0x71, 0xda, 0x96, 0xcb, 0x43, 0xd8,
0x37, 0x03, 0x8b, 0xb0, 0x34, 0xa2, 0x7e, 0x31, 0x6d, 0xde, 0xae, 0x94, 0x95, 0xac, 0xb1, 0x48,
0xd5, 0xcb, 0xfb, 0x11, 0xf6, 0xd6, 0x29, 0x2f, 0xa1, 0x84, 0xab, 0x4e, 0x2a, 0x72, 0x33, 0x35,
0xdc, 0xc0, 0x3d, 0x01, 0xb7, 0xea, 0x3e, 0xab, 0x5b, 0xa3, 0xa8, 0xf4, 0x53, 0xf3, 0x3f, 0xf7,
0xd3, 0xf0, 0x08, 0xf6, 0x2b, 0x76, 0x6c, 0x43, 0xfb, 0x28, 0x98, 0x87, 0x71, 0xaf, 0x81, 0x01,
0x76, 0xbf, 0xce, 0x42, 0xc1, 0xb2, 0x1e, 0x92, 0x6b, 0x19, 0x2a, 0xcb, 0x7a, 0x4d, 0xec, 0x80,
0xa5, 0xa5, 0x09, 0x7a, 0xad, 0xe1, 0x6f, 0x4d, 0xd8, 0x7d, 0xe5, 0x4f, 0xbe, 0xca, 0x03, 0x6e,
0xe7, 0x45, 0x1f, 0x70, 0xed, 0x17, 0x7b, 0xc0, 0xed, 0x6e, 0x7b, 0xc0, 0x59, 0x15, 0x15, 0xfa,
0x00, 0x21, 0x2f, 0x9e, 0x05, 0x1d, 0x35, 0x90, 0x4b, 0xc8, 0xf1, 0x3b, 0x4f, 0xaf, 0xfb, 0xe8,
0xd9, 0x75, 0x1f, 0xfd, 0x75, 0xdd, 0x47, 0xbf, 0xdc, 0xf4, 0x1b, 0xcf, 0x6e, 0xfa, 0x8d, 0x3f,
0x6e, 0xfa, 0x8d, 0x6f, 0xdb, 0xea, 0xa5, 0x7e, 0xbe, 0xab, 0x1e, 0xe6, 0xf7, 0xff, 0x0d, 0x00,
0x00, 0xff, 0xff, 0xf9, 0xfe, 0x4f, 0x12, 0xcc, 0x0b, 0x00, 0x00,
}
func (m *RawChange) Marshal() (dAtA []byte, err error) {
@ -1951,6 +2054,88 @@ 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.AclHeadIds) > 0 {
for iNdEx := len(m.AclHeadIds) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.AclHeadIds[iNdEx])
copy(dAtA[i:], m.AclHeadIds[iNdEx])
i = encodeVarintAclchanges(dAtA, i, uint64(len(m.AclHeadIds[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if len(m.TreeHeadIds) > 0 {
for iNdEx := len(m.TreeHeadIds) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.TreeHeadIds[iNdEx])
copy(dAtA[i:], m.TreeHeadIds[iNdEx])
i = encodeVarintAclchanges(dAtA, i, uint64(len(m.TreeHeadIds[iNdEx])))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func encodeVarintAclchanges(dAtA []byte, offset int, v uint64) int {
offset -= sovAclchanges(v)
base := offset
@ -2366,6 +2551,48 @@ 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))
}
}
if len(m.AclHeadIds) > 0 {
for _, s := range m.AclHeadIds {
l = len(s)
n += 1 + l + sovAclchanges(uint64(l))
}
}
l = len(m.SnapshotBaseId)
if l > 0 {
n += 1 + l + sovAclchanges(uint64(l))
}
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 sovAclchanges(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
@ -4813,6 +5040,276 @@ 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 AclHeadIds", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAclchanges
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthAclchanges
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthAclchanges
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.AclHeadIds = append(m.AclHeadIds, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SnapshotBaseId", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAclchanges
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthAclchanges
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthAclchanges
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SnapshotBaseId = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field 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 skipAclchanges(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0

View File

@ -109,3 +109,14 @@ message ACLChange {
Removed = 3;
}
}
message Change {
repeated string treeHeadIds = 1;
repeated string aclHeadIds = 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;
}

View File

@ -0,0 +1,8 @@
package acltree
type DocTree interface {
}
type docTree struct {
tree *Tree
}

367
pkg/acl/tree/aclstate.go Normal file
View File

@ -0,0 +1,367 @@
package tree
import (
"bytes"
"errors"
"fmt"
"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"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
"github.com/gogo/protobuf/proto"
"hash/fnv"
)
var ErrNoSuchUser = errors.New("no such user")
var ErrFailedToDecrypt = errors.New("failed to decrypt key")
var ErrUserRemoved = errors.New("user was removed from the document")
var ErrDocumentForbidden = errors.New("your user was forbidden access to the document")
var ErrUserAlreadyExists = errors.New("user already exists")
type ACLState struct {
currentReadKeyHash uint64
userReadKeys map[uint64]*symmetric.Key
userStates map[string]*aclpb.ACLChangeUserState
userInvites map[string]*aclpb.ACLChangeUserInvite
signingPubKeyDecoder signingkey.PubKeyDecoder
encryptionKey encryptionkey.PrivKey
identity string
}
func newACLStateWithIdentity(
identity string,
encryptionKey encryptionkey.PrivKey,
signingPubKeyDecoder signingkey.PubKeyDecoder) *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,
}
}
func newACLState() *ACLState {
return &ACLState{
userReadKeys: make(map[uint64]*symmetric.Key),
userStates: make(map[string]*aclpb.ACLChangeUserState),
userInvites: make(map[string]*aclpb.ACLChangeUserInvite),
}
}
func (st *ACLState) applyChange(change *aclpb.Change) (err error) {
aclData := &aclpb.ACLChangeACLData{}
err = proto.Unmarshal(change.ChangesData, aclData)
if err != nil {
return
}
defer func() {
if err != nil {
return
}
st.currentReadKeyHash = change.CurrentReadKeyHash
}()
// we can't check this for the user which is joining, because it will not be in our list
// the same is for the first change to be added
skipIdentityCheck := st.isUserJoin(aclData) || (st.currentReadKeyHash == 0 && st.isUserAdd(aclData, change.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 {
err = ErrNoSuchUser
return
}
if !st.hasPermission(change.Identity, aclpb.ACLChange_Admin) {
err = fmt.Errorf("user %s must have admin permissions", change.Identity)
return
}
}
for _, ch := range aclData.GetAclContent() {
if err = st.applyChangeContent(ch); err != nil {
log.Infof("error while applying changes: %v; ignore", err)
return err
}
}
return nil
}
func (st *ACLState) applyChangeContent(ch *aclpb.ACLChangeACLContentValue) error {
switch {
case ch.GetUserPermissionChange() != nil:
return st.applyUserPermissionChange(ch.GetUserPermissionChange())
case ch.GetUserAdd() != nil:
return st.applyUserAdd(ch.GetUserAdd())
case ch.GetUserRemove() != nil:
return st.applyUserRemove(ch.GetUserRemove())
case ch.GetUserInvite() != nil:
return st.applyUserInvite(ch.GetUserInvite())
case ch.GetUserJoin() != nil:
return st.applyUserJoin(ch.GetUserJoin())
case ch.GetUserConfirm() != nil:
return st.applyUserConfirm(ch.GetUserConfirm())
default:
return fmt.Errorf("unexpected change type: %v", ch)
}
}
func (st *ACLState) applyUserPermissionChange(ch *aclpb.ACLChangeUserPermissionChange) error {
if _, exists := st.userStates[ch.Identity]; !exists {
return ErrNoSuchUser
}
st.userStates[ch.Identity].Permissions = ch.Permissions
return nil
}
func (st *ACLState) applyUserInvite(ch *aclpb.ACLChangeUserInvite) error {
st.userInvites[ch.InviteId] = ch
return nil
}
func (st *ACLState) applyUserJoin(ch *aclpb.ACLChangeUserJoin) error {
invite, exists := st.userInvites[ch.UserInviteId]
if !exists {
return fmt.Errorf("no such invite with id %s", ch.UserInviteId)
}
if _, exists = st.userStates[ch.Identity]; exists {
return ErrUserAlreadyExists
}
// validating signature
signature := ch.GetAcceptSignature()
verificationKey, err := st.signingPubKeyDecoder.DecodeFromBytes(invite.AcceptPublicKey)
if err != nil {
return fmt.Errorf("public key verifying invite accepts is given in incorrect format: %v", err)
}
rawSignedId, err := st.signingPubKeyDecoder.DecodeFromStringIntoBytes(ch.Identity)
if err != nil {
return fmt.Errorf("failed to decode signing identity as bytes")
}
res, err := verificationKey.Verify(rawSignedId, signature)
if err != nil {
return fmt.Errorf("verification returned error: %w", err)
}
if !res {
return fmt.Errorf("signature is invalid")
}
// if ourselves -> we need to decrypt the read keys
if st.identity == ch.Identity {
for _, key := range ch.EncryptedReadKeys {
key, hash, err := st.decryptReadKeyAndHash(key)
if err != nil {
return ErrFailedToDecrypt
}
st.userReadKeys[hash] = key
}
}
// adding user to the list
userState := &aclpb.ACLChangeUserState{
Identity: ch.Identity,
EncryptionKey: ch.EncryptionKey,
EncryptedReadKeys: ch.EncryptedReadKeys,
Permissions: invite.Permissions,
IsConfirmed: true,
}
st.userStates[ch.Identity] = userState
return nil
}
func (st *ACLState) applyUserAdd(ch *aclpb.ACLChangeUserAdd) error {
if _, exists := st.userStates[ch.Identity]; exists {
return ErrUserAlreadyExists
}
st.userStates[ch.Identity] = &aclpb.ACLChangeUserState{
Identity: ch.Identity,
EncryptionKey: ch.EncryptionKey,
Permissions: ch.Permissions,
EncryptedReadKeys: ch.EncryptedReadKeys,
}
if ch.Identity == st.identity {
for _, key := range ch.EncryptedReadKeys {
key, hash, err := st.decryptReadKeyAndHash(key)
if err != nil {
return ErrFailedToDecrypt
}
st.userReadKeys[hash] = key
}
}
return nil
}
func (st *ACLState) applyUserRemove(ch *aclpb.ACLChangeUserRemove) error {
if ch.Identity == st.identity {
return ErrDocumentForbidden
}
if _, exists := st.userStates[ch.Identity]; !exists {
return ErrNoSuchUser
}
delete(st.userStates, ch.Identity)
for _, replace := range ch.ReadKeyReplaces {
userState, exists := st.userStates[replace.Identity]
if !exists {
continue
}
userState.EncryptedReadKeys = append(userState.EncryptedReadKeys, replace.EncryptedReadKey)
// if this is our identity then we have to decrypt the key
if replace.Identity == st.identity {
key, hash, err := st.decryptReadKeyAndHash(replace.EncryptedReadKey)
if err != nil {
return ErrFailedToDecrypt
}
st.currentReadKeyHash = hash
st.userReadKeys[st.currentReadKeyHash] = key
}
}
return nil
}
func (st *ACLState) applyUserConfirm(ch *aclpb.ACLChangeUserConfirm) error {
if _, exists := st.userStates[ch.Identity]; !exists {
return ErrNoSuchUser
}
userState := st.userStates[ch.Identity]
userState.IsConfirmed = true
return nil
}
func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) {
decrypted, err := st.encryptionKey.Decrypt(msg)
if err != nil {
return nil, 0, ErrFailedToDecrypt
}
key, err := symmetric.FromBytes(decrypted)
if err != nil {
return nil, 0, ErrFailedToDecrypt
}
hasher := fnv.New64()
hasher.Write(decrypted)
return key, hasher.Sum64(), nil
}
func (st *ACLState) hasPermission(identity string, permission aclpb.ACLChangeUserPermissions) bool {
state, exists := st.userStates[identity]
if !exists {
return false
}
return state.Permissions == permission
}
func (st *ACLState) isUserJoin(data *aclpb.ACLChangeACLData) bool {
// if we have a UserJoin, then it should always be the first one applied
return data.GetAclContent() != nil && data.GetAclContent()[0].GetUserJoin() != nil
}
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 := data.GetAclContent()[0].GetUserAdd()
return data.GetAclContent() != nil && userAdd != nil && userAdd.GetIdentity() == identity
}
func (st *ACLState) getPermissionDecreasedUsers(ch *aclpb.ACLChange) (identities []*aclpb.ACLChangeUserPermissionChange) {
// this should be called after general checks are completed
if ch.GetAclData().GetAclContent() == nil {
return nil
}
contents := ch.GetAclData().GetAclContent()
for _, c := range contents {
if c.GetUserPermissionChange() != nil {
content := c.GetUserPermissionChange()
currentState := st.userStates[content.Identity]
// the comparison works in different direction :-)
if content.Permissions > currentState.Permissions {
identities = append(identities, &aclpb.ACLChangeUserPermissionChange{
Identity: content.Identity,
Permissions: content.Permissions,
})
}
}
if c.GetUserRemove() != nil {
content := c.GetUserRemove()
identities = append(identities, &aclpb.ACLChangeUserPermissionChange{
Identity: content.Identity,
Permissions: aclpb.ACLChange_Removed,
})
}
}
return identities
}
func (st *ACLState) equal(other *ACLState) bool {
if st == nil && other == nil {
return true
}
if st == nil || other == nil {
return false
}
if st.currentReadKeyHash != other.currentReadKeyHash {
return false
}
if st.identity != other.identity {
return false
}
if len(st.userStates) != len(other.userStates) {
return false
}
for _, st := range st.userStates {
otherSt, exists := other.userStates[st.Identity]
if !exists {
return false
}
if st.Permissions != otherSt.Permissions {
return false
}
if bytes.Compare(st.EncryptionKey, otherSt.EncryptionKey) != 0 {
return false
}
}
if len(st.userInvites) != len(other.userInvites) {
return false
}
// TODO: add detailed user invites comparison + compare other stuff
return true
}
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 == ""
}

82
pkg/acl/tree/change.go Normal file
View File

@ -0,0 +1,82 @@
package tree
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
DecryptedChange []byte
Content *aclpb.Change
Sign []byte
}
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 NewFromRawChange(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)
ch.Sign = rawChange.Signature
return ch, nil
}
func NewChange(id string, ch *aclpb.Change) *Change {
return &Change{
Next: nil,
PreviousIds: ch.TreeHeadIds,
Id: id,
Content: ch,
SnapshotId: ch.SnapshotBaseId,
IsSnapshot: ch.IsSnapshot,
}
}
func (ch *Change) ProtoChange() *aclpb.Change {
return ch.Content
}
func (ch *Change) DecryptedChangeContent() []byte {
return ch.DecryptedChange
}
func (ch *Change) Signature() []byte {
return ch.Sign
}
func (ch *Change) CID() string {
return ch.Id
}

416
pkg/acl/tree/tree.go Normal file
View File

@ -0,0 +1,416 @@
package tree
import (
"bytes"
"crypto/md5"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
"sort"
)
type Mode int
const (
Append Mode = iota
Rebuild
Nothing
)
// TODO: consider abstracting into separate package with iterator, remove
type Tree struct {
root *Change
headIds []string
metaHeadIds []string
attached map[string]*Change
unAttached map[string]*Change
// missed id -> list of dependency ids
waitList map[string][]string
invalidChanges map[string]struct{}
// bufs
iterCompBuf []*Change
iterQueue []*Change
duplicateEvents int
}
func (t *Tree) GetUnattachedChanges(changes ...*Change) []*Change {
return nil
}
func (t *Tree) RootId() string {
if t.root != nil {
return t.root.Id
}
return ""
}
func (t *Tree) Root() *Change {
return t.root
}
func (t *Tree) AddFast(changes ...*Change) {
for _, c := range changes {
// ignore existing
if _, ok := t.attached[c.Id]; ok {
continue
} else if _, ok := t.unAttached[c.Id]; ok {
continue
}
t.add(c)
}
t.updateHeads()
}
func (t *Tree) Add(changes ...*Change) (mode Mode) {
var beforeHeadIds = t.headIds
var attached bool
var empty = t.Len() == 0
for _, c := range changes {
// ignore existing
if _, ok := t.attached[c.Id]; ok {
continue
} else if _, ok := t.unAttached[c.Id]; ok {
continue
}
if t.add(c) {
attached = true
}
}
if !attached {
return Nothing
}
t.updateHeads()
if empty {
return Rebuild
}
for _, hid := range beforeHeadIds {
for _, newCh := range changes {
if _, ok := t.attached[newCh.Id]; ok {
if !t.after(newCh.Id, hid) {
return Rebuild
}
}
}
}
return Append
}
func (t *Tree) RemoveInvalidChange(id string) {
stack := []string{id}
// removing all children of this id (either next or unattached)
for len(stack) > 0 {
var exists bool
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if _, exists = t.invalidChanges[top]; exists {
continue
}
var rem *Change
t.invalidChanges[top] = struct{}{}
if rem, exists = t.unAttached[top]; exists {
delete(t.unAttached, top)
} else if rem, exists = t.attached[top]; exists {
// remove from all prev changes
for _, id := range rem.PreviousIds {
prev, exists := t.attached[id]
if !exists {
continue
}
for i, next := range prev.Next {
if next.Id == top {
prev.Next[i] = nil
prev.Next = append(prev.Next[:i], prev.Next[i+1:]...)
break
}
}
}
delete(t.attached, top)
}
for _, el := range rem.Unattached {
stack = append(stack, el.Id)
}
for _, el := range rem.Next {
stack = append(stack, el.Id)
}
}
t.updateHeads()
}
func (t *Tree) add(c *Change) (attached bool) {
if c == nil {
return false
}
if _, exists := t.invalidChanges[c.Id]; exists {
return false
}
if t.root == nil { // first element
t.root = c
t.attached = map[string]*Change{
c.Id: c,
}
t.unAttached = make(map[string]*Change)
t.waitList = make(map[string][]string)
t.invalidChanges = make(map[string]struct{})
return true
}
if len(c.PreviousIds) > 1 {
sort.Strings(c.PreviousIds)
}
// attaching only if all prev ids are attached
attached = true
for _, pid := range c.PreviousIds {
if prev, ok := t.attached[pid]; ok {
prev.Unattached = append(prev.Unattached, c)
continue
}
attached = false
if prev, ok := t.unAttached[pid]; ok {
prev.Unattached = append(prev.Unattached, c)
continue
}
wl := t.waitList[pid]
wl = append(wl, c.Id)
t.waitList[pid] = wl
}
if attached {
t.attach(c, true)
} else {
// clearing wait list
for _, wid := range t.waitList[c.Id] {
c.Unattached = append(c.Unattached, t.unAttached[wid])
}
delete(t.waitList, c.Id)
t.unAttached[c.Id] = c
}
return
}
func (t *Tree) canAttach(c *Change) (attach bool) {
if c == nil {
return false
}
attach = true
for _, id := range c.PreviousIds {
if _, exists := t.attached[id]; !exists {
attach = false
}
}
return
}
func (t *Tree) attach(c *Change, newEl bool) {
t.attached[c.Id] = c
if !newEl {
delete(t.unAttached, c.Id)
}
// add next to all prev changes
for _, id := range c.PreviousIds {
// prev id must be attached if we attach this id
prev := t.attached[id]
prev.Next = append(prev.Next, c)
if len(prev.Next) > 1 {
sort.Sort(sortChanges(prev.Next))
}
for i, next := range prev.Unattached {
if next.Id == c.Id {
prev.Unattached[i] = nil
prev.Unattached = append(prev.Unattached[:i], prev.Unattached[i+1:]...)
break
}
}
}
// clearing wait list
if waitIds, ok := t.waitList[c.Id]; ok {
for _, wid := range waitIds {
next := t.unAttached[wid]
if t.canAttach(next) {
t.attach(next, false)
}
}
delete(t.waitList, c.Id)
}
for _, next := range c.Unattached {
if t.canAttach(next) {
t.attach(next, false)
}
}
}
func (t *Tree) after(id1, id2 string) (found bool) {
t.iterate(t.attached[id2], func(c *Change) (isContinue bool) {
if c.Id == id1 {
found = true
return false
}
return true
})
return
}
func (t *Tree) dfs(startChange string) (uniqMap map[string]*Change) {
stack := make([]*Change, 0, 10)
stack = append(stack, t.attached[startChange])
uniqMap = map[string]*Change{}
for len(stack) > 0 {
ch := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if _, exists := uniqMap[ch.Id]; exists {
continue
}
uniqMap[ch.Id] = ch
for _, prev := range ch.PreviousIds {
stack = append(stack, t.attached[prev])
}
}
return uniqMap
}
func (t *Tree) updateHeads() {
var newHeadIds, newMetaHeadIds []string
t.iterate(t.root, func(c *Change) (isContinue bool) {
if len(c.Next) == 0 {
newHeadIds = append(newHeadIds, c.Id)
}
return true
})
t.headIds = newHeadIds
t.metaHeadIds = newMetaHeadIds
sort.Strings(t.headIds)
sort.Strings(t.metaHeadIds)
}
func (t *Tree) ACLHeads() []string {
var aclTreeHeads []string
for _, head := range t.Heads() {
if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads
continue
}
precedingHeads := t.getPrecedingACLHeads(head)
for _, aclHead := range precedingHeads {
if slice.FindPos(aclTreeHeads, aclHead) != -1 {
continue
}
aclTreeHeads = append(aclTreeHeads, aclHead)
}
}
return aclTreeHeads
}
func (t *Tree) getPrecedingACLHeads(head string) []string {
headChange := t.attached[head]
if headChange.Content.GetAclData() != nil {
return []string{head}
} else {
return headChange.Content.AclHeadIds
}
}
func (t *Tree) iterate(start *Change, f func(c *Change) (isContinue bool)) {
it := newIterator()
defer freeIterator(it)
it.iterate(start, f)
}
func (t *Tree) iterateSkip(start *Change, skipBefore *Change, f func(c *Change) (isContinue bool)) {
it := newIterator()
defer freeIterator(it)
it.iterateSkip(start, skipBefore, f)
}
func (t *Tree) IterateSkip(startId string, skipBeforeId string, f func(c *Change) (isContinue bool)) {
it := newIterator()
defer freeIterator(it)
it.iterateSkip(t.attached[startId], t.attached[skipBeforeId], f)
}
func (t *Tree) Iterate(startId string, f func(c *Change) (isContinue bool)) {
t.iterate(t.attached[startId], f)
}
func (t *Tree) IterateBranching(startId string, f func(c *Change, branchLevel int) (isContinue bool)) {
// branchLevel indicates the number of parallel branches
var bc int
t.iterate(t.attached[startId], func(c *Change) (isContinue bool) {
if pl := len(c.PreviousIds); pl > 1 {
bc -= pl - 1
}
bl := bc
if nl := len(c.Next); nl > 1 {
bc += nl - 1
}
return f(c, bl)
})
}
func (t *Tree) Hash() string {
h := md5.New()
n := 0
t.iterate(t.root, func(c *Change) (isContinue bool) {
n++
fmt.Fprintf(h, "-%s", c.Id)
return true
})
return fmt.Sprintf("%d-%x", n, h.Sum(nil))
}
func (t *Tree) GetDuplicateEvents() int {
return t.duplicateEvents
}
func (t *Tree) ResetDuplicateEvents() {
t.duplicateEvents = 0
}
func (t *Tree) Len() int {
return len(t.attached)
}
func (t *Tree) Heads() []string {
return t.headIds
}
func (t *Tree) String() string {
var buf = bytes.NewBuffer(nil)
t.Iterate(t.RootId(), func(c *Change) (isContinue bool) {
buf.WriteString(c.Id)
if len(c.Next) > 1 {
buf.WriteString("-<")
} else if len(c.Next) > 0 {
buf.WriteString("->")
} else {
buf.WriteString("-|")
}
return true
})
return buf.String()
}
func (t *Tree) Get(id string) *Change {
return t.attached[id]
}
type sortChanges []*Change
func (s sortChanges) Len() int {
return len(s)
}
func (s sortChanges) Less(i, j int) bool {
return s[i].Id < s[j].Id
}
func (s sortChanges) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

365
pkg/acl/tree/treebuilder.go Normal file
View File

@ -0,0 +1,365 @@
package tree
import (
"context"
"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/pkg/acl/treestorage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
"github.com/gogo/protobuf/proto"
"time"
)
var (
log = logger.NewNamed("acltree").Sugar()
ErrEmpty = errors.New("logs empty")
)
type treeBuilder struct {
cache map[string]*Change
identityKeys map[string]signingkey.PubKey
signingPubKeyDecoder signingkey.PubKeyDecoder
tree *Tree
treeStorage treestorage.TreeStorage
}
func newTreeBuilder(t treestorage.TreeStorage, decoder signingkey.PubKeyDecoder) *treeBuilder {
return &treeBuilder{
signingPubKeyDecoder: decoder,
treeStorage: t,
}
}
func (tb *treeBuilder) Init() {
tb.cache = make(map[string]*Change)
tb.identityKeys = make(map[string]signingkey.PubKey)
tb.tree = &Tree{}
}
func (tb *treeBuilder) Build(fromStart bool) (*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...)
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)
}
}
tb.cache = nil
return tb.tree, nil
}
func (tb *treeBuilder) 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 *treeBuilder) 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 *treeBuilder) buildTree(heads []string, breakpoint string) (err error) {
ch, err := tb.loadChange(breakpoint)
if err != nil {
return
}
tb.tree.AddFast(ch)
changes, err := tb.dfs(heads, breakpoint, tb.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)
}
}
return buf, nil
}
func (tb *treeBuilder) loadChange(id string) (ch *Change, err error) {
if ch, ok := tb.cache[id]; ok {
return ch, nil
}
// TODO: Add virtual changes logic
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
change, err := tb.treeStorage.GetChange(ctx, id)
if err != nil {
return nil, err
}
verifiedChange, err := tb.makeVerifiedChange(change)
if err != nil {
return nil, err
}
tb.cache[id] = NewChange(id, verifiedChange)
return ch, nil
}
func (tb *treeBuilder) verify(identity string, payload, signature []byte) (isVerified bool, err error) {
identityKey, exists := tb.identityKeys[identity]
if !exists {
identityKey, err = tb.signingPubKeyDecoder.DecodeFromString(identity)
if err != nil {
return
}
tb.identityKeys[identity] = identityKey
}
return identityKey.Verify(payload, signature)
}
func (tb *treeBuilder) makeVerifiedChange(change *aclpb.RawChange) (aclChange *aclpb.Change, err error) {
aclChange = new(aclpb.Change)
// TODO: think what should we do with such cases, because this can be used by attacker to break our Tree
if err = proto.Unmarshal(change.Payload, aclChange); err != nil {
return
}
var verified bool
verified, err = tb.verify(aclChange.Identity, change.Payload, change.Signature)
if err != nil {
return
}
if !verified {
err = fmt.Errorf("the signature of the payload cannot be verified")
return
}
return
}
func (tb *treeBuilder) makeUnverifiedACLChange(change *aclpb.RawChange) (aclChange *aclpb.ACLChange, err error) {
aclChange = new(aclpb.ACLChange)
err = proto.Unmarshal(change.Payload, aclChange)
return
}
func (tb *treeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) {
var (
ch *Change
snapshotIds []string
)
for _, head := range heads {
if ch, err = tb.loadChange(head); err != nil {
return
}
shId := ch.SnapshotId
if ch.IsSnapshot {
shId = ch.Id
}
if slice.FindPos(snapshotIds, shId) == -1 {
snapshotIds = append(snapshotIds, shId)
}
}
return tb.findCommonSnapshot(snapshotIds)
}
func (tb *treeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId string, err error) {
if len(snapshotIds) == 1 {
return snapshotIds[0], nil
} else if len(snapshotIds) == 0 {
return "", fmt.Errorf("snapshots not found")
}
for len(snapshotIds) > 1 {
l := len(snapshotIds)
shId, e := tb.findCommonForTwoSnapshots(snapshotIds[l-2], snapshotIds[l-1])
if e != nil {
return "", e
}
snapshotIds[l-2] = shId
snapshotIds = snapshotIds[:l-1]
}
return snapshotIds[0], nil
}
func (tb *treeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err error) {
// fast cases
if s1 == s2 {
return s1, nil
}
ch1, err := tb.loadChange(s1)
if err != nil {
return "", err
}
if ch1.SnapshotId == s2 {
return s2, nil
}
ch2, err := tb.loadChange(s2)
if err != nil {
return "", err
}
if ch2.SnapshotId == s1 {
return s1, nil
}
if ch1.SnapshotId == ch2.SnapshotId && ch1.SnapshotId != "" {
return ch1.SnapshotId, nil
}
// traverse
var t1 = make([]string, 0, 5)
var t2 = make([]string, 0, 5)
t1 = append(t1, ch1.Id, ch1.SnapshotId)
t2 = append(t2, ch2.Id, ch2.SnapshotId)
for {
lid1 := t1[len(t1)-1]
if lid1 != "" {
l1, e := tb.loadChange(lid1)
if e != nil {
return "", e
}
if l1.SnapshotId != "" {
if slice.FindPos(t2, l1.SnapshotId) != -1 {
return l1.SnapshotId, nil
}
}
t1 = append(t1, l1.SnapshotId)
}
lid2 := t2[len(t2)-1]
if lid2 != "" {
l2, e := tb.loadChange(t2[len(t2)-1])
if e != nil {
return "", e
}
if l2.SnapshotId != "" {
if slice.FindPos(t1, l2.SnapshotId) != -1 {
return l2.SnapshotId, nil
}
}
t2 = append(t2, l2.SnapshotId)
}
if lid1 == "" && lid2 == "" {
break
}
}
log.Warnf("changes build Tree: possible versions split")
// prefer not first snapshot
if len(ch1.PreviousIds) == 0 && len(ch2.PreviousIds) > 0 {
log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s2, len(ch2.PreviousIds), s1, len(ch1.PreviousIds))
return s2, nil
} else if len(ch1.PreviousIds) > 0 && len(ch2.PreviousIds) == 0 {
log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s1, len(ch1.PreviousIds), s2, len(ch2.PreviousIds))
return s1, nil
}
isEmptySnapshot := func(ch *Change) bool {
// TODO: add more sophisticated checks in Change for snapshots
return !ch.IsSnapshot
}
// TODO: can we even have empty snapshots?
// prefer not empty snapshot
if isEmptySnapshot(ch1) && !isEmptySnapshot(ch2) {
log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s2, s1)
return s2, nil
} else if isEmptySnapshot(ch2) && !isEmptySnapshot(ch1) {
log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s1, s2)
return s1, nil
}
// TODO: add virtual change mechanics
// unexpected behavior - just return lesser id
if s1 < s2 {
log.Warnf("changes build Tree: prefer %s (%s<%s)", s1, s1, s2)
return s1, nil
}
log.Warnf("changes build Tree: prefer %s (%s<%s)", s2, s2, s1)
return s2, nil
}