1
0
Fork 0
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:
mcrakhman 2022-08-19 21:44:38 +02:00 committed by Mikhail Iudin
parent a140834288
commit 8868bd92fe
No known key found for this signature in database
GPG key ID: FAAAA8BAABDFF1C0
8 changed files with 188 additions and 71 deletions

View file

@ -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
}

View file

@ -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
}
}
}

View file

@ -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
}

View file

@ -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)

View file

@ -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 {

View file

@ -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) {

View file

@ -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

View file

@ -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")