1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-08 05:57:03 +09:00
This commit is contained in:
Sergey Cherepanov 2024-02-15 19:12:28 +01:00
commit e86ba80a67
No known key found for this signature in database
GPG key ID: 87F8EDE8FBDF637C
21 changed files with 485 additions and 164 deletions

View file

@ -23,6 +23,7 @@ type AclJoiningClient interface {
app.Component app.Component
AclGetRecords(ctx context.Context, spaceId, aclHead string) ([]*consensusproto.RawRecordWithId, error) AclGetRecords(ctx context.Context, spaceId, aclHead string) ([]*consensusproto.RawRecordWithId, error)
RequestJoin(ctx context.Context, spaceId string, payload list.RequestJoinPayload) error RequestJoin(ctx context.Context, spaceId string, payload list.RequestJoinPayload) error
SendRecord(ctx context.Context, spaceId string, rec *consensusproto.RawRecord) (res *spacesyncproto.AclAddRecordResponse, err error)
} }
type aclJoiningClient struct { type aclJoiningClient struct {
@ -113,6 +114,24 @@ func (c *aclJoiningClient) RequestJoin(ctx context.Context, spaceId string, payl
}) })
} }
func (c *aclJoiningClient) SendRecord(ctx context.Context, spaceId string, rec *consensusproto.RawRecord) (res *spacesyncproto.AclAddRecordResponse, err error) {
marshalled, err := rec.Marshal()
if err != nil {
return
}
err = c.doClient(ctx, spaceId, func(cl spacesyncproto.DRPCSpaceSyncClient) error {
res, err = cl.AclAddRecord(ctx, &spacesyncproto.AclAddRecordRequest{
SpaceId: spaceId,
Payload: marshalled,
})
if err != nil {
return err
}
return nil
})
return
}
func (c *aclJoiningClient) doClient(ctx context.Context, spaceId string, f func(cl spacesyncproto.DRPCSpaceSyncClient) error) error { func (c *aclJoiningClient) doClient(ctx context.Context, spaceId string, f func(cl spacesyncproto.DRPCSpaceSyncClient) error) error {
p, err := c.pool.GetOneOf(ctx, c.nodeConf.NodeIds(spaceId)) p, err := c.pool.GetOneOf(ctx, c.nodeConf.NodeIds(spaceId))
if err != nil { if err != nil {

View file

@ -113,6 +113,9 @@ func (d *diffSyncer) Sync(ctx context.Context) error {
} }
func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error) { func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error) {
if !d.treeSyncer.ShouldSync(p.Id()) {
return
}
ctx = logger.CtxWithFields(ctx, zap.String("peerId", p.Id())) ctx = logger.CtxWithFields(ctx, zap.String("peerId", p.Id()))
conn, err := p.AcquireDrpcConn(ctx) conn, err := p.AcquireDrpcConn(ctx)
if err != nil { if err != nil {

View file

@ -119,6 +119,7 @@ func TestDiffSyncer(t *testing.T) {
mPeer := mockPeer{} mPeer := mockPeer{}
remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock) remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock)
fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId")
fx.treeSyncerMock.EXPECT().ShouldSync(gomock.Any()).Return(true)
fx.peerManagerMock.EXPECT(). fx.peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()). GetResponsiblePeers(gomock.Any()).
Return([]peer.Peer{mPeer}, nil) Return([]peer.Peer{mPeer}, nil)
@ -140,6 +141,7 @@ func TestDiffSyncer(t *testing.T) {
defer fx.stop() defer fx.stop()
mPeer := mockPeer{} mPeer := mockPeer{}
remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock) remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock)
fx.treeSyncerMock.EXPECT().ShouldSync(gomock.Any()).Return(true)
fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId")
fx.peerManagerMock.EXPECT(). fx.peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()). GetResponsiblePeers(gomock.Any()).
@ -205,6 +207,7 @@ func TestDiffSyncer(t *testing.T) {
fx.initDiffSyncer(t) fx.initDiffSyncer(t)
defer fx.stop() defer fx.stop()
fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId")
fx.treeSyncerMock.EXPECT().ShouldSync(gomock.Any()).Return(true)
aclStorageMock := mock_liststorage.NewMockListStorage(fx.ctrl) aclStorageMock := mock_liststorage.NewMockListStorage(fx.ctrl)
settingsStorage := mock_treestorage.NewMockTreeStorage(fx.ctrl) settingsStorage := mock_treestorage.NewMockTreeStorage(fx.ctrl)
settingsId := "settingsId" settingsId := "settingsId"
@ -255,6 +258,7 @@ func TestDiffSyncer(t *testing.T) {
defer fx.stop() defer fx.stop()
remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock) remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock)
fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId")
fx.treeSyncerMock.EXPECT().ShouldSync(gomock.Any()).Return(true)
fx.peerManagerMock.EXPECT(). fx.peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()). GetResponsiblePeers(gomock.Any()).
Return([]peer.Peer{mockPeer{}}, nil) Return([]peer.Peer{mockPeer{}}, nil)
@ -273,6 +277,7 @@ func TestDiffSyncer(t *testing.T) {
defer fx.stop() defer fx.stop()
mPeer := mockPeer{} mPeer := mockPeer{}
remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock) remDiff := NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock)
fx.treeSyncerMock.EXPECT().ShouldSync(gomock.Any()).Return(true)
fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId")
fx.peerManagerMock.EXPECT(). fx.peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()). GetResponsiblePeers(gomock.Any()).

View file

@ -1,6 +1,7 @@
package list package list
import ( import (
"errors"
"time" "time"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
@ -509,6 +510,10 @@ func (a *aclRecordBuilder) BuildRequestRemove() (rawRecord *consensusproto.RawRe
err = ErrIsOwner err = ErrIsOwner
return return
} }
_, err = a.state.Record(a.state.pubKey)
if !errors.Is(err, ErrNoSuchRecord) {
return nil, ErrPendingRequest
}
removeRec := &aclrecordproto.AclAccountRequestRemove{} removeRec := &aclrecordproto.AclAccountRequestRemove{}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_AccountRequestRemove{AccountRequestRemove: removeRec}} content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_AccountRequestRemove{AccountRequestRemove: removeRec}}
return a.buildRecord(content) return a.buildRecord(content)

View file

@ -163,7 +163,7 @@ func (st *AclState) PermissionsAtRecord(id string, pubKey crypto.PubKey) (AclPer
return perms, nil return perms, nil
} }
func (st *AclState) CurrentStates() []AccountState { func (st *AclState) CurrentAccounts() []AccountState {
var res []AccountState var res []AccountState
for _, state := range st.accountStates { for _, state := range st.accountStates {
res = append(res, state) res = append(res, state)
@ -171,6 +171,19 @@ func (st *AclState) CurrentStates() []AccountState {
return res return res
} }
func (st *AclState) HadReadPermissions(identity crypto.PubKey) (had bool) {
state, exists := st.accountStates[mapKeyFromPubKey(identity)]
if !exists {
return false
}
for _, perm := range state.PermissionChanges {
if !perm.Permission.NoPermissions() {
return true
}
}
return false
}
func (st *AclState) Invites() []crypto.PubKey { func (st *AclState) Invites() []crypto.PubKey {
var invites []crypto.PubKey var invites []crypto.PubKey
for _, inv := range st.inviteKeys { for _, inv := range st.inviteKeys {

View file

@ -101,6 +101,7 @@ func TestAclExecutor(t *testing.T) {
// g can cancel request to remove // g can cancel request to remove
{"g.cancel:g", nil}, {"g.cancel:g", nil},
{"g.request_remove:g", nil}, {"g.request_remove:g", nil},
{"g.request_remove:g", ErrPendingRequest},
{"a.remove:g", nil}, {"a.remove:g", nil},
// g cannot cancel not existing request to remove // g cannot cancel not existing request to remove
{"g.cancel:g", ErrNoSuchRecord}, {"g.cancel:g", ErrNoSuchRecord},

View file

@ -247,6 +247,9 @@ func (c *contentValidator) ValidateRequestRemove(ch *aclrecordproto.AclAccountRe
if c.aclState.Permissions(authorIdentity).NoPermissions() { if c.aclState.Permissions(authorIdentity).NoPermissions() {
return ErrInsufficientPermissions return ErrInsufficientPermissions
} }
if c.aclState.Permissions(authorIdentity).IsOwner() {
return ErrIsOwner
}
if _, exists := c.aclState.pendingRequests[mapKeyFromPubKey(authorIdentity)]; exists { if _, exists := c.aclState.pendingRequests[mapKeyFromPubKey(authorIdentity)]; exists {
return ErrPendingRequest return ErrPendingRequest
} }

View file

@ -1,12 +1,17 @@
package objecttree package objecttree
import ( import (
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
) )
func IsEmptyDerivedTree(tree ObjectTree) bool { func IsEmptyDerivedTree(tree ObjectTree) bool {
return tree.IsDerived() && tree.Len() == 1 && tree.Root().Id == tree.Header().Id return tree.IsDerived() && IsEmptyTree(tree)
}
func IsEmptyTree(tree ObjectTree) bool {
return tree.Len() == 1 && tree.Root().Id == tree.Header().Id
} }
func IsDerivedRoot(root *treechangeproto.RawTreeChangeWithId) (derived bool, err error) { func IsDerivedRoot(root *treechangeproto.RawTreeChangeWithId) (derived bool, err error) {

View file

@ -366,6 +366,16 @@ func (ot *objectTree) addRawChanges(ctx context.Context, changesPayload RawChang
return return
} }
var (
filteredHeads = false
headsToUse = changesPayload.NewHeads
)
// if our validator provides filtering mechanism then we use it
filteredHeads, ot.newChangesBuf, ot.newSnapshotsBuf, ot.notSeenIdxBuf = ot.validator.FilterChanges(ot.aclList, changesPayload.NewHeads, ot.newChangesBuf, ot.newSnapshotsBuf, ot.notSeenIdxBuf)
if filteredHeads {
// if we filtered some of the heads, then we don't know which heads to use
headsToUse = []string{}
}
rollback := func(changes []*Change) { rollback := func(changes []*Change) {
for _, ch := range changes { for _, ch := range changes {
if _, exists := ot.tree.attached[ch.Id]; exists { if _, exists := ot.tree.attached[ch.Id]; exists {
@ -400,7 +410,7 @@ func (ot *objectTree) addRawChanges(ctx context.Context, changesPayload RawChang
} }
if shouldRebuildFromStorage { if shouldRebuildFromStorage {
err = ot.rebuildFromStorage(changesPayload.NewHeads, ot.newChangesBuf) err = ot.rebuildFromStorage(headsToUse, ot.newChangesBuf)
if err != nil { if err != nil {
// rebuilding without new changes // rebuilding without new changes
ot.rebuildFromStorage(nil, nil) ot.rebuildFromStorage(nil, nil)
@ -662,12 +672,15 @@ func (ot *objectTree) readKeysFromAclState(state *list.AclState) (err error) {
return nil return nil
} }
// if we can't read the keys anyway // if we can't read the keys anyway
if state.AccountKey() == nil || state.Permissions(state.AccountKey().GetPublic()).NoPermissions() { if state.AccountKey() == nil || !state.HadReadPermissions(state.AccountKey().GetPublic()) {
return nil return nil
} }
for key, value := range state.Keys() { for key, value := range state.Keys() {
if _, exists := ot.keys[key]; exists {
continue
}
if value.ReadKey == nil { if value.ReadKey == nil {
return list.ErrNoReadKey continue
} }
treeKey, err := deriveTreeKey(value.ReadKey, ot.id) treeKey, err := deriveTreeKey(value.ReadKey, ot.id)
if err != nil { if err != nil {
@ -679,6 +692,9 @@ func (ot *objectTree) readKeysFromAclState(state *list.AclState) (err error) {
if err != nil { if err != nil {
return err return err
} }
if curKey == nil {
return nil
}
ot.currentReadKey, err = deriveTreeKey(curKey, ot.id) ot.currentReadKey, err = deriveTreeKey(curKey, ot.id)
return err return err
} }

View file

@ -25,6 +25,25 @@ type testTreeContext struct {
objTree ObjectTree objTree ObjectTree
} }
func genBuildFilterableTestableTree(filterFunc func(ch *Change) bool) func(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
return func(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
root, _ := treeStorage.Root()
changeBuilder := &nonVerifiableChangeBuilder{
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
}
deps := objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: newTreeBuilder(true, treeStorage, changeBuilder),
treeStorage: treeStorage,
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
validator: &noOpTreeValidator{filterFunc: filterFunc},
aclList: aclList,
}
return buildObjectTree(deps)
}
}
func prepareAclList(t *testing.T) (list.AclList, *accountdata.AccountKeys) { func prepareAclList(t *testing.T) (list.AclList, *accountdata.AccountKeys) {
randKeys, err := accountdata.NewRandom() randKeys, err := accountdata.NewRandom()
require.NoError(t, err) require.NoError(t, err)
@ -129,7 +148,7 @@ func TestObjectTree(t *testing.T) {
}, aAccount.Acl) }, aAccount.Acl)
require.NoError(t, err) require.NoError(t, err)
aStore, _ := treestorage.NewInMemoryTreeStorage(root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root}) aStore, _ := treestorage.NewInMemoryTreeStorage(root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root})
aTree, err := BuildKeyVerifiableObjectTree(aStore, aAccount.Acl) aTree, err := BuildKeyFilterableObjectTree(aStore, aAccount.Acl)
require.NoError(t, err) require.NoError(t, err)
_, err = aTree.AddContent(ctx, SignableChangeContent{ _, err = aTree.AddContent(ctx, SignableChangeContent{
Data: []byte("some"), Data: []byte("some"),
@ -140,7 +159,7 @@ func TestObjectTree(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
bStore := aTree.Storage().(*treestorage.InMemoryTreeStorage).Copy() bStore := aTree.Storage().(*treestorage.InMemoryTreeStorage).Copy()
bTree, err := BuildKeyVerifiableObjectTree(bStore, bAccount.Acl) bTree, err := BuildKeyFilterableObjectTree(bStore, bAccount.Acl)
require.NoError(t, err) require.NoError(t, err)
err = exec.Execute("a.remove:b") err = exec.Execute("a.remove:b")
require.NoError(t, err) require.NoError(t, err)
@ -157,17 +176,17 @@ func TestObjectTree(t *testing.T) {
NewHeads: aTree.Heads(), NewHeads: aTree.Heads(),
RawChanges: res.Added, RawChanges: res.Added,
}) })
require.Equal(t, ErrHasInvalidChanges, err)
require.Equal(t, oldHeads, bTree.Heads()) require.Equal(t, oldHeads, bTree.Heads())
bStore = aTree.Storage().(*treestorage.InMemoryTreeStorage).Copy() bStore = aTree.Storage().(*treestorage.InMemoryTreeStorage).Copy()
root, _ = bStore.Root() root, _ = bStore.Root()
heads, _ := bStore.Heads() heads, _ := bStore.Heads()
err = ValidateRawTreeBuildFunc(treestorage.TreeStorageCreatePayload{ filteredPayload, err := ValidateFilterRawTree(treestorage.TreeStorageCreatePayload{
RootRawChange: root, RootRawChange: root,
Changes: bStore.AllChanges(), Changes: bStore.AllChanges(),
Heads: heads, Heads: heads,
}, BuildKeyVerifiableObjectTree, bAccount.Acl) }, bAccount.Acl)
require.Equal(t, ErrHasInvalidChanges, err) require.NoError(t, err)
require.Equal(t, 2, len(filteredPayload.Changes))
err = aTree.IterateRoot(func(change *Change, decrypted []byte) (any, error) { err = aTree.IterateRoot(func(change *Change, decrypted []byte) (any, error) {
return nil, nil return nil, nil
}, func(change *Change) bool { }, func(change *Change) bool {
@ -667,6 +686,35 @@ func TestObjectTree(t *testing.T) {
}) })
}) })
t.Run("test tree filter", func(t *testing.T) {
filterFunc := func(change *Change) bool {
return slices.Contains([]string{"0", "1", "2", "3", "4", "7", "8"}, change.Id)
}
ctx := prepareContext(t, aclList, genBuildFilterableTestableTree(filterFunc), false, nil)
rawChanges := []*treechangeproto.RawTreeChangeWithId{
ctx.changeCreator.CreateRawWithData("1", aclList.Head().Id, "0", false, []byte("1"), "0"),
ctx.changeCreator.CreateRawWithData("2", aclList.Head().Id, "0", false, []byte("2"), "1"),
ctx.changeCreator.CreateRawWithData("3", aclList.Head().Id, "0", false, []byte("3"), "2"),
ctx.changeCreator.CreateRawWithData("4", aclList.Head().Id, "0", false, []byte("4"), "2"),
ctx.changeCreator.CreateRawWithData("5", aclList.Head().Id, "0", false, []byte("5"), "1"),
ctx.changeCreator.CreateRawWithData("6", aclList.Head().Id, "0", true, []byte("6"), "3", "4", "5"),
ctx.changeCreator.CreateRawWithData("7", aclList.Head().Id, "6", false, []byte("7"), "6"),
ctx.changeCreator.CreateRawWithData("8", aclList.Head().Id, "6", false, []byte("8"), "6"),
}
_, err := ctx.objTree.AddRawChanges(context.Background(), RawChangesPayload{
NewHeads: []string{"7", "8"},
RawChanges: rawChanges,
})
require.NoError(t, err)
var ids []string
ctx.objTree.IterateRoot(nil, func(change *Change) bool {
ids = append(ids, change.Id)
return true
})
slices.Sort(ids)
require.Equal(t, []string{"0", "1", "2", "3", "4"}, ids)
})
t.Run("rollback when add to storage returns error", func(t *testing.T) { t.Run("rollback when add to storage returns error", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList) ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator

View file

@ -55,7 +55,7 @@ func verifiableTreeDeps(
changeBuilder: changeBuilder, changeBuilder: changeBuilder,
treeBuilder: treeBuilder, treeBuilder: treeBuilder,
treeStorage: treeStorage, treeStorage: treeStorage,
validator: newTreeValidator(false), validator: newTreeValidator(false, false),
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder), rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
aclList: aclList, aclList: aclList,
} }
@ -71,7 +71,7 @@ func emptyDataTreeDeps(
changeBuilder: changeBuilder, changeBuilder: changeBuilder,
treeBuilder: treeBuilder, treeBuilder: treeBuilder,
treeStorage: treeStorage, treeStorage: treeStorage,
validator: newTreeValidator(false), validator: newTreeValidator(false, false),
rawChangeLoader: newStorageLoader(treeStorage, changeBuilder), rawChangeLoader: newStorageLoader(treeStorage, changeBuilder),
aclList: aclList, aclList: aclList,
} }
@ -136,13 +136,13 @@ func BuildEmptyDataTestableTree(treeStorage treestorage.TreeStorage, aclList lis
return buildObjectTree(deps) return buildObjectTree(deps)
} }
func BuildKeyVerifiableObjectTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) { func BuildKeyFilterableObjectTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
rootChange, err := treeStorage.Root() rootChange, err := treeStorage.Root()
if err != nil { if err != nil {
return nil, err return nil, err
} }
deps := defaultObjectTreeDeps(rootChange, treeStorage, aclList) deps := defaultObjectTreeDeps(rootChange, treeStorage, aclList)
deps.validator = newTreeValidator(true) deps.validator = newTreeValidator(true, true)
return buildObjectTree(deps) return buildObjectTree(deps)
} }

View file

@ -9,14 +9,19 @@ import (
"github.com/anyproto/any-sync/util/slice" "github.com/anyproto/any-sync/util/slice"
) )
type ValidatorFunc func(payload treestorage.TreeStorageCreatePayload, buildFunc BuildObjectTreeFunc, aclList list.AclList) (retPayload treestorage.TreeStorageCreatePayload, err error)
type ObjectTreeValidator interface { type ObjectTreeValidator interface {
// ValidateFullTree should always be entered while holding a read lock on AclList // ValidateFullTree should always be entered while holding a read lock on AclList
ValidateFullTree(tree *Tree, aclList list.AclList) error ValidateFullTree(tree *Tree, aclList list.AclList) error
// ValidateNewChanges should always be entered while holding a read lock on AclList // ValidateNewChanges should always be entered while holding a read lock on AclList
ValidateNewChanges(tree *Tree, aclList list.AclList, newChanges []*Change) error ValidateNewChanges(tree *Tree, aclList list.AclList, newChanges []*Change) error
FilterChanges(aclList list.AclList, heads []string, changes []*Change, snapshots []*Change, indexes []int) (filteredHeads bool, filtered, filteredSnapshots []*Change, newIndexes []int)
} }
type noOpTreeValidator struct{} type noOpTreeValidator struct {
filterFunc func(ch *Change) bool
}
func (n *noOpTreeValidator) ValidateFullTree(tree *Tree, aclList list.AclList) error { func (n *noOpTreeValidator) ValidateFullTree(tree *Tree, aclList list.AclList) error {
return nil return nil
@ -26,13 +31,34 @@ func (n *noOpTreeValidator) ValidateNewChanges(tree *Tree, aclList list.AclList,
return nil return nil
} }
type objectTreeValidator struct { func (n *noOpTreeValidator) FilterChanges(aclList list.AclList, heads []string, changes []*Change, snapshots []*Change, indexes []int) (filteredHeads bool, filtered, filteredSnapshots []*Change, newIndexes []int) {
validateKeys bool if n.filterFunc == nil {
return false, changes, snapshots, indexes
}
for idx, c := range changes {
// only taking changes which we can read
if n.filterFunc(c) {
newIndexes = append(newIndexes, indexes[idx])
filtered = append(filtered, c)
if c.IsSnapshot {
filteredSnapshots = append(filteredSnapshots, c)
}
} else {
filteredHeads = true
}
}
return
} }
func newTreeValidator(validateKeys bool) ObjectTreeValidator { type objectTreeValidator struct {
validateKeys bool
shouldFilter bool
}
func newTreeValidator(validateKeys bool, filterChanges bool) ObjectTreeValidator {
return &objectTreeValidator{ return &objectTreeValidator{
validateKeys: validateKeys, validateKeys: validateKeys,
shouldFilter: filterChanges,
} }
} }
@ -54,6 +80,30 @@ func (v *objectTreeValidator) ValidateNewChanges(tree *Tree, aclList list.AclLis
return return
} }
func (v *objectTreeValidator) FilterChanges(aclList list.AclList, heads []string, changes []*Change, snapshots []*Change, indexes []int) (filteredHeads bool, filtered, filteredSnapshots []*Change, newIndexes []int) {
if !v.shouldFilter {
return false, changes, snapshots, indexes
}
aclList.RLock()
defer aclList.RUnlock()
state := aclList.AclState()
for idx, c := range changes {
// only taking changes which we can read
if keys, exists := state.Keys()[c.ReadKeyId]; exists && keys.ReadKey != nil {
newIndexes = append(newIndexes, indexes[idx])
filtered = append(filtered, c)
if c.IsSnapshot {
filteredSnapshots = append(filteredSnapshots, c)
}
} else {
// if we filtered at least one change this can be the change between heads and other changes
// thus we cannot use heads
filteredHeads = true
}
}
return
}
func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c *Change) (err error) { func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c *Change) (err error) {
var ( var (
perms list.AclPermissions perms list.AclPermissions
@ -103,7 +153,7 @@ func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c
return return
} }
func ValidateRawTreeBuildFunc(payload treestorage.TreeStorageCreatePayload, buildFunc BuildObjectTreeFunc, aclList list.AclList) (err error) { func ValidateRawTreeBuildFunc(payload treestorage.TreeStorageCreatePayload, buildFunc BuildObjectTreeFunc, aclList list.AclList) (newPayload treestorage.TreeStorageCreatePayload, err error) {
treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, []string{payload.RootRawChange.Id}, nil) treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, []string{payload.RootRawChange.Id}, nil)
if err != nil { if err != nil {
return return
@ -120,15 +170,48 @@ func ValidateRawTreeBuildFunc(payload treestorage.TreeStorageCreatePayload, buil
return return
} }
if !slice.UnsortedEquals(res.Heads, payload.Heads) { if !slice.UnsortedEquals(res.Heads, payload.Heads) {
return ErrHasInvalidChanges return payload, ErrHasInvalidChanges
} }
// if tree has only one change we still should check if the snapshot id is same as root // if tree has only one change we still should check if the snapshot id is same as root
if IsEmptyDerivedTree(tree) { if IsEmptyDerivedTree(tree) {
return ErrDerived return payload, ErrDerived
} }
return return payload, nil
}
func ValidateFilterRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList) (retPayload treestorage.TreeStorageCreatePayload, err error) {
aclList.RLock()
if !aclList.AclState().HadReadPermissions(aclList.AclState().Identity()) {
aclList.RUnlock()
return payload, list.ErrNoReadKey
}
aclList.RUnlock()
treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, []string{payload.RootRawChange.Id}, nil)
if err != nil {
return
}
tree, err := BuildKeyFilterableObjectTree(treeStorage, aclList)
if err != nil {
return
}
res, err := tree.AddRawChanges(context.Background(), RawChangesPayload{
NewHeads: payload.Heads,
RawChanges: payload.Changes,
})
if err != nil {
return
}
if IsEmptyTree(tree) {
return payload, ErrNoChangeInTree
}
return treestorage.TreeStorageCreatePayload{
RootRawChange: payload.RootRawChange,
Heads: res.Heads,
Changes: treeStorage.(*treestorage.InMemoryTreeStorage).AllChanges(),
}, nil
} }
func ValidateRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList) (err error) { func ValidateRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList) (err error) {
return ValidateRawTreeBuildFunc(payload, BuildObjectTree, aclList) _, err = ValidateRawTreeBuildFunc(payload, BuildObjectTree, aclList)
return
} }

View file

@ -6,6 +6,8 @@ import (
"errors" "errors"
"time" "time"
"go.uber.org/zap"
"github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
@ -16,7 +18,6 @@ import (
"github.com/anyproto/any-sync/commonspace/syncstatus" "github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/net/peer" "github.com/anyproto/any-sync/net/peer"
"github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/nodeconf"
"go.uber.org/zap"
) )
var ( var (
@ -59,18 +60,19 @@ type ResponsiblePeersGetter interface {
} }
type BuildDeps struct { type BuildDeps struct {
SpaceId string SpaceId string
SyncClient SyncClient SyncClient SyncClient
Configuration nodeconf.NodeConf Configuration nodeconf.NodeConf
HeadNotifiable HeadNotifiable HeadNotifiable HeadNotifiable
Listener updatelistener.UpdateListener Listener updatelistener.UpdateListener
AclList list.AclList AclList list.AclList
SpaceStorage spacestorage.SpaceStorage SpaceStorage spacestorage.SpaceStorage
TreeStorage treestorage.TreeStorage TreeStorage treestorage.TreeStorage
OnClose func(id string) OnClose func(id string)
SyncStatus syncstatus.StatusUpdater SyncStatus syncstatus.StatusUpdater
PeerGetter ResponsiblePeersGetter PeerGetter ResponsiblePeersGetter
BuildObjectTree objecttree.BuildObjectTreeFunc BuildObjectTree objecttree.BuildObjectTreeFunc
ValidateObjectTree objecttree.ValidatorFunc
} }
func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t SyncTree, err error) { func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t SyncTree, err error) {

View file

@ -104,13 +104,17 @@ func (t treeRemoteGetter) getTree(ctx context.Context) (treeStorage treestorage.
Heads: fullSyncResp.Heads, Heads: fullSyncResp.Heads,
} }
validatorFunc := t.deps.ValidateObjectTree
if validatorFunc == nil {
validatorFunc = objecttree.ValidateRawTreeBuildFunc
}
// basically building tree with in-memory storage and validating that it was without errors // basically building tree with in-memory storage and validating that it was without errors
log.With(zap.String("id", t.treeId)).DebugCtx(ctx, "validating tree") log.With(zap.String("id", t.treeId)).DebugCtx(ctx, "validating tree")
err = objecttree.ValidateRawTreeBuildFunc(payload, t.deps.BuildObjectTree, t.deps.AclList) newPayload, err := validatorFunc(payload, t.deps.BuildObjectTree, t.deps.AclList)
if err != nil { if err != nil {
return return
} }
// now we are sure that we can save it to the storage // now we are sure that we can save it to the storage
treeStorage, err = t.deps.SpaceStorage.CreateTreeStorage(payload) treeStorage, err = t.deps.SpaceStorage.CreateTreeStorage(newPayload)
return return
} }

View file

@ -5,7 +5,6 @@
// //
// mockgen -destination mock_treesyncer/mock_treesyncer.go github.com/anyproto/any-sync/commonspace/object/treesyncer TreeSyncer // mockgen -destination mock_treesyncer/mock_treesyncer.go github.com/anyproto/any-sync/commonspace/object/treesyncer TreeSyncer
// //
// Package mock_treesyncer is a generated GoMock package. // Package mock_treesyncer is a generated GoMock package.
package mock_treesyncer package mock_treesyncer
@ -96,6 +95,20 @@ func (mr *MockTreeSyncerMockRecorder) Run(arg0 any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockTreeSyncer)(nil).Run), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockTreeSyncer)(nil).Run), arg0)
} }
// ShouldSync mocks base method.
func (m *MockTreeSyncer) ShouldSync(arg0 string) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ShouldSync", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// ShouldSync indicates an expected call of ShouldSync.
func (mr *MockTreeSyncerMockRecorder) ShouldSync(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShouldSync", reflect.TypeOf((*MockTreeSyncer)(nil).ShouldSync), arg0)
}
// StartSync mocks base method. // StartSync mocks base method.
func (m *MockTreeSyncer) StartSync() { func (m *MockTreeSyncer) StartSync() {
m.ctrl.T.Helper() m.ctrl.T.Helper()
@ -108,6 +121,18 @@ func (mr *MockTreeSyncerMockRecorder) StartSync() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartSync", reflect.TypeOf((*MockTreeSyncer)(nil).StartSync)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartSync", reflect.TypeOf((*MockTreeSyncer)(nil).StartSync))
} }
// StopSync mocks base method.
func (m *MockTreeSyncer) StopSync() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "StopSync")
}
// StopSync indicates an expected call of StopSync.
func (mr *MockTreeSyncerMockRecorder) StopSync() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopSync", reflect.TypeOf((*MockTreeSyncer)(nil).StopSync))
}
// SyncAll mocks base method. // SyncAll mocks base method.
func (m *MockTreeSyncer) SyncAll(arg0 context.Context, arg1 string, arg2, arg3 []string) error { func (m *MockTreeSyncer) SyncAll(arg0 context.Context, arg1 string, arg2, arg3 []string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View file

@ -12,5 +12,7 @@ const CName = "common.object.treesyncer"
type TreeSyncer interface { type TreeSyncer interface {
app.ComponentRunnable app.ComponentRunnable
StartSync() StartSync()
StopSync()
ShouldSync(peerId string) bool
SyncAll(ctx context.Context, peerId string, existing, missing []string) error SyncAll(ctx context.Context, peerId string, existing, missing []string) error
} }

View file

@ -6,6 +6,8 @@ import (
"errors" "errors"
"sync/atomic" "sync/atomic"
"go.uber.org/zap"
"github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/headsync" "github.com/anyproto/any-sync/commonspace/headsync"
@ -23,12 +25,12 @@ import (
"github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/syncstatus" "github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/nodeconf"
"go.uber.org/zap"
) )
type BuildTreeOpts struct { type BuildTreeOpts struct {
Listener updatelistener.UpdateListener Listener updatelistener.UpdateListener
TreeBuilder objecttree.BuildObjectTreeFunc TreeBuilder objecttree.BuildObjectTreeFunc
TreeValidator objecttree.ValidatorFunc
} }
const CName = "common.commonspace.objecttreebuilder" const CName = "common.commonspace.objecttreebuilder"
@ -111,17 +113,18 @@ func (t *treeBuilder) BuildTree(ctx context.Context, id string, opts BuildTreeOp
treeBuilder = t.builder treeBuilder = t.builder
} }
deps := synctree.BuildDeps{ deps := synctree.BuildDeps{
SpaceId: t.spaceId, SpaceId: t.spaceId,
SyncClient: t.syncClient, SyncClient: t.syncClient,
Configuration: t.configuration, Configuration: t.configuration,
HeadNotifiable: t.headsNotifiable, HeadNotifiable: t.headsNotifiable,
Listener: opts.Listener, Listener: opts.Listener,
AclList: t.aclList, AclList: t.aclList,
SpaceStorage: t.spaceStorage, SpaceStorage: t.spaceStorage,
OnClose: t.onClose, OnClose: t.onClose,
SyncStatus: t.syncStatus, SyncStatus: t.syncStatus,
PeerGetter: t.peerManager, PeerGetter: t.peerManager,
BuildObjectTree: treeBuilder, BuildObjectTree: treeBuilder,
ValidateObjectTree: opts.TreeValidator,
} }
t.treesUsed.Add(1) t.treesUsed.Add(1)
t.log.Debug("incrementing counter", zap.String("id", id), zap.Int32("trees", t.treesUsed.Load())) t.log.Debug("incrementing counter", zap.String("id", id), zap.Int32("trees", t.treesUsed.Load()))

View file

@ -4,9 +4,13 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/anyproto/any-sync/commonspace/deletionmanager" "github.com/anyproto/any-sync/commonspace/deletionmanager"
"github.com/anyproto/any-sync/util/crypto" "github.com/anyproto/any-sync/util/crypto"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
"github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
@ -18,8 +22,6 @@ import (
"github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto" "github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/nodeconf"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
) )
var log = logger.NewNamed("common.commonspace.settings") var log = logger.NewNamed("common.commonspace.settings")
@ -166,7 +168,10 @@ func (s *settingsObject) checkHistoryState(ctx context.Context) (err error) {
} }
func (s *settingsObject) Close() error { func (s *settingsObject) Close() error {
return s.SyncTree.Close() if s.SyncTree != nil {
return s.SyncTree.Close()
}
return nil
} }
var isDerivedRoot = objecttree.IsDerivedRoot var isDerivedRoot = objecttree.IsDerivedRoot

View file

@ -6,6 +6,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/anyproto/go-chash"
"github.com/stretchr/testify/require"
accountService "github.com/anyproto/any-sync/accountservice" accountService "github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/ocache" "github.com/anyproto/any-sync/app/ocache"
@ -23,8 +26,6 @@ import (
"github.com/anyproto/any-sync/net/pool" "github.com/anyproto/any-sync/net/pool"
"github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/nodeconf"
"github.com/anyproto/any-sync/testutil/accounttest" "github.com/anyproto/any-sync/testutil/accounttest"
"github.com/anyproto/go-chash"
"github.com/stretchr/testify/require"
) )
// //
@ -282,6 +283,10 @@ func (n noOpSyncer) Close() error {
type mockTreeSyncer struct { type mockTreeSyncer struct {
} }
func (m mockTreeSyncer) ShouldSync(peerId string) bool {
return false
}
func (m mockTreeSyncer) Init(a *app.App) (err error) { func (m mockTreeSyncer) Init(a *app.App) (err error) {
return nil return nil
} }
@ -301,6 +306,9 @@ func (m mockTreeSyncer) Close(ctx context.Context) (err error) {
func (m mockTreeSyncer) StartSync() { func (m mockTreeSyncer) StartSync() {
} }
func (m mockTreeSyncer) StopSync() {
}
func (m mockTreeSyncer) SyncAll(ctx context.Context, peerId string, existing, missing []string) error { func (m mockTreeSyncer) SyncAll(ctx context.Context, peerId string, existing, missing []string) error {
return nil return nil
} }

View file

@ -99,6 +99,31 @@ func (SpaceStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d94f6f99586adae2, []int{1} return fileDescriptor_d94f6f99586adae2, []int{1}
} }
type SpacePermissions int32
const (
SpacePermissions_SpacePermissionsUnknown SpacePermissions = 0
SpacePermissions_SpacePermissionsOwner SpacePermissions = 1
)
var SpacePermissions_name = map[int32]string{
0: "SpacePermissionsUnknown",
1: "SpacePermissionsOwner",
}
var SpacePermissions_value = map[string]int32{
"SpacePermissionsUnknown": 0,
"SpacePermissionsOwner": 1,
}
func (x SpacePermissions) String() string {
return proto.EnumName(SpacePermissions_name, int32(x))
}
func (SpacePermissions) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d94f6f99586adae2, []int{2}
}
// NodeType determines the type of API that a node supports // NodeType determines the type of API that a node supports
type NodeType int32 type NodeType int32
@ -142,7 +167,7 @@ func (x NodeType) String() string {
} }
func (NodeType) EnumDescriptor() ([]byte, []int) { func (NodeType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d94f6f99586adae2, []int{2} return fileDescriptor_d94f6f99586adae2, []int{3}
} }
// DeletionChangeType determines the type of deletion payload // DeletionChangeType determines the type of deletion payload
@ -171,7 +196,7 @@ func (x DeletionPayloadType) String() string {
} }
func (DeletionPayloadType) EnumDescriptor() ([]byte, []int) { func (DeletionPayloadType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d94f6f99586adae2, []int{3} return fileDescriptor_d94f6f99586adae2, []int{4}
} }
type DeletionLogRecordStatus int32 type DeletionLogRecordStatus int32
@ -202,7 +227,7 @@ func (x DeletionLogRecordStatus) String() string {
} }
func (DeletionLogRecordStatus) EnumDescriptor() ([]byte, []int) { func (DeletionLogRecordStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_d94f6f99586adae2, []int{4} return fileDescriptor_d94f6f99586adae2, []int{5}
} }
type SpaceSignRequest struct { type SpaceSignRequest struct {
@ -287,8 +312,9 @@ func (m *SpaceSignRequest) GetForceRequest() bool {
} }
type SpaceStatusPayload struct { type SpaceStatusPayload struct {
Status SpaceStatus `protobuf:"varint,1,opt,name=status,proto3,enum=coordinator.SpaceStatus" json:"status,omitempty"` Status SpaceStatus `protobuf:"varint,1,opt,name=status,proto3,enum=coordinator.SpaceStatus" json:"status,omitempty"`
DeletionTimestamp int64 `protobuf:"varint,2,opt,name=deletionTimestamp,proto3" json:"deletionTimestamp,omitempty"` DeletionTimestamp int64 `protobuf:"varint,2,opt,name=deletionTimestamp,proto3" json:"deletionTimestamp,omitempty"`
Permissions SpacePermissions `protobuf:"varint,3,opt,name=permissions,proto3,enum=coordinator.SpacePermissions" json:"permissions,omitempty"`
} }
func (m *SpaceStatusPayload) Reset() { *m = SpaceStatusPayload{} } func (m *SpaceStatusPayload) Reset() { *m = SpaceStatusPayload{} }
@ -338,6 +364,13 @@ func (m *SpaceStatusPayload) GetDeletionTimestamp() int64 {
return 0 return 0
} }
func (m *SpaceStatusPayload) GetPermissions() SpacePermissions {
if m != nil {
return m.Permissions
}
return SpacePermissions_SpacePermissionsUnknown
}
type SpaceSignResponse struct { type SpaceSignResponse struct {
Receipt *SpaceReceiptWithSignature `protobuf:"bytes,1,opt,name=receipt,proto3" json:"receipt,omitempty"` Receipt *SpaceReceiptWithSignature `protobuf:"bytes,1,opt,name=receipt,proto3" json:"receipt,omitempty"`
} }
@ -1995,6 +2028,7 @@ func (m *AclGetRecordsResponse) GetRecords() [][]byte {
func init() { func init() {
proto.RegisterEnum("coordinator.ErrorCodes", ErrorCodes_name, ErrorCodes_value) proto.RegisterEnum("coordinator.ErrorCodes", ErrorCodes_name, ErrorCodes_value)
proto.RegisterEnum("coordinator.SpaceStatus", SpaceStatus_name, SpaceStatus_value) proto.RegisterEnum("coordinator.SpaceStatus", SpaceStatus_name, SpaceStatus_value)
proto.RegisterEnum("coordinator.SpacePermissions", SpacePermissions_name, SpacePermissions_value)
proto.RegisterEnum("coordinator.NodeType", NodeType_name, NodeType_value) proto.RegisterEnum("coordinator.NodeType", NodeType_name, NodeType_value)
proto.RegisterEnum("coordinator.DeletionPayloadType", DeletionPayloadType_name, DeletionPayloadType_value) proto.RegisterEnum("coordinator.DeletionPayloadType", DeletionPayloadType_name, DeletionPayloadType_value)
proto.RegisterEnum("coordinator.DeletionLogRecordStatus", DeletionLogRecordStatus_name, DeletionLogRecordStatus_value) proto.RegisterEnum("coordinator.DeletionLogRecordStatus", DeletionLogRecordStatus_name, DeletionLogRecordStatus_value)
@ -2037,105 +2071,109 @@ func init() {
} }
var fileDescriptor_d94f6f99586adae2 = []byte{ var fileDescriptor_d94f6f99586adae2 = []byte{
// 1567 bytes of a gzipped FileDescriptorProto // 1626 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0xcf, 0x73, 0xd3, 0xc6, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0xcf, 0x6f, 0x13, 0xc7,
0x17, 0xb7, 0x64, 0x3b, 0xb1, 0x9f, 0x83, 0x51, 0x36, 0x3f, 0xf0, 0xd7, 0x18, 0x63, 0x16, 0xbe, 0x17, 0xf7, 0x6e, 0x9c, 0xc4, 0x7e, 0x0e, 0x66, 0x33, 0xf9, 0x81, 0x31, 0xc6, 0x84, 0x85, 0x2f,
0xd4, 0xb8, 0x1d, 0xa0, 0x66, 0xda, 0x29, 0x43, 0x0f, 0x84, 0x40, 0x69, 0xf8, 0x11, 0x32, 0x0a, 0xdf, 0xe0, 0xef, 0x57, 0x40, 0x8d, 0x5a, 0x15, 0x51, 0xa9, 0x84, 0x40, 0x69, 0xf8, 0x11, 0xa2,
0xa1, 0xd3, 0x72, 0xe8, 0x08, 0x69, 0xe3, 0x68, 0x62, 0x4b, 0xae, 0xb4, 0x06, 0xfc, 0x5f, 0xf4, 0x0d, 0xa1, 0x6a, 0x39, 0x54, 0xcb, 0xee, 0xc4, 0x59, 0xc5, 0x9e, 0x75, 0x67, 0xc7, 0x84, 0xfc,
0xd6, 0x53, 0xef, 0x9d, 0x69, 0x0f, 0x9d, 0x5e, 0x7a, 0xe8, 0x4c, 0xcf, 0x3d, 0x72, 0xec, 0x91, 0x17, 0xbd, 0xf5, 0xd4, 0x7b, 0xa5, 0xf6, 0x50, 0x55, 0xaa, 0x7a, 0xa8, 0xd4, 0x73, 0x8f, 0x1c,
0x81, 0x6b, 0xff, 0x88, 0xce, 0xae, 0x56, 0xd2, 0xea, 0x87, 0x9d, 0x74, 0x38, 0x70, 0x49, 0xbc, 0x7b, 0x44, 0x70, 0xed, 0x1f, 0x51, 0xcd, 0xec, 0xec, 0xee, 0xec, 0x0f, 0x3b, 0xa9, 0x38, 0x70,
0x9f, 0x7d, 0xef, 0xed, 0xfb, 0xb5, 0xef, 0xbd, 0x15, 0x7c, 0x62, 0xba, 0xae, 0x67, 0xd9, 0x8e, 0x49, 0x3c, 0x9f, 0x79, 0xef, 0xcd, 0xfb, 0x35, 0xef, 0xcd, 0x5b, 0xf8, 0xd0, 0xf1, 0x7d, 0xea,
0x41, 0x5d, 0xef, 0x8a, 0xf4, 0x7b, 0xec, 0xb9, 0xd4, 0xbd, 0xc2, 0xff, 0xfa, 0x32, 0x7e, 0x99, 0x7a, 0xc4, 0x66, 0x3e, 0xbd, 0xaa, 0xfc, 0x1e, 0x52, 0x9f, 0xf9, 0x57, 0xc5, 0xdf, 0x40, 0xc5,
0x43, 0xa8, 0x26, 0x41, 0xf8, 0x4f, 0x05, 0xb4, 0xdd, 0xb1, 0x61, 0x92, 0x5d, 0x7b, 0xe0, 0xe8, 0xaf, 0x08, 0x08, 0xd5, 0x14, 0xc8, 0xfc, 0x43, 0x03, 0x63, 0x7b, 0x68, 0x3b, 0x78, 0xdb, 0xeb,
0xe4, 0xbb, 0x09, 0xf1, 0x29, 0x6a, 0xc0, 0xa2, 0xcf, 0xb0, 0x2d, 0xab, 0xa1, 0x74, 0x94, 0x6e, 0x11, 0x0b, 0x7f, 0x33, 0xc2, 0x01, 0x43, 0x0d, 0x98, 0x0d, 0x38, 0xb6, 0xe1, 0x36, 0xb4, 0x15,
0x55, 0x0f, 0x97, 0x68, 0x1d, 0x16, 0x0e, 0x88, 0x61, 0x11, 0xaf, 0xa1, 0x76, 0x94, 0xee, 0x92, 0x6d, 0xb5, 0x6a, 0x45, 0x4b, 0xb4, 0x0c, 0x33, 0x7b, 0xd8, 0x76, 0x31, 0x6d, 0xe8, 0x2b, 0xda,
0x2e, 0x56, 0xa8, 0x03, 0x35, 0x77, 0x68, 0x6d, 0x59, 0xc4, 0xa1, 0x36, 0x9d, 0x36, 0x8a, 0x7c, 0xea, 0x9c, 0x25, 0x57, 0x68, 0x05, 0x6a, 0x7e, 0xdf, 0xdd, 0x70, 0x31, 0x61, 0x1e, 0x3b, 0x6c,
0x53, 0x86, 0x50, 0x1f, 0x56, 0x1d, 0xf2, 0x22, 0x5c, 0xb2, 0xd3, 0x0c, 0x3a, 0xf1, 0x48, 0xa3, 0x4c, 0x89, 0x4d, 0x15, 0x42, 0x5d, 0x58, 0x24, 0xf8, 0x20, 0x5a, 0xf2, 0xd3, 0x6c, 0x36, 0xa2,
0xc4, 0x49, 0x73, 0xf7, 0x10, 0x86, 0xa5, 0x7d, 0xd7, 0x33, 0x89, 0xd0, 0xab, 0x51, 0xee, 0x28, 0xb8, 0x51, 0x16, 0xa4, 0x85, 0x7b, 0xc8, 0x84, 0xb9, 0x5d, 0x9f, 0x3a, 0x58, 0xea, 0xd5, 0x98,
0xdd, 0x8a, 0x9e, 0xc0, 0x30, 0x05, 0x14, 0xe8, 0x4f, 0x0d, 0x3a, 0xf1, 0x77, 0x8c, 0xe9, 0xd0, 0x5e, 0xd1, 0x56, 0x2b, 0x56, 0x0a, 0x33, 0x7f, 0xd5, 0x00, 0x85, 0x06, 0x30, 0x9b, 0x8d, 0x82,
0x35, 0x2c, 0x74, 0x15, 0x16, 0x7c, 0x0e, 0x70, 0x03, 0xea, 0xfd, 0xc6, 0x65, 0xd9, 0x0f, 0x12, 0x2d, 0xfb, 0xb0, 0xef, 0xdb, 0x2e, 0xba, 0x06, 0x33, 0x81, 0x00, 0x84, 0x05, 0xf5, 0x6e, 0xe3,
0x83, 0x2e, 0xe8, 0xd0, 0x47, 0xb0, 0x6c, 0x91, 0x21, 0xa1, 0xb6, 0xeb, 0x3c, 0xb6, 0x47, 0xc4, 0x8a, 0xea, 0x08, 0x85, 0xc1, 0x92, 0x74, 0xe8, 0xff, 0x30, 0xef, 0xe2, 0x3e, 0x66, 0x9e, 0x4f,
0xa7, 0xc6, 0x68, 0xcc, 0x8d, 0x2c, 0xea, 0xd9, 0x0d, 0xbc, 0x07, 0xcb, 0x92, 0xd7, 0xfc, 0xb1, 0x9e, 0x78, 0x03, 0x1c, 0x30, 0x7b, 0x30, 0x14, 0x56, 0x4e, 0x59, 0xf9, 0x0d, 0xf4, 0x29, 0xd4,
0xeb, 0xf8, 0x04, 0xdd, 0x84, 0x45, 0x8f, 0x98, 0xc4, 0x1e, 0x53, 0x7e, 0x6a, 0xad, 0x7f, 0x31, 0x86, 0x98, 0x0e, 0xbc, 0x20, 0xf0, 0x7c, 0x12, 0x08, 0x83, 0xeb, 0xdd, 0xb3, 0xf9, 0x43, 0xb6,
0x7b, 0xaa, 0x1e, 0x10, 0x7c, 0x65, 0xd3, 0x83, 0xc8, 0x4e, 0x3d, 0x64, 0xc3, 0x87, 0xf0, 0xbf, 0x12, 0x22, 0x4b, 0xe5, 0x30, 0x77, 0x60, 0x5e, 0xf1, 0x7b, 0x30, 0xf4, 0x49, 0x80, 0xd1, 0x2d,
0x99, 0x54, 0xe8, 0x2a, 0xac, 0xf8, 0xd2, 0xa6, 0x30, 0x95, 0x1f, 0xb5, 0xa4, 0xe7, 0x6d, 0xa1, 0x98, 0xa5, 0xd8, 0xc1, 0xde, 0x90, 0x09, 0xb5, 0x6b, 0xdd, 0x4b, 0x79, 0x89, 0x56, 0x48, 0xf0,
0x16, 0x54, 0xfd, 0xc8, 0xd1, 0x41, 0xc0, 0x62, 0x00, 0xff, 0xa4, 0xc0, 0x92, 0x7c, 0xda, 0xfc, 0x85, 0xc7, 0xf6, 0x62, 0x4f, 0x59, 0x11, 0x9b, 0xb9, 0x0f, 0xa7, 0xc7, 0x52, 0xa1, 0x6b, 0xb0,
0xb0, 0x8f, 0x09, 0xf1, 0xb6, 0x2c, 0x2e, 0xa5, 0xaa, 0x8b, 0x15, 0xea, 0xc2, 0x49, 0xc3, 0x34, 0x10, 0x28, 0x9b, 0xd2, 0x57, 0xe2, 0xa8, 0x39, 0xab, 0x68, 0x0b, 0xb5, 0xa0, 0x1a, 0xc4, 0xa1,
0xdd, 0x89, 0x43, 0x53, 0xa1, 0x4f, 0xc3, 0x4c, 0x15, 0x87, 0xd0, 0x17, 0xae, 0x77, 0xb8, 0x65, 0x0a, 0x43, 0x9e, 0x00, 0xe6, 0x0f, 0x1a, 0xcc, 0xa9, 0xa7, 0x4d, 0x4e, 0x9c, 0x21, 0xc6, 0x74,
0xf1, 0x98, 0x57, 0xf5, 0x18, 0x40, 0x6d, 0x80, 0xe7, 0xc6, 0xd0, 0xb6, 0xf6, 0x1c, 0x6a, 0x0f, 0xc3, 0x15, 0x52, 0xaa, 0x96, 0x5c, 0xa1, 0x55, 0x38, 0x69, 0x3b, 0x8e, 0x3f, 0x22, 0x2c, 0x93,
0x79, 0x98, 0x4b, 0xba, 0x84, 0xe0, 0xa7, 0xb0, 0xf6, 0x85, 0x3d, 0x24, 0x0f, 0xec, 0x91, 0x4d, 0x3c, 0x59, 0x98, 0xab, 0x42, 0x30, 0x3b, 0xf0, 0xe9, 0xfe, 0x86, 0x2b, 0xb2, 0xa6, 0x6a, 0x25,
0x37, 0x0f, 0x88, 0x79, 0x18, 0x66, 0x6a, 0x8e, 0x02, 0x4a, 0xbe, 0x02, 0x92, 0x71, 0x6a, 0xc2, 0x00, 0x6a, 0x03, 0xbc, 0xb0, 0xfb, 0x9e, 0xbb, 0x43, 0x98, 0xd7, 0x17, 0x89, 0x52, 0xb6, 0x14,
0x38, 0xbc, 0x0d, 0xeb, 0x69, 0xe1, 0x22, 0xa0, 0xab, 0x50, 0x1e, 0x32, 0x94, 0xcb, 0x2c, 0xe9, 0xc4, 0x7c, 0x06, 0x4b, 0x9f, 0x79, 0x7d, 0xfc, 0xd0, 0x1b, 0x78, 0x6c, 0x7d, 0x0f, 0x3b, 0xfb,
0xc1, 0x82, 0x29, 0xeb, 0x53, 0xd7, 0x33, 0x06, 0xe4, 0x3e, 0x99, 0x0a, 0x61, 0x12, 0x82, 0xaf, 0x51, 0xae, 0x17, 0x28, 0xa0, 0x15, 0x2b, 0xa0, 0x18, 0xa7, 0xa7, 0x8c, 0x33, 0x37, 0x61, 0x39,
0xc1, 0x29, 0x29, 0xc1, 0x12, 0xea, 0xce, 0xf4, 0x30, 0xde, 0x83, 0x46, 0x96, 0x49, 0xa8, 0x71, 0x2b, 0x5c, 0x06, 0x74, 0x11, 0xa6, 0xfb, 0x1c, 0x15, 0x32, 0xcb, 0x56, 0xb8, 0xe0, 0xca, 0x06,
0x1d, 0x16, 0xc7, 0x52, 0xb0, 0x6b, 0xfd, 0xb3, 0xb3, 0xb2, 0x59, 0x04, 0x5e, 0x0f, 0xe9, 0xf1, 0xcc, 0xa7, 0x76, 0x0f, 0x3f, 0xc0, 0x87, 0x52, 0x98, 0x82, 0x98, 0xd7, 0xe1, 0x94, 0x92, 0xa1,
0x75, 0x38, 0x9d, 0x16, 0xfb, 0xd0, 0x70, 0xa6, 0xa1, 0x3e, 0x4d, 0xa8, 0x08, 0x05, 0xd8, 0x45, 0x29, 0x75, 0xc7, 0x7a, 0xd8, 0xdc, 0x81, 0x46, 0x9e, 0x49, 0xaa, 0x71, 0x03, 0x66, 0x87, 0x4a,
0x29, 0x76, 0xab, 0x7a, 0xb4, 0xc6, 0x4f, 0xa1, 0x95, 0xcf, 0x2a, 0xb4, 0xba, 0x01, 0x15, 0x71, 0xb0, 0x6b, 0xdd, 0x73, 0xe3, 0xae, 0x83, 0x0c, 0xbc, 0x15, 0xd1, 0x9b, 0x37, 0xe0, 0x4c, 0x56,
0x4a, 0xc0, 0x7b, 0x0c, 0xb5, 0x22, 0x06, 0xfc, 0x5a, 0x49, 0xd9, 0x6b, 0x38, 0x03, 0x72, 0x74, 0xec, 0x23, 0x9b, 0x1c, 0x46, 0xfa, 0x34, 0xa1, 0x22, 0x15, 0xe0, 0x37, 0x6d, 0x6a, 0xb5, 0x6a,
0xf9, 0x91, 0x2e, 0xa9, 0x90, 0x19, 0x85, 0x33, 0xbb, 0xc1, 0x92, 0x23, 0x05, 0x86, 0xd9, 0x99, 0xc5, 0x6b, 0xf3, 0x19, 0xb4, 0x8a, 0x59, 0xa5, 0x56, 0x37, 0xa1, 0x22, 0x4f, 0x09, 0x79, 0x8f,
0x82, 0x91, 0x0e, 0x2b, 0x29, 0xe8, 0xf1, 0x74, 0x1c, 0xd4, 0xa6, 0x7a, 0xbf, 0x93, 0x30, 0xeb, 0xa1, 0x56, 0xcc, 0x60, 0xbe, 0xd6, 0x32, 0xf6, 0xda, 0xa4, 0x87, 0x8f, 0x2e, 0x60, 0xca, 0x2d,
0x76, 0x96, 0x4e, 0xcf, 0x63, 0xc6, 0x4f, 0xc4, 0x5d, 0x4e, 0x5a, 0xf8, 0xee, 0x21, 0xbd, 0x01, 0x97, 0x32, 0xe3, 0x70, 0xe6, 0x37, 0x78, 0x72, 0x64, 0xc0, 0x28, 0x3b, 0x33, 0x30, 0xb2, 0x60,
0xa7, 0xb7, 0x83, 0x8b, 0xb3, 0xe9, 0x3a, 0xfb, 0xf6, 0x60, 0xe2, 0x19, 0xec, 0xe8, 0xd0, 0x79, 0x21, 0x03, 0x3d, 0x39, 0x1c, 0x86, 0xd5, 0xad, 0xde, 0x5d, 0x49, 0x99, 0x75, 0x27, 0x4f, 0x67,
0x2d, 0xa8, 0x9a, 0x13, 0xcf, 0x23, 0x2c, 0xf5, 0x85, 0xfb, 0x62, 0x00, 0xff, 0xa1, 0x40, 0x2b, 0x15, 0x31, 0x9b, 0x4f, 0xe5, 0x5d, 0x4e, 0x5b, 0xf8, 0xee, 0x21, 0xbd, 0x09, 0x67, 0x36, 0xc3,
0x9f, 0x5b, 0x28, 0xd6, 0x85, 0x93, 0xa6, 0xbc, 0x11, 0x09, 0x49, 0xc3, 0xc9, 0x1b, 0xad, 0xa6, 0x8b, 0xb3, 0xee, 0x93, 0x5d, 0xaf, 0x37, 0xa2, 0x36, 0x3f, 0x3a, 0x72, 0x5e, 0x0b, 0xaa, 0xce,
0x6f, 0xf4, 0x07, 0x50, 0x76, 0x5c, 0x8b, 0xf8, 0x8d, 0x22, 0x4f, 0x8d, 0xe5, 0x84, 0x79, 0xdb, 0x88, 0x52, 0xcc, 0x53, 0x5f, 0xba, 0x2f, 0x01, 0xcc, 0xdf, 0x35, 0x68, 0x15, 0x73, 0x4b, 0xc5,
0xae, 0x45, 0xf4, 0x60, 0x1f, 0xf5, 0x40, 0x33, 0x3d, 0x62, 0x84, 0xe5, 0x75, 0xcf, 0xb1, 0x5f, 0x56, 0xe1, 0xa4, 0xa3, 0x6e, 0xc4, 0x42, 0xb2, 0x70, 0xfa, 0x46, 0xeb, 0xd9, 0x1b, 0xfd, 0x5f,
0x72, 0xbf, 0x97, 0xf4, 0x0c, 0x8e, 0x6d, 0x28, 0x31, 0x56, 0xa9, 0x1c, 0x29, 0x89, 0x72, 0xd4, 0x98, 0x26, 0xbe, 0x8b, 0x79, 0x6d, 0xe5, 0xa9, 0x31, 0x9f, 0x32, 0x6f, 0xd3, 0x77, 0xb1, 0x15,
0x82, 0xaa, 0x61, 0x59, 0x1e, 0xf1, 0x7d, 0xe2, 0x37, 0x54, 0x9e, 0xcf, 0x31, 0x80, 0x3e, 0x84, 0xee, 0xa3, 0x0e, 0x18, 0x0e, 0xc5, 0x76, 0x54, 0x9f, 0x77, 0x88, 0xf7, 0x52, 0xf8, 0xbd, 0x6c,
0x32, 0x9d, 0x8e, 0x85, 0x4a, 0xf5, 0xfe, 0x5a, 0x46, 0x25, 0x1e, 0xcb, 0x80, 0x06, 0x8f, 0xe0, 0xe5, 0x70, 0xd3, 0x83, 0x32, 0x67, 0x55, 0xca, 0x91, 0x96, 0x2a, 0x47, 0x2d, 0xa8, 0xda, 0xae,
0x7c, 0x18, 0x69, 0xee, 0x28, 0x6f, 0x24, 0x02, 0x91, 0xac, 0xc9, 0x39, 0x29, 0xa6, 0xe4, 0xa7, 0x4b, 0x71, 0x10, 0xe0, 0xa0, 0xa1, 0x8b, 0x7c, 0x4e, 0x00, 0xf4, 0x3f, 0x98, 0x66, 0x87, 0x43,
0xd8, 0xfc, 0x5a, 0xfc, 0xab, 0x02, 0xeb, 0xf9, 0xe7, 0xbd, 0xc7, 0xaa, 0xdc, 0x82, 0x2a, 0x8d, 0xa9, 0x52, 0xbd, 0xbb, 0x94, 0x53, 0x49, 0xc4, 0x32, 0xa4, 0x31, 0x07, 0x70, 0x21, 0x8a, 0xb4,
0x5a, 0x61, 0x99, 0xb7, 0xc2, 0x18, 0xc0, 0xb7, 0x01, 0x85, 0x1a, 0x3f, 0x70, 0x07, 0xd2, 0xdd, 0x70, 0x14, 0x1d, 0xc8, 0x40, 0xa4, 0x6b, 0x72, 0x41, 0x8a, 0x69, 0xc5, 0x29, 0x36, 0xb9, 0x16,
0x35, 0xf6, 0xa9, 0x14, 0x9b, 0x70, 0x19, 0x17, 0x53, 0xa6, 0xec, 0x09, 0x51, 0x4c, 0xb1, 0x0d, 0xff, 0xac, 0xc1, 0x72, 0xf1, 0x79, 0xef, 0xb1, 0x2a, 0xb7, 0xa0, 0xca, 0xe2, 0x5e, 0x3a, 0x2d,
0x2b, 0x09, 0x29, 0x22, 0x0d, 0x3f, 0xe3, 0xad, 0xd4, 0xf5, 0xa2, 0xda, 0xd2, 0xce, 0xbd, 0x84, 0x7a, 0x69, 0x02, 0x98, 0x77, 0x00, 0x45, 0x1a, 0x3f, 0xf4, 0x7b, 0xca, 0xdd, 0xb5, 0x77, 0x99,
0x9c, 0x85, 0x91, 0xe9, 0x21, 0x39, 0x53, 0xe0, 0xc0, 0xf0, 0x1f, 0xba, 0xc2, 0xcb, 0x15, 0x3d, 0x12, 0x9b, 0x68, 0x99, 0x14, 0x53, 0xae, 0xec, 0x09, 0x59, 0x4c, 0x4d, 0x0f, 0x16, 0x52, 0x52,
0x5c, 0xe2, 0xdf, 0x15, 0x58, 0xce, 0x30, 0xa2, 0x3a, 0xa8, 0x76, 0xa8, 0xab, 0x6a, 0x5b, 0xb3, 0x64, 0x1a, 0x7e, 0x2c, 0x5a, 0xa9, 0x4f, 0xe3, 0xda, 0xd2, 0x2e, 0xbc, 0x84, 0x82, 0x85, 0x93,
0xfb, 0x04, 0xfa, 0x3c, 0x9a, 0x29, 0x8a, 0xbc, 0x2e, 0x5c, 0x98, 0xaf, 0x52, 0x6a, 0xbe, 0x48, 0x59, 0x11, 0x39, 0x57, 0x60, 0xcf, 0x0e, 0x1e, 0xf9, 0xd2, 0xcb, 0x15, 0x2b, 0x5a, 0x9a, 0xbf,
0x38, 0xb3, 0x94, 0x72, 0x26, 0xdb, 0xdd, 0xb7, 0x87, 0xe4, 0xae, 0xe7, 0x4e, 0x02, 0x57, 0x57, 0x69, 0x30, 0x9f, 0x63, 0x44, 0x75, 0xd0, 0xbd, 0x48, 0x57, 0xdd, 0x73, 0xc7, 0xf7, 0x09, 0xf4,
0xf5, 0x18, 0xc0, 0xbf, 0x29, 0x62, 0xc8, 0xe1, 0x87, 0xbc, 0xc7, 0x3a, 0xd9, 0x03, 0x2d, 0x84, 0x49, 0xfc, 0x28, 0x09, 0xdf, 0x0b, 0x17, 0x27, 0xab, 0x94, 0x79, 0xa0, 0xa4, 0x9c, 0x59, 0xce,
0x6e, 0x8b, 0x4a, 0x20, 0x6c, 0xc9, 0xe0, 0x78, 0x0b, 0x56, 0x12, 0x3a, 0x8b, 0xc8, 0xf6, 0x61, 0x38, 0x93, 0xef, 0xee, 0x7a, 0x7d, 0x7c, 0x8f, 0xfa, 0xa3, 0xd0, 0xd5, 0x55, 0x2b, 0x01, 0xcc,
0x95, 0xba, 0xb7, 0x04, 0x6a, 0xc5, 0xa3, 0x96, 0xc2, 0xc5, 0xe4, 0xee, 0x61, 0x07, 0x56, 0x37, 0x5f, 0xa2, 0x57, 0x92, 0x38, 0xe4, 0x3d, 0xd6, 0xc9, 0x0e, 0x18, 0x11, 0x74, 0x47, 0x56, 0x02,
0x82, 0xcc, 0x4d, 0x3a, 0x20, 0xd7, 0x4c, 0xe5, 0x3f, 0x98, 0xa9, 0xe6, 0x9a, 0x89, 0x7f, 0x54, 0x69, 0x4b, 0x0e, 0x37, 0x37, 0x60, 0x21, 0xa5, 0xb3, 0x8c, 0x6c, 0x17, 0x16, 0x99, 0x7f, 0x5b,
0xe0, 0x8c, 0x7c, 0x60, 0xf6, 0x52, 0xce, 0xaa, 0x40, 0x39, 0x57, 0x4f, 0x3d, 0xc6, 0xd5, 0x2b, 0xa2, 0x6e, 0xf2, 0x56, 0xd3, 0x84, 0x98, 0xc2, 0x3d, 0x93, 0xc0, 0xe2, 0x5a, 0x98, 0xb9, 0x69,
0xce, 0xbd, 0x7a, 0xe9, 0x6c, 0xc1, 0xf7, 0x61, 0x2d, 0xe5, 0x8f, 0x77, 0x70, 0x6e, 0x1b, 0x5a, 0x07, 0x14, 0x9a, 0xa9, 0xfd, 0x0b, 0x33, 0xf5, 0x42, 0x33, 0xcd, 0xef, 0x35, 0x38, 0xab, 0x1e,
0x42, 0x98, 0x4e, 0x9e, 0x13, 0x2f, 0xb2, 0x38, 0x1c, 0xb0, 0xcf, 0x46, 0xbe, 0x48, 0xef, 0x07, 0x98, 0xbf, 0x94, 0xe3, 0x2a, 0x50, 0xc1, 0xd5, 0xd3, 0x8f, 0x71, 0xf5, 0xa6, 0x26, 0x5e, 0xbd,
0x87, 0xb2, 0x40, 0x6f, 0x98, 0xc3, 0x0d, 0xcb, 0x12, 0x57, 0xf1, 0xc8, 0xec, 0x6c, 0xc4, 0xcd, 0x6c, 0xb6, 0x98, 0x0f, 0x60, 0x29, 0xe3, 0x8f, 0x77, 0x70, 0x6e, 0x1b, 0x5a, 0x52, 0x98, 0x85,
0x2f, 0x70, 0x4e, 0xd4, 0xdb, 0x1e, 0xb0, 0x40, 0xcb, 0xa2, 0x84, 0x5d, 0x4d, 0xa8, 0x04, 0xf7, 0x5f, 0x60, 0x1a, 0x5b, 0x1c, 0x3d, 0xd1, 0xcf, 0xc5, 0xbe, 0xc8, 0xee, 0x87, 0x87, 0xf2, 0x40,
0x3b, 0x12, 0x16, 0xad, 0xe7, 0x48, 0xbb, 0xc7, 0xa5, 0xdd, 0x25, 0x34, 0x90, 0xe6, 0x1f, 0x4b, 0xaf, 0x39, 0xfd, 0x35, 0xd7, 0x95, 0x57, 0xf1, 0xc8, 0xec, 0x6c, 0x24, 0xcd, 0x2f, 0x74, 0x4e,
0x33, 0xc3, 0x1c, 0x7e, 0x49, 0x8c, 0xe8, 0xf2, 0x8b, 0x25, 0xfe, 0x98, 0xb9, 0x3c, 0x21, 0x4b, 0xdc, 0xdb, 0x1e, 0xf2, 0x40, 0xab, 0xa2, 0xa4, 0x5d, 0x4d, 0xa8, 0x84, 0xf7, 0x3b, 0x16, 0x16,
0xa8, 0xd6, 0x48, 0x56, 0xaa, 0xa5, 0xa8, 0x12, 0xf5, 0x7e, 0x56, 0x00, 0xee, 0x78, 0x9e, 0xeb, 0xaf, 0x27, 0x48, 0xbb, 0x2f, 0xa4, 0xdd, 0xc3, 0x2c, 0x94, 0x16, 0x1c, 0x4b, 0x33, 0xdb, 0xe9,
0x6d, 0xf2, 0x46, 0x57, 0x07, 0xd8, 0x73, 0xc8, 0xcb, 0x31, 0x31, 0x29, 0xb1, 0xb4, 0x02, 0xd2, 0x7f, 0x8e, 0xed, 0xf8, 0xf2, 0xcb, 0xa5, 0xf9, 0x01, 0x77, 0x79, 0x4a, 0x96, 0x54, 0xad, 0x91,
0xc4, 0xf4, 0x2d, 0x02, 0xa2, 0x29, 0xa8, 0x01, 0xab, 0x31, 0xc2, 0xd2, 0x91, 0x38, 0x96, 0xed, 0xae, 0x54, 0x73, 0x71, 0x25, 0xea, 0xfc, 0xa8, 0x01, 0xdc, 0xa5, 0xd4, 0xa7, 0xeb, 0xa2, 0xd1,
0x0c, 0x34, 0x35, 0xa2, 0xdd, 0x64, 0x1d, 0x91, 0x58, 0x5a, 0x11, 0x21, 0xa8, 0x73, 0x64, 0xdb, 0xd5, 0x01, 0x76, 0x08, 0x7e, 0x39, 0xc4, 0x0e, 0xc3, 0xae, 0x51, 0x42, 0x86, 0x7c, 0x7d, 0xcb,
0xa5, 0x77, 0x5e, 0xda, 0x3e, 0xf5, 0xb5, 0x12, 0x5a, 0x13, 0x8f, 0x12, 0x3e, 0xc9, 0xea, 0xc4, 0x80, 0x18, 0x1a, 0x6a, 0xc0, 0x62, 0x82, 0xf0, 0x74, 0xc4, 0xc4, 0xf5, 0x48, 0xcf, 0xd0, 0x63,
0x30, 0x0f, 0x88, 0xa5, 0x95, 0x19, 0x69, 0x22, 0x5b, 0x2c, 0x6d, 0x01, 0x69, 0x50, 0xe3, 0xaa, 0xda, 0x75, 0xde, 0x11, 0xb1, 0x6b, 0x4c, 0x21, 0x04, 0x75, 0x81, 0x6c, 0xfa, 0xec, 0xee, 0x4b,
0x3d, 0xda, 0xdf, 0xf7, 0x09, 0xd5, 0x7e, 0x51, 0x7b, 0x3f, 0x28, 0x50, 0x93, 0xc6, 0x0e, 0xb4, 0x2f, 0x60, 0x81, 0x51, 0x46, 0x4b, 0x72, 0x28, 0x11, 0x2f, 0x59, 0x0b, 0xdb, 0xce, 0x1e, 0x76,
0x9e, 0x78, 0x57, 0x85, 0x07, 0x17, 0x50, 0x1b, 0x9a, 0xf2, 0x74, 0x12, 0xa8, 0x18, 0x6a, 0xac, 0x8d, 0x69, 0x4e, 0x9a, 0xca, 0x16, 0xd7, 0x98, 0x41, 0x06, 0xd4, 0x84, 0x6a, 0x8f, 0x77, 0x77,
0x29, 0xa9, 0xfd, 0x70, 0x63, 0x97, 0x1a, 0x1e, 0xe3, 0x57, 0x53, 0x72, 0x43, 0x8d, 0x8a, 0x91, 0x03, 0xcc, 0x8c, 0x9f, 0xf4, 0xce, 0x77, 0x1a, 0xd4, 0x94, 0x67, 0x07, 0x5a, 0x4e, 0x0d, 0x66,
0xf1, 0x01, 0x2e, 0x99, 0xd5, 0x7b, 0x01, 0x95, 0xb0, 0x3b, 0xa3, 0x1a, 0x2c, 0x3e, 0xf6, 0x08, 0xd1, 0xc1, 0x25, 0xd4, 0x86, 0xa6, 0xfa, 0x3a, 0x09, 0x55, 0x8c, 0x34, 0x36, 0xb4, 0xcc, 0x7e,
0xd9, 0xd8, 0xd9, 0xd2, 0x0a, 0x6c, 0xc1, 0x06, 0x77, 0xb6, 0x50, 0x98, 0x95, 0x9b, 0x71, 0x39, 0xb4, 0xb1, 0xcd, 0x6c, 0xca, 0xf9, 0xf5, 0x8c, 0xdc, 0x48, 0xa3, 0xa9, 0xd8, 0xf8, 0x10, 0x57,
0x66, 0x18, 0x77, 0xdb, 0x26, 0x0b, 0x92, 0xe3, 0x4f, 0x7c, 0x86, 0x14, 0xd1, 0x32, 0x9c, 0xd8, 0xcc, 0xea, 0xdc, 0x97, 0x33, 0xae, 0x32, 0x8c, 0xa1, 0x33, 0xf2, 0x8d, 0xad, 0x60, 0x3b, 0x64,
0x36, 0x46, 0xb6, 0x33, 0x60, 0x12, 0x19, 0x54, 0x62, 0x07, 0xef, 0x18, 0xd3, 0x11, 0x71, 0xe8, 0x9f, 0xf8, 0x07, 0xc4, 0x28, 0xa1, 0xd3, 0xb0, 0x94, 0xdd, 0x7c, 0x7c, 0x40, 0x30, 0x35, 0xb4,
0x8e, 0xe7, 0x9a, 0xc4, 0xf7, 0x6d, 0x67, 0xc0, 0x76, 0xca, 0xbd, 0xeb, 0x71, 0x6f, 0x92, 0x06, 0xce, 0x01, 0x54, 0xa2, 0x4e, 0x8f, 0x6a, 0x30, 0xfb, 0x84, 0x62, 0xbc, 0xb6, 0xb5, 0x61, 0x94,
0x3b, 0x54, 0x81, 0x12, 0xd3, 0x21, 0x50, 0x40, 0xd4, 0x05, 0x4d, 0x61, 0x0b, 0xe1, 0x66, 0x4d, 0xf8, 0x82, 0x0f, 0x01, 0x7c, 0xa1, 0x71, 0x8f, 0xad, 0x27, 0xa5, 0x9d, 0x63, 0x22, 0x04, 0xeb,
0xed, 0xdd, 0x84, 0x53, 0x33, 0x1a, 0x02, 0x5a, 0x00, 0xf5, 0xd1, 0xa1, 0x56, 0x60, 0xaa, 0xe8, 0x3c, 0xe0, 0x24, 0x18, 0x05, 0x1c, 0x99, 0x42, 0xf3, 0x70, 0x62, 0xd3, 0x1e, 0x78, 0xa4, 0xc7,
0x64, 0xe4, 0x3e, 0x27, 0x3b, 0x1e, 0x19, 0x1b, 0x1e, 0xd1, 0x14, 0x04, 0xb0, 0x10, 0x40, 0x9a, 0x25, 0x72, 0xa8, 0xcc, 0x8d, 0xd8, 0xb2, 0x0f, 0x07, 0x98, 0xb0, 0x2d, 0xea, 0x3b, 0x38, 0x08,
0xda, 0xff, 0xa7, 0x02, 0x35, 0xc9, 0x20, 0x74, 0x0f, 0xaa, 0xd1, 0x8b, 0x13, 0x9d, 0xc9, 0x99, 0x3c, 0xd2, 0xe3, 0x3b, 0xd3, 0x9d, 0x1b, 0x49, 0x9f, 0x53, 0x1e, 0x89, 0xa8, 0x02, 0x65, 0xae,
0x16, 0xe3, 0xf7, 0x7b, 0xb3, 0x3d, 0x6b, 0x5b, 0xe4, 0xec, 0xd7, 0x50, 0x4f, 0xbe, 0x78, 0x10, 0x43, 0xa8, 0x80, 0xac, 0x31, 0x86, 0xc6, 0x17, 0x32, 0x64, 0x86, 0xde, 0xb9, 0x05, 0xa7, 0xc6,
0x4e, 0x70, 0xe4, 0xbe, 0xb5, 0x9a, 0xe7, 0xe7, 0xd2, 0x08, 0xd1, 0xdf, 0x86, 0x9f, 0x13, 0xe2, 0x34, 0x17, 0x34, 0x03, 0xfa, 0xe3, 0x7d, 0xa3, 0xc4, 0x55, 0xb1, 0xf0, 0xc0, 0x7f, 0x81, 0xb7,
0x57, 0x03, 0xba, 0x30, 0x6b, 0xb6, 0x4d, 0x88, 0xff, 0xff, 0x11, 0x54, 0xe2, 0x80, 0xc3, 0x44, 0x28, 0x1e, 0xda, 0x14, 0x1b, 0x1a, 0x02, 0x98, 0x09, 0x21, 0x43, 0xef, 0xfe, 0x5d, 0x81, 0x9a,
0x9e, 0x44, 0xcf, 0x12, 0xd4, 0x9d, 0xcb, 0x2e, 0x3d, 0x7a, 0x9a, 0x97, 0x8e, 0x41, 0x29, 0x0e, 0x62, 0x10, 0xba, 0x0f, 0xd5, 0x78, 0x7a, 0x45, 0x05, 0x63, 0xaf, 0xf2, 0x35, 0xa1, 0xd9, 0x1e,
0x7b, 0x16, 0x3e, 0xf3, 0xa5, 0x19, 0x1e, 0xcd, 0x51, 0x54, 0x7a, 0xc5, 0x34, 0x2f, 0x1e, 0x45, 0xb7, 0x2d, 0xf3, 0xff, 0x4b, 0xa8, 0xa7, 0xa7, 0x27, 0x64, 0xa6, 0x38, 0x0a, 0xe7, 0xb6, 0xe6,
0x16, 0x1b, 0x94, 0x37, 0x91, 0xa7, 0x0c, 0x9a, 0x33, 0xf2, 0xa7, 0x0c, 0x9a, 0x3b, 0xde, 0xef, 0x85, 0x89, 0x34, 0x52, 0xf4, 0xd7, 0xd1, 0xc7, 0x8d, 0x64, 0x02, 0x41, 0x17, 0xc7, 0xbd, 0x93,
0x40, 0x4d, 0xca, 0x4b, 0x74, 0x76, 0xf6, 0x08, 0x13, 0x88, 0xee, 0xcc, 0x26, 0x88, 0x25, 0x4a, 0x53, 0xe2, 0xff, 0x73, 0x04, 0x95, 0x3c, 0x60, 0x3f, 0x95, 0x73, 0xf1, 0x88, 0x83, 0x56, 0x27,
0x65, 0x0c, 0xe5, 0xbc, 0x63, 0x12, 0x3d, 0x3b, 0x25, 0x31, 0x6f, 0x42, 0x78, 0x02, 0x27, 0x12, 0xb2, 0x2b, 0x03, 0x54, 0xf3, 0xf2, 0x31, 0x28, 0xe5, 0x61, 0xcf, 0xa3, 0x4f, 0x06, 0xca, 0x3c,
0xf5, 0x0a, 0x9d, 0x4b, 0xb0, 0xe4, 0x4d, 0x02, 0x4d, 0x3c, 0x8f, 0x44, 0xc8, 0x75, 0xa2, 0xae, 0x80, 0x26, 0x28, 0xaa, 0x4c, 0x44, 0xcd, 0x4b, 0x47, 0x91, 0x25, 0x06, 0x15, 0xbd, 0xee, 0x33,
0x99, 0x6c, 0x64, 0xe8, 0x52, 0x1e, 0x73, 0x6e, 0x33, 0x6c, 0xf6, 0x8e, 0x43, 0x2a, 0xce, 0xdb, 0x06, 0x4d, 0x18, 0x1f, 0x32, 0x06, 0x4d, 0x1c, 0x15, 0xb6, 0xa0, 0xa6, 0xe4, 0x25, 0x3a, 0x37,
0x85, 0x25, 0xb9, 0x99, 0xa1, 0x4e, 0x8a, 0x37, 0xd3, 0x32, 0x9b, 0xe7, 0xe6, 0x50, 0xc8, 0xce, 0xfe, 0x39, 0x14, 0x8a, 0x5e, 0x19, 0x4f, 0x90, 0x48, 0x54, 0x4a, 0x22, 0x2a, 0x98, 0x89, 0x52,
0x91, 0xfa, 0x50, 0xc6, 0x39, 0xd9, 0x7e, 0x97, 0x71, 0x4e, 0x4e, 0x1b, 0xbb, 0xf5, 0xe9, 0x5f, 0xfd, 0x3f, 0x23, 0xb1, 0xe8, 0xb5, 0xf1, 0x14, 0x4e, 0xa4, 0x6a, 0x1f, 0x3a, 0x9f, 0x62, 0x29,
0x6f, 0xda, 0xca, 0xab, 0x37, 0x6d, 0xe5, 0xf5, 0x9b, 0xb6, 0xf2, 0xfd, 0xdb, 0x76, 0xe1, 0xd5, 0x7a, 0x55, 0x34, 0xcd, 0x49, 0x24, 0x52, 0x2e, 0x89, 0x3b, 0x70, 0xba, 0x29, 0xa2, 0xcb, 0x45,
0xdb, 0x76, 0xe1, 0xef, 0xb7, 0xed, 0xc2, 0x37, 0xad, 0x79, 0x5f, 0x19, 0x9f, 0x2d, 0xf0, 0x7f, 0xcc, 0x85, 0x8d, 0xb5, 0xd9, 0x39, 0x0e, 0xa9, 0x3c, 0x6f, 0x1b, 0xe6, 0xd4, 0xc6, 0x88, 0x56,
0xd7, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xe6, 0x06, 0x8a, 0x8c, 0x14, 0x00, 0x00, 0x32, 0xbc, 0xb9, 0xf6, 0xdb, 0x3c, 0x3f, 0x81, 0x42, 0x75, 0x8e, 0xd2, 0xd3, 0x72, 0xce, 0xc9,
0xf7, 0xce, 0x9c, 0x73, 0x0a, 0x5a, 0xe2, 0xed, 0x8f, 0xfe, 0x7c, 0xd3, 0xd6, 0x5e, 0xbd, 0x69,
0x6b, 0xaf, 0xdf, 0xb4, 0xb5, 0x6f, 0xdf, 0xb6, 0x4b, 0xaf, 0xde, 0xb6, 0x4b, 0x7f, 0xbd, 0x6d,
0x97, 0xbe, 0x6a, 0x4d, 0xfa, 0xe6, 0xf9, 0x7c, 0x46, 0xfc, 0xbb, 0xfe, 0x4f, 0x00, 0x00, 0x00,
0xff, 0xff, 0x83, 0x2d, 0x56, 0x38, 0x1a, 0x15, 0x00, 0x00,
} }
func (m *SpaceSignRequest) Marshal() (dAtA []byte, err error) { func (m *SpaceSignRequest) Marshal() (dAtA []byte, err error) {
@ -2219,6 +2257,11 @@ func (m *SpaceStatusPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.Permissions != 0 {
i = encodeVarintCoordinator(dAtA, i, uint64(m.Permissions))
i--
dAtA[i] = 0x18
}
if m.DeletionTimestamp != 0 { if m.DeletionTimestamp != 0 {
i = encodeVarintCoordinator(dAtA, i, uint64(m.DeletionTimestamp)) i = encodeVarintCoordinator(dAtA, i, uint64(m.DeletionTimestamp))
i-- i--
@ -3453,6 +3496,9 @@ func (m *SpaceStatusPayload) Size() (n int) {
if m.DeletionTimestamp != 0 { if m.DeletionTimestamp != 0 {
n += 1 + sovCoordinator(uint64(m.DeletionTimestamp)) n += 1 + sovCoordinator(uint64(m.DeletionTimestamp))
} }
if m.Permissions != 0 {
n += 1 + sovCoordinator(uint64(m.Permissions))
}
return n return n
} }
@ -4261,6 +4307,25 @@ func (m *SpaceStatusPayload) Unmarshal(dAtA []byte) error {
break break
} }
} }
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType)
}
m.Permissions = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCoordinator
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Permissions |= SpacePermissions(b&0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipCoordinator(dAtA[iNdEx:]) skippy, err := skipCoordinator(dAtA[iNdEx:])

View file

@ -75,9 +75,15 @@ enum SpaceStatus {
SpaceStatusNotExists = 4; SpaceStatusNotExists = 4;
} }
enum SpacePermissions {
SpacePermissionsUnknown = 0;
SpacePermissionsOwner = 1;
}
message SpaceStatusPayload { message SpaceStatusPayload {
SpaceStatus status = 1; SpaceStatus status = 1;
int64 deletionTimestamp = 2; int64 deletionTimestamp = 2;
SpacePermissions permissions = 3;
} }
message SpaceSignResponse { message SpaceSignResponse {