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