mirror of
https://github.com/anyproto/any-sync.git
synced 2025-06-10 18:10:54 +09:00
Add ACLList struct
This commit is contained in:
parent
a140834288
commit
8868bd92fe
8 changed files with 188 additions and 71 deletions
|
@ -7,7 +7,6 @@ import (
|
|||
)
|
||||
|
||||
type aclStateBuilder struct {
|
||||
log ACLList
|
||||
identity string
|
||||
key encryptionkey.PrivKey
|
||||
decoder keys.Decoder
|
||||
|
@ -25,12 +24,7 @@ func newACLStateBuilder() *aclStateBuilder {
|
|||
return &aclStateBuilder{}
|
||||
}
|
||||
|
||||
func (sb *aclStateBuilder) Init(aclLog ACLList) error {
|
||||
sb.log = aclLog
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *aclStateBuilder) Build() (*ACLState, error) {
|
||||
func (sb *aclStateBuilder) Build(records []*Record) (*ACLState, error) {
|
||||
var (
|
||||
err error
|
||||
state *ACLState
|
||||
|
@ -41,11 +35,12 @@ func (sb *aclStateBuilder) Build() (*ACLState, error) {
|
|||
} else {
|
||||
state = newACLState()
|
||||
}
|
||||
|
||||
sb.log.Iterate(func(c *Record) (isContinue bool) {
|
||||
err = state.applyChangeAndUpdate(c)
|
||||
return err == nil
|
||||
})
|
||||
for _, rec := range records {
|
||||
err = state.applyChangeAndUpdate(rec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return state, err
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
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/tree"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type IterFunc = func(record *Record) (IsContinue bool)
|
||||
|
@ -11,7 +15,7 @@ type ACLList interface {
|
|||
tree.RWLocker
|
||||
ID() string
|
||||
Header() *aclpb.Header
|
||||
ACLState() ACLState
|
||||
ACLState() *ACLState
|
||||
IsAfter(first string, second string) (bool, error)
|
||||
Head() *Record
|
||||
Get(id string) (*Record, error)
|
||||
|
@ -19,43 +23,123 @@ type ACLList interface {
|
|||
IterateFrom(startId string, iterFunc IterFunc)
|
||||
}
|
||||
|
||||
//func (t *ACLListStorageBuilder) IsAfter(first string, second string) (bool, error) {
|
||||
// firstRec, okFirst := t.indexes[first]
|
||||
// secondRec, okSecond := t.indexes[second]
|
||||
// if !okFirst || !okSecond {
|
||||
// return false, fmt.Errorf("not all entries are there: first (%b), second (%b)", okFirst, okSecond)
|
||||
// }
|
||||
// return firstRec > secondRec, nil
|
||||
//}
|
||||
//
|
||||
//func (t *ACLListStorageBuilder) Head() *list.Record {
|
||||
// return t.records[len(t.records)-1]
|
||||
//}
|
||||
//
|
||||
//func (t *ACLListStorageBuilder) Get(id string) (*list.Record, error) {
|
||||
// recIdx, ok := t.indexes[id]
|
||||
// if !ok {
|
||||
// return nil, fmt.Errorf("no such record")
|
||||
// }
|
||||
// return t.records[recIdx], nil
|
||||
//}
|
||||
//
|
||||
//func (t *ACLListStorageBuilder) Iterate(iterFunc list.IterFunc) {
|
||||
// for _, rec := range t.records {
|
||||
// if !iterFunc(rec) {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (t *ACLListStorageBuilder) IterateFrom(startId string, iterFunc list.IterFunc) {
|
||||
// recIdx, ok := t.indexes[startId]
|
||||
// if !ok {
|
||||
// return
|
||||
// }
|
||||
// for i := recIdx; i < len(t.records); i++ {
|
||||
// if !iterFunc(t.records[i]) {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
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) (ACLList, error) {
|
||||
builder := newACLStateBuilderWithIdentity(acc.Decoder, acc)
|
||||
header, err := storage.Header()
|
||||
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.GetRecord(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
|
||||
}
|
||||
|
||||
state, err := builder.Build(records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &aclList{
|
||||
header: header,
|
||||
records: records,
|
||||
indexes: indexes,
|
||||
builder: builder,
|
||||
aclState: state,
|
||||
RWMutex: sync.RWMutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
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 (%b), second (%b)", 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package list
|
||||
|
||||
import "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
||||
import (
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
type Record struct {
|
||||
Id string
|
||||
|
@ -15,3 +18,17 @@ func NewRecord(id string, aclRecord *aclpb.Record) *Record {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
type Storage interface {
|
||||
ID() string
|
||||
ID() (string, error)
|
||||
Head() (*aclpb.RawRecord, error)
|
||||
Header() (*aclpb.Header, error)
|
||||
GetRecord(ctx context.Context, id string) (*aclpb.RawRecord, error)
|
||||
|
|
|
@ -101,8 +101,8 @@ func (t *ACLListStorageBuilder) AddRecord(ctx context.Context, rec *aclpb.Record
|
|||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *ACLListStorageBuilder) ID() string {
|
||||
return t.id
|
||||
func (t *ACLListStorageBuilder) ID() (string, error) {
|
||||
return t.id, nil
|
||||
}
|
||||
|
||||
func (t *ACLListStorageBuilder) GetKeychain() *Keychain {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
||||
"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"
|
||||
"go.uber.org/zap"
|
||||
"sync"
|
||||
|
@ -130,6 +131,22 @@ func BuildDocTree(t treestorage.TreeStorage, decoder keys.Decoder, listener Tree
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageHeads, err := t.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, docTree.tree.Heads()) {
|
||||
log.With(zap.Strings("storage", storageHeads), zap.Strings("rebuilt", docTree.tree.Heads())).
|
||||
Errorf("the heads in storage and tree are different")
|
||||
err = t.SetHeads(docTree.tree.Heads())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
docTree.id, err = t.ID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -324,11 +341,22 @@ func (d *docTree) AddRawChanges(ctx context.Context, aclList list.ACLList, rawCh
|
|||
return added
|
||||
}
|
||||
|
||||
rollback := func() {
|
||||
for _, ch := range d.tmpChangesBuf {
|
||||
if _, exists := d.tree.attached[ch.Id]; exists {
|
||||
delete(d.tree.attached, ch.Id)
|
||||
} else if _, exists := d.tree.unAttached[ch.Id]; exists {
|
||||
delete(d.tree.unAttached, ch.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checking if we have some changes with different snapshot and then rebuilding
|
||||
for _, ch := range d.tmpChangesBuf {
|
||||
if ch.SnapshotId != d.tree.RootId() && ch.SnapshotId != "" {
|
||||
err = d.rebuildFromStorage(aclList, d.tmpChangesBuf)
|
||||
if err != nil {
|
||||
rollback()
|
||||
return AddResult{}, err
|
||||
}
|
||||
|
||||
|
@ -360,14 +388,7 @@ func (d *docTree) AddRawChanges(ctx context.Context, aclList list.ACLList, rawCh
|
|||
// as an optimization we could've started from current heads, but I didn't implement that
|
||||
err = d.validator.ValidateTree(d.tree, aclList)
|
||||
if err != nil {
|
||||
// rolling back
|
||||
for _, ch := range d.tmpChangesBuf {
|
||||
if _, exists := d.tree.attached[ch.Id]; exists {
|
||||
delete(d.tree.attached, ch.Id)
|
||||
} else if _, exists := d.tree.unAttached[ch.Id]; exists {
|
||||
delete(d.tree.unAttached, ch.Id)
|
||||
}
|
||||
}
|
||||
rollback()
|
||||
return AddResult{}, ErrHasInvalidChanges
|
||||
}
|
||||
|
||||
|
@ -444,6 +465,7 @@ func (d *docTree) ChangesAfterCommonSnapshot(theirPath []string) ([]*aclpb.RawCh
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
// TODO: if the snapshot is in the tree we probably can skip going to the DB
|
||||
var rawChanges []*aclpb.RawChange
|
||||
// using custom load function to skip verification step and save raw changes
|
||||
load := func(id string) (*Change, error) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"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/tree"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb"
|
||||
"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"
|
||||
|
@ -23,7 +22,7 @@ var log = logger.NewNamed("treecache")
|
|||
|
||||
type Service interface {
|
||||
Do(ctx context.Context, treeId string, f TreeFunc) error
|
||||
Add(ctx context.Context, treeId string, header *treepb.TreeHeader, changes []*aclpb.RawChange, f TreeFunc) error
|
||||
Add(ctx context.Context, treeId string, header *aclpb.Header, changes []*aclpb.RawChange, f TreeFunc) error
|
||||
}
|
||||
|
||||
type service struct {
|
||||
|
@ -91,10 +90,10 @@ func (s *service) loadTree(ctx context.Context, id string) (ocache.Object, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
switch header.Type {
|
||||
case treepb.TreeHeader_ACLTree:
|
||||
switch header.DocType {
|
||||
case aclpb.Header_ACL:
|
||||
return tree.BuildACLTreeWithIdentity(t, s.account.Account(), nil)
|
||||
case treepb.TreeHeader_DocTree:
|
||||
case aclpb.Header_DocTree:
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("incorrect type")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue