mirror of
https://github.com/anyproto/any-sync.git
synced 2025-06-08 05:57:03 +09:00
Merge branch 'main' of ssh://github.com/anyproto/any-sync into go-5646-live-tech-metrics-ui
This commit is contained in:
commit
a3a3045426
51 changed files with 3467 additions and 673 deletions
|
@ -10,6 +10,7 @@ import (
|
|||
"go.uber.org/zap"
|
||||
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
)
|
||||
|
||||
|
@ -58,7 +59,7 @@ func (a *aclObject) AddConsensusRecords(recs []*consensusproto.RawRecordWithId)
|
|||
if a.store, a.consErr = list.NewInMemoryStorage(a.id, recs); a.consErr != nil {
|
||||
return
|
||||
}
|
||||
if a.AclList, a.consErr = list.BuildAclListWithIdentity(a.aclService.accountService.Account(), a.store, list.NoOpAcceptorVerifier{}); a.consErr != nil {
|
||||
if a.AclList, a.consErr = list.BuildAclListWithIdentity(a.aclService.accountService.Account(), a.store, recordverifier.New()); a.consErr != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -381,17 +381,24 @@ func (d *diff) compareResults(dctx *diffCtx, r Range, myRes, otherRes RangeResul
|
|||
}
|
||||
|
||||
func (d *diff) compareElementsEqual(dctx *diffCtx, my, other []Element) {
|
||||
find := func(list []Element, targetEl Element) (has, eq bool) {
|
||||
for _, el := range list {
|
||||
if el.Id == targetEl.Id {
|
||||
return true, el.Head == targetEl.Head
|
||||
}
|
||||
find := func(list map[string]string, targetEl Element) (has, eq bool) {
|
||||
if head, ok := list[targetEl.Id]; ok {
|
||||
return true, head == targetEl.Head
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
toMap := func(els []Element) map[string]string {
|
||||
result := make(map[string]string, len(els))
|
||||
for _, el := range els {
|
||||
result[el.Id] = el.Head
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
otherMap := toMap(other)
|
||||
for _, el := range my {
|
||||
has, eq := find(other, el)
|
||||
has, eq := find(otherMap, el)
|
||||
if !has {
|
||||
dctx.removedIds = append(dctx.removedIds, el.Id)
|
||||
continue
|
||||
|
@ -402,8 +409,9 @@ func (d *diff) compareElementsEqual(dctx *diffCtx, my, other []Element) {
|
|||
}
|
||||
}
|
||||
|
||||
myMap := toMap(my)
|
||||
for _, el := range other {
|
||||
if has, _ := find(my, el); !has {
|
||||
if has, _ := find(myMap, el); !has {
|
||||
dctx.newIds = append(dctx.newIds, el.Id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/node/nodeclient"
|
||||
)
|
||||
|
@ -20,6 +21,7 @@ type AclJoiningClient interface {
|
|||
AclGetRecords(ctx context.Context, spaceId, aclHead string) ([]*consensusproto.RawRecordWithId, error)
|
||||
RequestJoin(ctx context.Context, spaceId string, payload list.RequestJoinPayload) (aclHeadId string, err error)
|
||||
CancelJoin(ctx context.Context, spaceId string) (err error)
|
||||
InviteJoin(ctx context.Context, spaceId string, payload list.InviteJoinPayload) (aclHeadId string, err error)
|
||||
CancelRemoveSelf(ctx context.Context, spaceId string) (err error)
|
||||
}
|
||||
|
||||
|
@ -59,7 +61,7 @@ func (c *aclJoiningClient) getAcl(ctx context.Context, spaceId string) (l list.A
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
return list.BuildAclListWithIdentity(c.keys, storage, list.NoOpAcceptorVerifier{})
|
||||
return list.BuildAclListWithIdentity(c.keys, storage, recordverifier.New())
|
||||
}
|
||||
|
||||
func (c *aclJoiningClient) CancelJoin(ctx context.Context, spaceId string) (err error) {
|
||||
|
@ -106,6 +108,23 @@ func (c *aclJoiningClient) RequestJoin(ctx context.Context, spaceId string, payl
|
|||
return
|
||||
}
|
||||
|
||||
func (c *aclJoiningClient) InviteJoin(ctx context.Context, spaceId string, payload list.InviteJoinPayload) (aclHeadId string, err error) {
|
||||
acl, err := c.getAcl(ctx, spaceId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rec, err := acl.RecordBuilder().BuildInviteJoin(payload)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
recWithId, err := c.nodeClient.AclAddRecord(ctx, spaceId, rec)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
aclHeadId = recWithId.Id
|
||||
return
|
||||
}
|
||||
|
||||
func (c *aclJoiningClient) CancelRemoveSelf(ctx context.Context, spaceId string) (err error) {
|
||||
acl, err := c.getAcl(ctx, spaceId)
|
||||
if err != nil {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/syncacl"
|
||||
"github.com/anyproto/any-sync/commonspace/spacestate"
|
||||
|
@ -18,6 +19,10 @@ type InviteResponse struct {
|
|||
InviteKey crypto.PrivKey
|
||||
}
|
||||
|
||||
type InviteChange struct {
|
||||
Perms list.AclPermissions
|
||||
}
|
||||
|
||||
type GetRecordsResponse struct {
|
||||
Records []*consensusproto.RawRecordWithId
|
||||
}
|
||||
|
@ -26,7 +31,8 @@ type InviteSaveFunc func()
|
|||
|
||||
type AclSpaceClient interface {
|
||||
app.Component
|
||||
GenerateInvite() (list.InviteResult, error)
|
||||
GenerateInvite(shouldRevokeAll, isRequestToJoin bool, permissions list.AclPermissions) (list.InviteResult, error)
|
||||
ChangeInvite(ctx context.Context, inviteId string, permissions list.AclPermissions) error
|
||||
StopSharing(ctx context.Context, readKeyChange list.ReadKeyChangePayload) (err error)
|
||||
AddRecord(ctx context.Context, consRec *consensusproto.RawRecord) error
|
||||
RemoveAccounts(ctx context.Context, payload list.AccountRemovePayload) error
|
||||
|
@ -127,7 +133,7 @@ func (c *aclSpaceClient) RevokeAllInvites(ctx context.Context) (err error) {
|
|||
return
|
||||
}
|
||||
c.acl.Unlock()
|
||||
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
|
||||
return c.sendRecordAndUpdate(ctx, c.spaceId, res.Rec)
|
||||
}
|
||||
|
||||
func (c *aclSpaceClient) StopSharing(ctx context.Context, readKeyChange list.ReadKeyChangePayload) (err error) {
|
||||
|
@ -164,7 +170,7 @@ func (c *aclSpaceClient) StopSharing(ctx context.Context, readKeyChange list.Rea
|
|||
return
|
||||
}
|
||||
c.acl.Unlock()
|
||||
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
|
||||
return c.sendRecordAndUpdate(ctx, c.spaceId, res.Rec)
|
||||
}
|
||||
|
||||
func (c *aclSpaceClient) DeclineRequest(ctx context.Context, identity crypto.PubKey) (err error) {
|
||||
|
@ -210,10 +216,54 @@ func (c *aclSpaceClient) AcceptRequest(ctx context.Context, payload list.Request
|
|||
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
|
||||
}
|
||||
|
||||
func (c *aclSpaceClient) GenerateInvite() (resp list.InviteResult, err error) {
|
||||
c.acl.RLock()
|
||||
defer c.acl.RUnlock()
|
||||
return c.acl.RecordBuilder().BuildInvite()
|
||||
func (c *aclSpaceClient) ChangeInvite(ctx context.Context, inviteId string, permissions list.AclPermissions) error {
|
||||
c.acl.Lock()
|
||||
res, err := c.acl.RecordBuilder().BuildInviteChange(list.InviteChangePayload{
|
||||
IniviteRecordId: inviteId,
|
||||
Permissions: permissions,
|
||||
})
|
||||
if err != nil {
|
||||
c.acl.Unlock()
|
||||
return err
|
||||
}
|
||||
c.acl.Unlock()
|
||||
return c.sendRecordAndUpdate(ctx, c.spaceId, res)
|
||||
}
|
||||
|
||||
func (c *aclSpaceClient) GenerateInvite(isRevoke, isRequestToJoin bool, permissions list.AclPermissions) (list.InviteResult, error) {
|
||||
c.acl.Lock()
|
||||
defer c.acl.Unlock()
|
||||
var inviteIds []string
|
||||
if isRevoke {
|
||||
for _, invite := range c.acl.AclState().Invites() {
|
||||
if isRequestToJoin && invite.Type == aclrecordproto.AclInviteType_RequestToJoin {
|
||||
return list.InviteResult{}, list.ErrDuplicateInvites
|
||||
} else if invite.Permissions == permissions {
|
||||
return list.InviteResult{}, list.ErrDuplicateInvites
|
||||
}
|
||||
}
|
||||
inviteIds = c.acl.AclState().InviteIds()
|
||||
}
|
||||
var payload list.BatchRequestPayload
|
||||
if isRequestToJoin {
|
||||
payload = list.BatchRequestPayload{
|
||||
InviteRevokes: inviteIds,
|
||||
NewInvites: []list.AclPermissions{list.AclPermissionsNone},
|
||||
}
|
||||
} else {
|
||||
payload = list.BatchRequestPayload{
|
||||
InviteRevokes: inviteIds,
|
||||
NewInvites: []list.AclPermissions{permissions},
|
||||
}
|
||||
}
|
||||
res, err := c.acl.RecordBuilder().BuildBatchRequest(payload)
|
||||
if err != nil {
|
||||
return list.InviteResult{}, err
|
||||
}
|
||||
return list.InviteResult{
|
||||
InviteRec: res.Rec,
|
||||
InviteKey: res.Invites[0],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *aclSpaceClient) AddRecord(ctx context.Context, consRec *consensusproto.RawRecord) (err error) {
|
||||
|
|
|
@ -99,6 +99,21 @@ func (mr *MockAclJoiningClientMockRecorder) Init(arg0 any) *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockAclJoiningClient)(nil).Init), arg0)
|
||||
}
|
||||
|
||||
// InviteJoin mocks base method.
|
||||
func (m *MockAclJoiningClient) InviteJoin(arg0 context.Context, arg1 string, arg2 list.InviteJoinPayload) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InviteJoin", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InviteJoin indicates an expected call of InviteJoin.
|
||||
func (mr *MockAclJoiningClientMockRecorder) InviteJoin(arg0, arg1, arg2 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InviteJoin", reflect.TypeOf((*MockAclJoiningClient)(nil).InviteJoin), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// Name mocks base method.
|
||||
func (m *MockAclJoiningClient) Name() string {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -207,6 +222,20 @@ func (mr *MockAclSpaceClientMockRecorder) CancelRequest(arg0 any) *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelRequest", reflect.TypeOf((*MockAclSpaceClient)(nil).CancelRequest), arg0)
|
||||
}
|
||||
|
||||
// ChangeInvite mocks base method.
|
||||
func (m *MockAclSpaceClient) ChangeInvite(arg0 context.Context, arg1 string, arg2 list.AclPermissions) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ChangeInvite", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ChangeInvite indicates an expected call of ChangeInvite.
|
||||
func (mr *MockAclSpaceClientMockRecorder) ChangeInvite(arg0, arg1, arg2 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChangeInvite", reflect.TypeOf((*MockAclSpaceClient)(nil).ChangeInvite), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// ChangePermissions mocks base method.
|
||||
func (m *MockAclSpaceClient) ChangePermissions(arg0 context.Context, arg1 list.PermissionChangesPayload) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -236,18 +265,18 @@ func (mr *MockAclSpaceClientMockRecorder) DeclineRequest(arg0, arg1 any) *gomock
|
|||
}
|
||||
|
||||
// GenerateInvite mocks base method.
|
||||
func (m *MockAclSpaceClient) GenerateInvite() (list.InviteResult, error) {
|
||||
func (m *MockAclSpaceClient) GenerateInvite(arg0, arg1 bool, arg2 list.AclPermissions) (list.InviteResult, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GenerateInvite")
|
||||
ret := m.ctrl.Call(m, "GenerateInvite", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(list.InviteResult)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GenerateInvite indicates an expected call of GenerateInvite.
|
||||
func (mr *MockAclSpaceClientMockRecorder) GenerateInvite() *gomock.Call {
|
||||
func (mr *MockAclSpaceClientMockRecorder) GenerateInvite(arg0, arg1, arg2 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateInvite", reflect.TypeOf((*MockAclSpaceClient)(nil).GenerateInvite))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateInvite", reflect.TypeOf((*MockAclSpaceClient)(nil).GenerateInvite), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// Init mocks base method.
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/anyproto/any-sync/commonspace/acl/aclclient"
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/util/periodicsync"
|
||||
)
|
||||
|
||||
|
@ -82,7 +83,7 @@ func (a *aclWaiter) loop(ctx context.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
acl, err := list.BuildAclListWithIdentity(a.keys, storage, list.NoOpAcceptorVerifier{})
|
||||
acl, err := list.BuildAclListWithIdentity(a.keys, storage, recordverifier.New())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
|
||||
"github.com/anyproto/any-sync/commonspace/spacepayloads"
|
||||
"github.com/anyproto/any-sync/commonspace/spacestorage"
|
||||
|
@ -17,6 +18,14 @@ import (
|
|||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
||||
func mockDeps() Deps {
|
||||
return Deps{
|
||||
TreeSyncer: mockTreeSyncer{},
|
||||
SyncStatus: syncstatus.NewNoOpSyncStatus(),
|
||||
recordVerifier: recordverifier.NewValidateFull(),
|
||||
}
|
||||
}
|
||||
|
||||
func createTree(t *testing.T, ctx context.Context, spc Space, acc *accountdata.AccountKeys) string {
|
||||
bytes := make([]byte, 32)
|
||||
rand.Read(bytes)
|
||||
|
@ -60,7 +69,7 @@ func TestSpaceDeleteIdsMarkDeleted(t *testing.T) {
|
|||
require.NotNil(t, sp)
|
||||
|
||||
// initializing space
|
||||
spc, err := fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
|
||||
spc, err := fx.spaceService.NewSpace(ctx, sp, mockDeps())
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, spc)
|
||||
// adding space to tree manager
|
||||
|
@ -109,7 +118,7 @@ func TestSpaceDeleteIdsMarkDeleted(t *testing.T) {
|
|||
time.Sleep(100 * time.Millisecond)
|
||||
storeSetter := fx.storageProvider.(storeSetter)
|
||||
storeSetter.SetStore(sp, newStore)
|
||||
spc, err = fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
|
||||
spc, err = fx.spaceService.NewSpace(ctx, sp, mockDeps())
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, spc)
|
||||
waitTest := make(chan struct{})
|
||||
|
@ -153,7 +162,7 @@ func TestSpaceDeleteIds(t *testing.T) {
|
|||
require.NotNil(t, sp)
|
||||
|
||||
// initializing space
|
||||
spc, err := fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
|
||||
spc, err := fx.spaceService.NewSpace(ctx, sp, mockDeps())
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, spc)
|
||||
// adding space to tree manager
|
||||
|
@ -202,7 +211,7 @@ func TestSpaceDeleteIds(t *testing.T) {
|
|||
time.Sleep(100 * time.Millisecond)
|
||||
storeSetter := fx.storageProvider.(storeSetter)
|
||||
storeSetter.SetStore(sp, newStore)
|
||||
spc, err = fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
|
||||
spc, err = fx.spaceService.NewSpace(ctx, sp, mockDeps())
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, spc)
|
||||
waitTest := make(chan struct{})
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/anyproto/any-sync/app/logger"
|
||||
"github.com/anyproto/any-sync/commonspace/deletionstate"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/synctree"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
|
||||
"github.com/anyproto/any-sync/commonspace/object/treemanager"
|
||||
"github.com/anyproto/any-sync/commonspace/spacestorage"
|
||||
|
@ -43,7 +44,7 @@ func (d *deleter) Delete(ctx context.Context) {
|
|||
}
|
||||
} else {
|
||||
err = d.getter.DeleteTree(ctx, spaceId, id)
|
||||
if err != nil && !errors.Is(err, spacestorage.ErrTreeStorageAlreadyDeleted) {
|
||||
if err != nil && !errors.Is(err, spacestorage.ErrTreeStorageAlreadyDeleted) && !errors.Is(err, synctree.ErrSyncTreeDeleted) {
|
||||
log.Error("failed to delete object", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,6 +18,19 @@ message AclRoot {
|
|||
// AclAccountInvite contains the public invite key, the private part of which is sent to the user directly
|
||||
message AclAccountInvite {
|
||||
bytes inviteKey = 1;
|
||||
AclInviteType inviteType = 2;
|
||||
AclUserPermissions permissions = 3;
|
||||
bytes encryptedReadKey = 4;
|
||||
}
|
||||
|
||||
message AclAccountInviteChange {
|
||||
string inviteRecordId = 1;
|
||||
AclUserPermissions permissions = 2;
|
||||
}
|
||||
|
||||
enum AclInviteType {
|
||||
RequestToJoin = 0;
|
||||
AnyoneCanJoin = 1;
|
||||
}
|
||||
|
||||
// AclAccountRequestJoin contains the reference to the invite record and the data of the person who wants to join, confirmed by the private invite key
|
||||
|
@ -29,6 +42,17 @@ message AclAccountRequestJoin {
|
|||
bytes metadata = 4;
|
||||
}
|
||||
|
||||
// AclInviteJoin contains the reference to the invite record and the data of the person who wants to join, confirmed by the private invite key
|
||||
// The person must encrypt the key with its own public key
|
||||
message AclAccountInviteJoin {
|
||||
bytes identity = 1;
|
||||
string inviteRecordId = 2;
|
||||
bytes inviteIdentitySignature = 3;
|
||||
// Metadata is encrypted with metadata key of the space
|
||||
bytes metadata = 4;
|
||||
bytes encryptedReadKey = 5;
|
||||
}
|
||||
|
||||
// AclAccountRequestAccept contains the reference to join record and all read keys, encrypted with the identity of the requestor
|
||||
message AclAccountRequestAccept {
|
||||
bytes identity = 1;
|
||||
|
@ -90,6 +114,7 @@ message AclReadKeyChange {
|
|||
bytes encryptedMetadataPrivKey = 3;
|
||||
// EncryptedOldReadKey is encrypted with new read key
|
||||
bytes encryptedOldReadKey = 4;
|
||||
repeated AclEncryptedReadKey inviteKeys = 5;
|
||||
}
|
||||
|
||||
// AclAccountRemove removes an account and changes read key for space
|
||||
|
@ -118,6 +143,8 @@ message AclContentValue {
|
|||
AclAccountPermissionChanges permissionChanges = 10;
|
||||
AclAccountsAdd accountsAdd = 11;
|
||||
AclAccountRequestCancel requestCancel = 12;
|
||||
AclAccountInviteJoin inviteJoin = 13;
|
||||
AclAccountInviteChange inviteChange = 14;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/util/cidutil"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
|
@ -26,6 +27,11 @@ type RequestJoinPayload struct {
|
|||
Metadata []byte
|
||||
}
|
||||
|
||||
type InviteJoinPayload struct {
|
||||
InviteKey crypto.PrivKey
|
||||
Metadata []byte
|
||||
}
|
||||
|
||||
type ReadKeyChangePayload struct {
|
||||
MetadataKey crypto.PrivKey
|
||||
ReadKey crypto.SymKey
|
||||
|
@ -41,6 +47,11 @@ type PermissionChangePayload struct {
|
|||
Permissions AclPermissions
|
||||
}
|
||||
|
||||
type InviteChangePayload struct {
|
||||
IniviteRecordId string
|
||||
Permissions AclPermissions
|
||||
}
|
||||
|
||||
type PermissionChangesPayload struct {
|
||||
Changes []PermissionChangePayload
|
||||
}
|
||||
|
@ -55,6 +66,10 @@ type AccountAdd struct {
|
|||
Metadata []byte
|
||||
}
|
||||
|
||||
type NewInvites struct {
|
||||
Permissions AclPermissions
|
||||
}
|
||||
|
||||
type BatchRequestPayload struct {
|
||||
Additions []AccountAdd
|
||||
Changes []PermissionChangePayload
|
||||
|
@ -62,6 +77,8 @@ type BatchRequestPayload struct {
|
|||
Approvals []RequestAcceptPayload
|
||||
Declines []string
|
||||
InviteRevokes []string
|
||||
InviteChanges []InviteChangePayload
|
||||
NewInvites []AclPermissions
|
||||
}
|
||||
|
||||
type AccountRemovePayload struct {
|
||||
|
@ -74,14 +91,22 @@ type InviteResult struct {
|
|||
InviteKey crypto.PrivKey
|
||||
}
|
||||
|
||||
type BatchResult struct {
|
||||
Rec *consensusproto.RawRecord
|
||||
Invites []crypto.PrivKey
|
||||
}
|
||||
|
||||
type AclRecordBuilder interface {
|
||||
UnmarshallWithId(rawIdRecord *consensusproto.RawRecordWithId) (rec *AclRecord, err error)
|
||||
Unmarshall(rawRecord *consensusproto.RawRecord) (rec *AclRecord, err error)
|
||||
|
||||
BuildRoot(content RootContent) (rec *consensusproto.RawRecordWithId, err error)
|
||||
BuildBatchRequest(payload BatchRequestPayload) (rawRecord *consensusproto.RawRecord, err error)
|
||||
BuildBatchRequest(payload BatchRequestPayload) (batchResult BatchResult, err error)
|
||||
BuildInvite() (res InviteResult, err error)
|
||||
BuildInviteAnyone(permissions AclPermissions) (res InviteResult, err error)
|
||||
BuildInviteChange(inviteChange InviteChangePayload) (rawRecord *consensusproto.RawRecord, err error)
|
||||
BuildInviteRevoke(inviteRecordId string) (rawRecord *consensusproto.RawRecord, err error)
|
||||
BuildInviteJoin(payload InviteJoinPayload) (rawRecord *consensusproto.RawRecord, err error)
|
||||
BuildRequestJoin(payload RequestJoinPayload) (rawRecord *consensusproto.RawRecord, err error)
|
||||
BuildRequestAccept(payload RequestAcceptPayload) (rawRecord *consensusproto.RawRecord, err error)
|
||||
BuildRequestDecline(requestRecordId string) (rawRecord *consensusproto.RawRecord, err error)
|
||||
|
@ -98,11 +123,11 @@ type aclRecordBuilder struct {
|
|||
id string
|
||||
keyStorage crypto.KeyStorage
|
||||
accountKeys *accountdata.AccountKeys
|
||||
verifier AcceptorVerifier
|
||||
verifier recordverifier.AcceptorVerifier
|
||||
state *AclState
|
||||
}
|
||||
|
||||
func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage, keys *accountdata.AccountKeys, verifier AcceptorVerifier) AclRecordBuilder {
|
||||
func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage, keys *accountdata.AccountKeys, verifier recordverifier.AcceptorVerifier) AclRecordBuilder {
|
||||
return &aclRecordBuilder{
|
||||
id: id,
|
||||
keyStorage: keyStorage,
|
||||
|
@ -111,51 +136,84 @@ func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage, keys *accountd
|
|||
}
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildBatchRequest(payload BatchRequestPayload) (rawRec *consensusproto.RawRecord, err error) {
|
||||
var aclContent []*aclrecordproto.AclContentValue
|
||||
if len(payload.Additions) > 0 {
|
||||
content, err := a.buildAccountsAdd(AccountsAddPayload{Additions: payload.Additions})
|
||||
func (a *aclRecordBuilder) BuildBatchRequest(payload BatchRequestPayload) (batchResult BatchResult, err error) {
|
||||
var (
|
||||
contentList []*aclrecordproto.AclContentValue
|
||||
content *aclrecordproto.AclContentValue
|
||||
)
|
||||
if len(payload.Removals.Identities) > 0 {
|
||||
content, err = a.buildAccountRemove(payload.Removals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
aclContent = append(aclContent, content)
|
||||
contentList = append(contentList, content)
|
||||
}
|
||||
if len(payload.Additions) > 0 {
|
||||
content, err = a.buildAccountsAdd(AccountsAddPayload{Additions: payload.Additions}, payload.Removals.Change.MetadataKey.GetPublic(), payload.Removals.Change.ReadKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
contentList = append(contentList, content)
|
||||
}
|
||||
if len(payload.Changes) > 0 {
|
||||
content, err := a.buildPermissionChanges(PermissionChangesPayload{Changes: payload.Changes})
|
||||
content, err = a.buildPermissionChanges(PermissionChangesPayload{Changes: payload.Changes})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
aclContent = append(aclContent, content)
|
||||
contentList = append(contentList, content)
|
||||
}
|
||||
for _, acc := range payload.Approvals {
|
||||
content, err := a.buildRequestAccept(acc)
|
||||
content, err = a.buildRequestAccept(acc, payload.Removals.Change.ReadKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
aclContent = append(aclContent, content)
|
||||
contentList = append(contentList, content)
|
||||
}
|
||||
for _, id := range payload.Declines {
|
||||
content, err := a.buildRequestDecline(id)
|
||||
content, err = a.buildRequestDecline(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
aclContent = append(aclContent, content)
|
||||
contentList = append(contentList, content)
|
||||
}
|
||||
for _, id := range payload.InviteRevokes {
|
||||
content, err := a.buildInviteRevoke(id)
|
||||
content, err = a.buildInviteRevoke(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
aclContent = append(aclContent, content)
|
||||
contentList = append(contentList, content)
|
||||
}
|
||||
if len(payload.Removals.Identities) > 0 {
|
||||
content, err := a.buildAccountRemove(payload.Removals)
|
||||
for _, invite := range payload.InviteChanges {
|
||||
content, err = a.buildInviteChange(invite)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
aclContent = append(aclContent, content)
|
||||
contentList = append(contentList, content)
|
||||
}
|
||||
return a.buildRecords(aclContent)
|
||||
for _, perms := range payload.NewInvites {
|
||||
var privKey crypto.PrivKey
|
||||
if perms.NoPermissions() {
|
||||
privKey, content, err = a.buildInvite()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
contentList = append(contentList, content)
|
||||
batchResult.Invites = append(batchResult.Invites, privKey)
|
||||
} else {
|
||||
privKey, content, err = a.buildInviteAnyone(perms)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
contentList = append(contentList, content)
|
||||
batchResult.Invites = append(batchResult.Invites, privKey)
|
||||
}
|
||||
}
|
||||
res, err := a.buildRecords(contentList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
batchResult.Rec = res
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) buildRecord(aclContent *aclrecordproto.AclContentValue) (rawRec *consensusproto.RawRecord, err error) {
|
||||
|
@ -190,9 +248,20 @@ func (a *aclRecordBuilder) buildRecords(aclContent []*aclrecordproto.AclContentV
|
|||
Payload: marshalledRec,
|
||||
Signature: signature,
|
||||
}
|
||||
err = a.preflightCheck(rawRec)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) preflightCheck(rawRecord *consensusproto.RawRecord) (err error) {
|
||||
aclRec, err := a.Unmarshall(rawRecord)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cp := a.state.Copy()
|
||||
cp.contentValidator.(*contentValidator).verifier = recordverifier.NewValidateFull()
|
||||
return cp.ApplyRecord(aclRec)
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildPermissionChanges(payload PermissionChangesPayload) (rawRecord *consensusproto.RawRecord, err error) {
|
||||
content, err := a.buildPermissionChanges(payload)
|
||||
if err != nil {
|
||||
|
@ -231,14 +300,14 @@ func (a *aclRecordBuilder) buildPermissionChanges(payload PermissionChangesPaylo
|
|||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildAccountsAdd(payload AccountsAddPayload) (rawRecord *consensusproto.RawRecord, err error) {
|
||||
content, err := a.buildAccountsAdd(payload)
|
||||
content, err := a.buildAccountsAdd(payload, nil, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return a.buildRecord(content)
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) buildAccountsAdd(payload AccountsAddPayload) (value *aclrecordproto.AclContentValue, err error) {
|
||||
func (a *aclRecordBuilder) buildAccountsAdd(payload AccountsAddPayload, mkKey crypto.PubKey, readKey crypto.SymKey) (value *aclrecordproto.AclContentValue, err error) {
|
||||
var accs []*aclrecordproto.AclAccountAdd
|
||||
for _, acc := range payload.Additions {
|
||||
if !a.state.Permissions(acc.Identity).NoPermissions() {
|
||||
|
@ -247,9 +316,11 @@ func (a *aclRecordBuilder) buildAccountsAdd(payload AccountsAddPayload) (value *
|
|||
if acc.Permissions.IsOwner() {
|
||||
return nil, ErrIsOwner
|
||||
}
|
||||
mkKey, err := a.state.CurrentMetadataKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if mkKey == nil {
|
||||
mkKey, err = a.state.CurrentMetadataKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
encMeta, err := mkKey.Encrypt(acc.Metadata)
|
||||
if err != nil {
|
||||
|
@ -258,9 +329,11 @@ func (a *aclRecordBuilder) buildAccountsAdd(payload AccountsAddPayload) (value *
|
|||
if len(encMeta) > MaxMetadataLen {
|
||||
return nil, ErrMetadataTooLarge
|
||||
}
|
||||
readKey, err := a.state.CurrentReadKey()
|
||||
if err != nil {
|
||||
return nil, ErrNoReadKey
|
||||
if readKey == nil {
|
||||
readKey, err = a.state.CurrentReadKey()
|
||||
if err != nil {
|
||||
return nil, ErrNoReadKey
|
||||
}
|
||||
}
|
||||
protoKey, err := readKey.Marshall()
|
||||
if err != nil {
|
||||
|
@ -287,6 +360,20 @@ func (a *aclRecordBuilder) buildAccountsAdd(payload AccountsAddPayload) (value *
|
|||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) {
|
||||
privKey, content, err := a.buildInvite()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rawRec, err := a.buildRecord(content)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res.InviteKey = privKey
|
||||
res.InviteRec = rawRec
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) buildInvite() (invKey crypto.PrivKey, content *aclrecordproto.AclContentValue, err error) {
|
||||
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
|
||||
err = ErrInsufficientPermissions
|
||||
return
|
||||
|
@ -300,7 +387,37 @@ func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) {
|
|||
return
|
||||
}
|
||||
inviteRec := &aclrecordproto.AclAccountInvite{InviteKey: invitePubKey}
|
||||
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}}
|
||||
content = &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}}
|
||||
invKey = privKey
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildInviteChange(inviteChange InviteChangePayload) (rawRecord *consensusproto.RawRecord, err error) {
|
||||
content, err := a.buildInviteChange(inviteChange)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return a.buildRecord(content)
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) buildInviteChange(inviteChange InviteChangePayload) (content *aclrecordproto.AclContentValue, err error) {
|
||||
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
|
||||
err = ErrInsufficientPermissions
|
||||
return
|
||||
}
|
||||
inviteRec := &aclrecordproto.AclAccountInviteChange{
|
||||
InviteRecordId: inviteChange.IniviteRecordId,
|
||||
Permissions: aclrecordproto.AclUserPermissions(inviteChange.Permissions),
|
||||
}
|
||||
content = &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_InviteChange{InviteChange: inviteRec}}
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildInviteAnyone(permissions AclPermissions) (res InviteResult, err error) {
|
||||
privKey, content, err := a.buildInviteAnyone(permissions)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rawRec, err := a.buildRecord(content)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -310,6 +427,42 @@ func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) buildInviteAnyone(permissions AclPermissions) (invKey crypto.PrivKey, content *aclrecordproto.AclContentValue, err error) {
|
||||
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
|
||||
err = ErrInsufficientPermissions
|
||||
return
|
||||
}
|
||||
privKey, pubKey, err := crypto.GenerateRandomEd25519KeyPair()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
invitePubKey, err := pubKey.Marshall()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
curReadKey, err := a.state.CurrentReadKey()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
raw, err := curReadKey.Marshall()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
encReadKey, err := pubKey.Encrypt(raw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
inviteRec := &aclrecordproto.AclAccountInvite{
|
||||
InviteKey: invitePubKey,
|
||||
InviteType: aclrecordproto.AclInviteType_AnyoneCanJoin,
|
||||
Permissions: aclrecordproto.AclUserPermissions(permissions),
|
||||
EncryptedReadKey: encReadKey,
|
||||
}
|
||||
content = &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}}
|
||||
invKey = privKey
|
||||
return
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildInviteRevoke(inviteRecordId string) (rawRecord *consensusproto.RawRecord, err error) {
|
||||
content, err := a.buildInviteRevoke(inviteRecordId)
|
||||
if err != nil {
|
||||
|
@ -323,7 +476,7 @@ func (a *aclRecordBuilder) buildInviteRevoke(inviteRecordId string) (value *aclr
|
|||
err = ErrInsufficientPermissions
|
||||
return
|
||||
}
|
||||
_, exists := a.state.inviteKeys[inviteRecordId]
|
||||
_, exists := a.state.invites[inviteRecordId]
|
||||
if !exists {
|
||||
err = ErrNoSuchInvite
|
||||
return
|
||||
|
@ -334,17 +487,17 @@ func (a *aclRecordBuilder) buildInviteRevoke(inviteRecordId string) (value *aclr
|
|||
|
||||
func (a *aclRecordBuilder) BuildRequestJoin(payload RequestJoinPayload) (rawRecord *consensusproto.RawRecord, err error) {
|
||||
var inviteId string
|
||||
for id, key := range a.state.inviteKeys {
|
||||
if key.Equals(payload.InviteKey.GetPublic()) {
|
||||
for id, inv := range a.state.invites {
|
||||
if inv.Key.Equals(payload.InviteKey.GetPublic()) {
|
||||
inviteId = id
|
||||
}
|
||||
}
|
||||
key, exists := a.state.inviteKeys[inviteId]
|
||||
invite, exists := a.state.invites[inviteId]
|
||||
if !exists {
|
||||
err = ErrNoSuchInvite
|
||||
return
|
||||
}
|
||||
if !payload.InviteKey.GetPublic().Equals(key) {
|
||||
if !payload.InviteKey.GetPublic().Equals(invite.Key) {
|
||||
err = ErrIncorrectInviteKey
|
||||
return
|
||||
}
|
||||
|
@ -385,15 +538,81 @@ func (a *aclRecordBuilder) BuildRequestJoin(payload RequestJoinPayload) (rawReco
|
|||
return a.buildRecord(content)
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildInviteJoin(payload InviteJoinPayload) (rawRecord *consensusproto.RawRecord, err error) {
|
||||
var inviteId string
|
||||
for id, inv := range a.state.invites {
|
||||
if inv.Key.Equals(payload.InviteKey.GetPublic()) && inv.Type == aclrecordproto.AclInviteType_AnyoneCanJoin {
|
||||
inviteId = id
|
||||
}
|
||||
}
|
||||
invite, exists := a.state.invites[inviteId]
|
||||
if !exists {
|
||||
err = ErrNoSuchInvite
|
||||
return
|
||||
}
|
||||
if !payload.InviteKey.GetPublic().Equals(invite.Key) {
|
||||
err = ErrIncorrectInviteKey
|
||||
return
|
||||
}
|
||||
if !a.state.Permissions(a.accountKeys.SignKey.GetPublic()).NoPermissions() {
|
||||
err = ErrInsufficientPermissions
|
||||
return
|
||||
}
|
||||
mkKey, err := a.state.CurrentMetadataKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encMeta, err := mkKey.Encrypt(payload.Metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(encMeta) > MaxMetadataLen {
|
||||
return nil, ErrMetadataTooLarge
|
||||
}
|
||||
rawIdentity, err := a.accountKeys.SignKey.GetPublic().Raw()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
signature, err := payload.InviteKey.Sign(rawIdentity)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
key, err := a.state.DecryptInvite(payload.InviteKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
readKey, err := key.Marshall()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
encReadKey, err := a.accountKeys.SignKey.GetPublic().Encrypt(readKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
protoIdentity, err := a.accountKeys.SignKey.GetPublic().Marshall()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
joinRec := &aclrecordproto.AclAccountInviteJoin{
|
||||
Identity: protoIdentity,
|
||||
InviteRecordId: inviteId,
|
||||
InviteIdentitySignature: signature,
|
||||
Metadata: encMeta,
|
||||
EncryptedReadKey: encReadKey,
|
||||
}
|
||||
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_InviteJoin{InviteJoin: joinRec}}
|
||||
return a.buildRecord(content)
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) BuildRequestAccept(payload RequestAcceptPayload) (rawRecord *consensusproto.RawRecord, err error) {
|
||||
content, err := a.buildRequestAccept(payload)
|
||||
content, err := a.buildRequestAccept(payload, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return a.buildRecord(content)
|
||||
}
|
||||
|
||||
func (a *aclRecordBuilder) buildRequestAccept(payload RequestAcceptPayload) (value *aclrecordproto.AclContentValue, err error) {
|
||||
func (a *aclRecordBuilder) buildRequestAccept(payload RequestAcceptPayload, readKey crypto.SymKey) (value *aclrecordproto.AclContentValue, err error) {
|
||||
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
|
||||
err = ErrInsufficientPermissions
|
||||
return
|
||||
|
@ -403,9 +622,11 @@ func (a *aclRecordBuilder) buildRequestAccept(payload RequestAcceptPayload) (val
|
|||
err = ErrNoSuchRequest
|
||||
return
|
||||
}
|
||||
readKey, err := a.state.CurrentReadKey()
|
||||
if err != nil {
|
||||
return nil, ErrNoReadKey
|
||||
if readKey == nil {
|
||||
readKey, err = a.state.CurrentReadKey()
|
||||
if err != nil {
|
||||
return nil, ErrNoReadKey
|
||||
}
|
||||
}
|
||||
protoKey, err := readKey.Marshall()
|
||||
if err != nil {
|
||||
|
@ -506,13 +727,19 @@ func (a *aclRecordBuilder) buildReadKeyChange(payload ReadKeyChangePayload, remo
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var aclReadKeys []*aclrecordproto.AclEncryptedReadKey
|
||||
var (
|
||||
aclReadKeys []*aclrecordproto.AclEncryptedReadKey
|
||||
invites []*aclrecordproto.AclEncryptedReadKey
|
||||
)
|
||||
for identity, st := range a.state.accountStates {
|
||||
if removedIdentities != nil {
|
||||
if _, exists := removedIdentities[identity]; exists {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if st.Permissions.NoPermissions() {
|
||||
continue
|
||||
}
|
||||
protoIdentity, err := st.PubKey.Marshall()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -526,6 +753,23 @@ func (a *aclRecordBuilder) buildReadKeyChange(payload ReadKeyChangePayload, remo
|
|||
EncryptedReadKey: enc,
|
||||
})
|
||||
}
|
||||
for _, invite := range a.state.invites {
|
||||
if invite.Type != aclrecordproto.AclInviteType_AnyoneCanJoin {
|
||||
continue
|
||||
}
|
||||
protoIdentity, err := invite.Key.Marshall()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enc, err := invite.Key.Encrypt(protoKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invites = append(invites, &aclrecordproto.AclEncryptedReadKey{
|
||||
Identity: protoIdentity,
|
||||
EncryptedReadKey: enc,
|
||||
})
|
||||
}
|
||||
// encrypting metadata key with new read key
|
||||
mkPubKey, err := payload.MetadataKey.GetPublic().Marshall()
|
||||
if err != nil {
|
||||
|
@ -557,6 +801,7 @@ func (a *aclRecordBuilder) buildReadKeyChange(payload ReadKeyChangePayload, remo
|
|||
MetadataPubKey: mkPubKey,
|
||||
EncryptedMetadataPrivKey: encPrivKey,
|
||||
EncryptedOldReadKey: encOldKey,
|
||||
InviteKeys: invites,
|
||||
}
|
||||
return readRec, nil
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ import (
|
|||
|
||||
"github.com/anyproto/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/anyproto/any-sync/app/logger"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
||||
|
@ -25,6 +27,7 @@ var (
|
|||
ErrNoSuchRequest = errors.New("no such request")
|
||||
ErrNoSuchInvite = errors.New("no such invite")
|
||||
ErrInsufficientPermissions = errors.New("insufficient permissions")
|
||||
ErrDuplicateInvites = errors.New("duplicate invites")
|
||||
ErrIsOwner = errors.New("can't be made by owner")
|
||||
ErrIncorrectNumberOfAccounts = errors.New("incorrect number of accounts")
|
||||
ErrDuplicateAccounts = errors.New("duplicate accounts")
|
||||
|
@ -48,6 +51,17 @@ type AclKeys struct {
|
|||
ReadKey crypto.SymKey
|
||||
MetadataPrivKey crypto.PrivKey
|
||||
MetadataPubKey crypto.PubKey
|
||||
|
||||
oldEncryptedReadKey []byte
|
||||
encMetadatKey []byte
|
||||
}
|
||||
|
||||
type Invite struct {
|
||||
Key crypto.PubKey
|
||||
Type aclrecordproto.AclInviteType
|
||||
Permissions AclPermissions
|
||||
Id string
|
||||
encryptedKey []byte
|
||||
}
|
||||
|
||||
type AclState struct {
|
||||
|
@ -57,7 +71,7 @@ type AclState struct {
|
|||
// accountStates is a map pubKey -> state which defines current account state
|
||||
accountStates map[string]AccountState
|
||||
// inviteKeys is a map recordId -> invite
|
||||
inviteKeys map[string]crypto.PubKey
|
||||
invites map[string]Invite
|
||||
// requestRecords is a map recordId -> RequestRecord
|
||||
requestRecords map[string]RequestRecord
|
||||
// pendingRequests is a map pubKey -> recordId
|
||||
|
@ -75,22 +89,20 @@ type AclState struct {
|
|||
|
||||
func newAclStateWithKeys(
|
||||
rootRecord *AclRecord,
|
||||
key crypto.PrivKey) (st *AclState, err error) {
|
||||
key crypto.PrivKey,
|
||||
verifier recordverifier.AcceptorVerifier) (st *AclState, err error) {
|
||||
st = &AclState{
|
||||
id: rootRecord.Id,
|
||||
key: key,
|
||||
pubKey: key.GetPublic(),
|
||||
keys: make(map[string]AclKeys),
|
||||
accountStates: make(map[string]AccountState),
|
||||
inviteKeys: make(map[string]crypto.PubKey),
|
||||
invites: make(map[string]Invite),
|
||||
requestRecords: make(map[string]RequestRecord),
|
||||
pendingRequests: make(map[string]string),
|
||||
keyStore: crypto.NewKeyStorage(),
|
||||
}
|
||||
st.contentValidator = &contentValidator{
|
||||
keyStore: st.keyStore,
|
||||
aclState: st,
|
||||
}
|
||||
st.contentValidator = newContentValidator(st.keyStore, st, verifier)
|
||||
err = st.applyRoot(rootRecord)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -98,20 +110,17 @@ func newAclStateWithKeys(
|
|||
return st, nil
|
||||
}
|
||||
|
||||
func newAclState(rootRecord *AclRecord) (st *AclState, err error) {
|
||||
func newAclState(rootRecord *AclRecord, verifier recordverifier.AcceptorVerifier) (st *AclState, err error) {
|
||||
st = &AclState{
|
||||
id: rootRecord.Id,
|
||||
keys: make(map[string]AclKeys),
|
||||
accountStates: make(map[string]AccountState),
|
||||
inviteKeys: make(map[string]crypto.PubKey),
|
||||
invites: make(map[string]Invite),
|
||||
requestRecords: make(map[string]RequestRecord),
|
||||
pendingRequests: make(map[string]string),
|
||||
keyStore: crypto.NewKeyStorage(),
|
||||
}
|
||||
st.contentValidator = &contentValidator{
|
||||
keyStore: st.keyStore,
|
||||
aclState: st,
|
||||
}
|
||||
st.contentValidator = newContentValidator(st.keyStore, st, verifier)
|
||||
err = st.applyRoot(rootRecord)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -209,9 +218,12 @@ func (st *AclState) HadReadPermissions(identity crypto.PubKey) (had bool) {
|
|||
return false
|
||||
}
|
||||
|
||||
func (st *AclState) Invites() []crypto.PubKey {
|
||||
var invites []crypto.PubKey
|
||||
for _, inv := range st.inviteKeys {
|
||||
func (st *AclState) Invites(inviteType ...aclrecordproto.AclInviteType) []Invite {
|
||||
var invites []Invite
|
||||
for _, inv := range st.invites {
|
||||
if len(inviteType) > 0 && !slices.Contains(inviteType, inv.Type) {
|
||||
continue
|
||||
}
|
||||
invites = append(invites, inv)
|
||||
}
|
||||
return invites
|
||||
|
@ -223,12 +235,36 @@ func (st *AclState) Key() crypto.PrivKey {
|
|||
|
||||
func (st *AclState) InviteIds() []string {
|
||||
var invites []string
|
||||
for invId := range st.inviteKeys {
|
||||
for invId := range st.invites {
|
||||
invites = append(invites, invId)
|
||||
}
|
||||
return invites
|
||||
}
|
||||
|
||||
func (st *AclState) RequestIds() []string {
|
||||
var requests []string
|
||||
for reqId := range st.requestRecords {
|
||||
requests = append(requests, reqId)
|
||||
}
|
||||
return requests
|
||||
}
|
||||
|
||||
func (st *AclState) DecryptInvite(invitePk crypto.PrivKey) (key crypto.SymKey, err error) {
|
||||
if invitePk == nil {
|
||||
return nil, ErrNoReadKey
|
||||
}
|
||||
for _, invite := range st.invites {
|
||||
if invite.Key.Equals(invitePk.GetPublic()) {
|
||||
res, err := st.unmarshallDecryptReadKey(invite.encryptedKey, invitePk.Decrypt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNoSuchInvite
|
||||
}
|
||||
|
||||
func (st *AclState) ApplyRecord(record *AclRecord) (err error) {
|
||||
if st.lastRecordId != record.PrevId {
|
||||
err = ErrIncorrectRecordSequence
|
||||
|
@ -277,7 +313,10 @@ func (st *AclState) applyRoot(record *AclRecord) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
st.keys[record.Id] = AclKeys{MetadataPubKey: mkPubKey}
|
||||
st.keys[record.Id] = AclKeys{
|
||||
MetadataPubKey: mkPubKey,
|
||||
encMetadatKey: root.EncryptedMetadataPrivKey,
|
||||
}
|
||||
} else {
|
||||
// this should be a derived acl
|
||||
st.keys[record.Id] = AclKeys{}
|
||||
|
@ -350,7 +389,7 @@ func (st *AclState) Copy() *AclState {
|
|||
pubKey: st.key.GetPublic(),
|
||||
keys: make(map[string]AclKeys),
|
||||
accountStates: make(map[string]AccountState),
|
||||
inviteKeys: make(map[string]crypto.PubKey),
|
||||
invites: make(map[string]Invite),
|
||||
requestRecords: make(map[string]RequestRecord),
|
||||
pendingRequests: make(map[string]string),
|
||||
keyStore: st.keyStore,
|
||||
|
@ -365,8 +404,8 @@ func (st *AclState) Copy() *AclState {
|
|||
accState.PermissionChanges = permChanges
|
||||
newSt.accountStates[k] = accState
|
||||
}
|
||||
for k, v := range st.inviteKeys {
|
||||
newSt.inviteKeys[k] = v
|
||||
for k, v := range st.invites {
|
||||
newSt.invites[k] = v
|
||||
}
|
||||
for k, v := range st.requestRecords {
|
||||
newSt.requestRecords[k] = v
|
||||
|
@ -377,12 +416,16 @@ func (st *AclState) Copy() *AclState {
|
|||
newSt.readKeyChanges = append(newSt.readKeyChanges, st.readKeyChanges...)
|
||||
newSt.list = st.list
|
||||
newSt.lastRecordId = st.lastRecordId
|
||||
newSt.contentValidator = newContentValidator(newSt.keyStore, newSt)
|
||||
newSt.contentValidator = newContentValidator(newSt.keyStore, newSt, st.list.verifier)
|
||||
return newSt
|
||||
}
|
||||
|
||||
func (st *AclState) applyChangeContent(ch *aclrecordproto.AclContentValue, record *AclRecord) error {
|
||||
switch {
|
||||
case ch.GetInviteChange() != nil:
|
||||
return st.applyInviteChange(ch.GetInviteChange(), record)
|
||||
case ch.GetInviteJoin() != nil:
|
||||
return st.applyInviteJoin(ch.GetInviteJoin(), record)
|
||||
case ch.GetPermissionChange() != nil:
|
||||
return st.applyPermissionChange(ch.GetPermissionChange(), record)
|
||||
case ch.GetInvite() != nil:
|
||||
|
@ -423,6 +466,17 @@ func (st *AclState) applyPermissionChanges(ch *aclrecordproto.AclAccountPermissi
|
|||
return nil
|
||||
}
|
||||
|
||||
func (st *AclState) applyInviteChange(ch *aclrecordproto.AclAccountInviteChange, record *AclRecord) (err error) {
|
||||
err = st.contentValidator.ValidateInviteChange(ch, record.Identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invite := st.invites[ch.InviteRecordId]
|
||||
invite.Permissions = AclPermissions(ch.Permissions)
|
||||
st.invites[ch.InviteRecordId] = invite
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *AclState) applyPermissionChange(ch *aclrecordproto.AclAccountPermissionChange, record *AclRecord) error {
|
||||
chIdentity, err := st.keyStore.PubKeyFromProto(ch.Identity)
|
||||
if err != nil {
|
||||
|
@ -452,7 +506,13 @@ func (st *AclState) applyInvite(ch *aclrecordproto.AclAccountInvite, record *Acl
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
st.inviteKeys[record.Id] = inviteKey
|
||||
st.invites[record.Id] = Invite{
|
||||
Key: inviteKey,
|
||||
Id: record.Id,
|
||||
Type: ch.InviteType,
|
||||
Permissions: AclPermissions(ch.Permissions),
|
||||
encryptedKey: ch.EncryptedReadKey,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -461,7 +521,7 @@ func (st *AclState) applyInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke,
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(st.inviteKeys, ch.InviteRecordId)
|
||||
delete(st.invites, ch.InviteRecordId)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -582,6 +642,58 @@ func (st *AclState) applyRequestAccept(ch *aclrecordproto.AclAccountRequestAccep
|
|||
return st.unpackAllKeys(ch.EncryptedReadKey)
|
||||
}
|
||||
|
||||
func (st *AclState) applyInviteJoin(ch *aclrecordproto.AclAccountInviteJoin, record *AclRecord) error {
|
||||
err := st.contentValidator.ValidateInviteJoin(ch, record.Identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
identity, err := st.keyStore.PubKeyFromProto(ch.Identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inviteRecord, _ := st.invites[ch.InviteRecordId]
|
||||
pKeyString := mapKeyFromPubKey(identity)
|
||||
state, exists := st.accountStates[pKeyString]
|
||||
if !exists {
|
||||
st.accountStates[pKeyString] = AccountState{
|
||||
PubKey: identity,
|
||||
Permissions: inviteRecord.Permissions,
|
||||
RequestMetadata: ch.Metadata,
|
||||
KeyRecordId: st.CurrentReadKeyId(),
|
||||
Status: StatusActive,
|
||||
PermissionChanges: []PermissionChange{
|
||||
{
|
||||
Permission: inviteRecord.Permissions,
|
||||
RecordId: record.Id,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
st.accountStates[pKeyString] = AccountState{
|
||||
PubKey: identity,
|
||||
Permissions: inviteRecord.Permissions,
|
||||
RequestMetadata: ch.Metadata,
|
||||
KeyRecordId: st.CurrentReadKeyId(),
|
||||
Status: StatusActive,
|
||||
PermissionChanges: append(state.PermissionChanges, PermissionChange{
|
||||
Permission: inviteRecord.Permissions,
|
||||
RecordId: record.Id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
for _, rec := range st.requestRecords {
|
||||
if rec.RequestIdentity.Equals(identity) {
|
||||
delete(st.pendingRequests, mapKeyFromPubKey(rec.RequestIdentity))
|
||||
delete(st.requestRecords, rec.RecordId)
|
||||
break
|
||||
}
|
||||
}
|
||||
if st.pubKey.Equals(identity) {
|
||||
return st.unpackAllKeys(ch.EncryptedReadKey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *AclState) unpackAllKeys(rk []byte) error {
|
||||
iterReadKey, err := st.unmarshallDecryptReadKey(rk, st.key.Decrypt)
|
||||
if err != nil {
|
||||
|
@ -589,42 +701,8 @@ func (st *AclState) unpackAllKeys(rk []byte) error {
|
|||
}
|
||||
for idx := len(st.readKeyChanges) - 1; idx >= 0; idx-- {
|
||||
recId := st.readKeyChanges[idx]
|
||||
rec, err := st.list.Get(recId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if this is a first key change
|
||||
if recId == st.id {
|
||||
ch := rec.Model.(*aclrecordproto.AclRoot)
|
||||
metadataKey, err := st.unmarshallDecryptPrivKey(ch.EncryptedMetadataPrivKey, iterReadKey.Decrypt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aclKeys := st.keys[recId]
|
||||
aclKeys.ReadKey = iterReadKey
|
||||
aclKeys.MetadataPrivKey = metadataKey
|
||||
st.keys[recId] = aclKeys
|
||||
break
|
||||
}
|
||||
model := rec.Model.(*aclrecordproto.AclData)
|
||||
content := model.GetAclContent()
|
||||
var readKeyChange *aclrecordproto.AclReadKeyChange
|
||||
for _, ch := range content {
|
||||
switch {
|
||||
case ch.GetReadKeyChange() != nil:
|
||||
readKeyChange = ch.GetReadKeyChange()
|
||||
case ch.GetAccountRemove() != nil:
|
||||
readKeyChange = ch.GetAccountRemove().GetReadKeyChange()
|
||||
}
|
||||
}
|
||||
if readKeyChange == nil {
|
||||
return ErrIncorrectReadKey
|
||||
}
|
||||
oldReadKey, err := st.unmarshallDecryptReadKey(readKeyChange.EncryptedOldReadKey, iterReadKey.Decrypt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
metadataKey, err := st.unmarshallDecryptPrivKey(readKeyChange.EncryptedMetadataPrivKey, iterReadKey.Decrypt)
|
||||
keys := st.keys[recId]
|
||||
metadataKey, err := st.unmarshallDecryptPrivKey(keys.encMetadatKey, iterReadKey.Decrypt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -632,7 +710,16 @@ func (st *AclState) unpackAllKeys(rk []byte) error {
|
|||
aclKeys.ReadKey = iterReadKey
|
||||
aclKeys.MetadataPrivKey = metadataKey
|
||||
st.keys[recId] = aclKeys
|
||||
iterReadKey = oldReadKey
|
||||
if idx != 0 {
|
||||
if keys.oldEncryptedReadKey == nil {
|
||||
return ErrIncorrectReadKey
|
||||
}
|
||||
oldReadKey, err := st.unmarshallDecryptReadKey(keys.oldEncryptedReadKey, iterReadKey.Decrypt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iterReadKey = oldReadKey
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -744,7 +831,9 @@ func (st *AclState) applyReadKeyChange(ch *aclrecordproto.AclReadKeyChange, reco
|
|||
return err
|
||||
}
|
||||
aclKeys := AclKeys{
|
||||
MetadataPubKey: mkPubKey,
|
||||
MetadataPubKey: mkPubKey,
|
||||
oldEncryptedReadKey: ch.EncryptedOldReadKey,
|
||||
encMetadatKey: ch.EncryptedMetadataPrivKey,
|
||||
}
|
||||
for _, accKey := range ch.AccountKeys {
|
||||
identity, _ := st.keyStore.PubKeyFromProto(accKey.Identity)
|
||||
|
@ -756,6 +845,19 @@ func (st *AclState) applyReadKeyChange(ch *aclrecordproto.AclReadKeyChange, reco
|
|||
aclKeys.ReadKey = res
|
||||
}
|
||||
}
|
||||
for _, encKey := range ch.InviteKeys {
|
||||
invKey, err := st.keyStore.PubKeyFromProto(encKey.Identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, invite := range st.invites {
|
||||
if invite.Key.Equals(invKey) {
|
||||
invite.encryptedKey = encKey.EncryptedReadKey
|
||||
st.invites[key] = invite
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if aclKeys.ReadKey != nil {
|
||||
metadataKey, err := st.unmarshallDecryptPrivKey(ch.EncryptedMetadataPrivKey, aclKeys.ReadKey.Decrypt)
|
||||
if err != nil {
|
||||
|
@ -792,8 +894,8 @@ func (st *AclState) unmarshallDecryptPrivKey(msg []byte, decryptor func(msg []by
|
|||
}
|
||||
|
||||
func (st *AclState) GetInviteIdByPrivKey(inviteKey crypto.PrivKey) (recId string, err error) {
|
||||
for id, inv := range st.inviteKeys {
|
||||
if inv.Equals(inviteKey.GetPublic()) {
|
||||
for id, inv := range st.invites {
|
||||
if inv.Key.Equals(inviteKey.GetPublic()) {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,12 +29,12 @@ func (sb *aclStateBuilder) Build(records []*AclRecord, list *aclList) (state *Ac
|
|||
return nil, ErrIncorrectRecordSequence
|
||||
}
|
||||
if sb.privKey != nil {
|
||||
state, err = newAclStateWithKeys(records[0], sb.privKey)
|
||||
state, err = newAclStateWithKeys(records[0], sb.privKey, list.verifier)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
state, err = newAclState(records[0])
|
||||
state, err = newAclState(records[0], list.verifier)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
@ -66,6 +67,7 @@ var (
|
|||
func (a *AclTestExecutor) buildBatchRequest(args []string, acl AclList, getPerm func(perm string) AclPermissions, addRec func(rec *consensusproto.RawRecordWithId) error) (afterAll []func(), err error) {
|
||||
// remove:a,b,c;add:d,rw,m1|e,r,m2;changes:f,rw|g,r;revoke:inv1id;decline:g,h;
|
||||
batchPayload := BatchRequestPayload{}
|
||||
var inviteIds []string
|
||||
for _, arg := range args {
|
||||
parts := strings.Split(arg, ":")
|
||||
if len(parts) != 2 {
|
||||
|
@ -86,7 +88,7 @@ func (a *AclTestExecutor) buildBatchRequest(args []string, acl AclList, getPerm
|
|||
return nil, err
|
||||
}
|
||||
ownerAcl := a.actualAccounts[a.owner].Acl.(*aclList)
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, ownerAcl.storage, NoOpAcceptorVerifier{})
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, ownerAcl.storage, recordverifier.NewValidateFull())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -181,6 +183,24 @@ func (a *AclTestExecutor) buildBatchRequest(args []string, acl AclList, getPerm
|
|||
afterAll = append(afterAll, func() {
|
||||
a.expectedAccounts[id].status = StatusDeclined
|
||||
})
|
||||
case "invite":
|
||||
inviteParts := strings.Split(commandArgs[0], ",")
|
||||
id := inviteParts[0]
|
||||
inviteIds = append(inviteIds, id)
|
||||
batchPayload.NewInvites = append(batchPayload.NewInvites, AclPermissionsNone)
|
||||
case "invite_anyone":
|
||||
inviteParts := strings.Split(commandArgs[0], ",")
|
||||
id := inviteParts[0]
|
||||
var permissions AclPermissions
|
||||
if inviteParts[1] == "r" {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Reader)
|
||||
} else if inviteParts[1] == "rw" {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Writer)
|
||||
} else {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Admin)
|
||||
}
|
||||
inviteIds = append(inviteIds, id)
|
||||
batchPayload.NewInvites = append(batchPayload.NewInvites, permissions)
|
||||
case "approve":
|
||||
recs, err := acl.AclState().JoinRecords(false)
|
||||
if err != nil {
|
||||
|
@ -198,7 +218,7 @@ func (a *AclTestExecutor) buildBatchRequest(args []string, acl AclList, getPerm
|
|||
}
|
||||
}
|
||||
if recId == "" {
|
||||
return nil, fmt.Errorf("no join records for approve")
|
||||
return nil, fmt.Errorf("no join records to approve")
|
||||
}
|
||||
perms := getPerm(argParts[1])
|
||||
afterAll = append(afterAll, func() {
|
||||
|
@ -211,12 +231,14 @@ func (a *AclTestExecutor) buildBatchRequest(args []string, acl AclList, getPerm
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
res, err := acl.RecordBuilder().BuildBatchRequest(batchPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return afterAll, addRec(WrapAclRecord(res))
|
||||
for i, id := range inviteIds {
|
||||
a.invites[id] = res.Invites[i]
|
||||
}
|
||||
return afterAll, addRec(WrapAclRecord(res.Rec))
|
||||
}
|
||||
|
||||
func (a *AclTestExecutor) Execute(cmd string) (err error) {
|
||||
|
@ -273,7 +295,7 @@ func (a *AclTestExecutor) Execute(cmd string) (err error) {
|
|||
} else {
|
||||
ownerAcl := a.actualAccounts[a.owner].Acl.(*aclList)
|
||||
copyStorage := ownerAcl.storage.(*inMemoryStorage).Copy()
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, copyStorage, NoOpAcceptorVerifier{})
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, copyStorage, recordverifier.NewValidateFull())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -291,7 +313,7 @@ func (a *AclTestExecutor) Execute(cmd string) (err error) {
|
|||
keys := a.actualAccounts[account].Keys
|
||||
ownerAcl := a.actualAccounts[a.owner].Acl.(*aclList)
|
||||
copyStorage := ownerAcl.storage.(*inMemoryStorage).Copy()
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, copyStorage, NoOpAcceptorVerifier{})
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, copyStorage, recordverifier.NewValidateFull())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -372,6 +394,51 @@ func (a *AclTestExecutor) Execute(cmd string) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "invite_change":
|
||||
var permissions AclPermissions
|
||||
inviteParts := strings.Split(args[0], ",")
|
||||
if inviteParts[1] == "r" {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Reader)
|
||||
} else if inviteParts[1] == "rw" {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Writer)
|
||||
} else {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Admin)
|
||||
}
|
||||
invite := a.invites[inviteParts[0]]
|
||||
invId, err := acl.AclState().GetInviteIdByPrivKey(invite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := acl.RecordBuilder().BuildInviteChange(InviteChangePayload{
|
||||
IniviteRecordId: invId,
|
||||
Permissions: permissions,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = addRec(WrapAclRecord(res))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "invite_anyone":
|
||||
var permissions AclPermissions
|
||||
inviteParts := strings.Split(args[0], ",")
|
||||
if inviteParts[1] == "r" {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Reader)
|
||||
} else if inviteParts[1] == "rw" {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Writer)
|
||||
} else {
|
||||
permissions = AclPermissions(aclrecordproto.AclUserPermissions_Admin)
|
||||
}
|
||||
res, err := acl.RecordBuilder().BuildInviteAnyone(permissions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.invites[inviteParts[0]] = res.InviteKey
|
||||
err = addRec(WrapAclRecord(res.InviteRec))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "batch":
|
||||
afterAll, err = a.buildBatchRequest(args, acl, getPerm, addRec)
|
||||
if err != nil {
|
||||
|
@ -394,7 +461,7 @@ func (a *AclTestExecutor) Execute(cmd string) (err error) {
|
|||
}
|
||||
}
|
||||
if recId == "" {
|
||||
return fmt.Errorf("no join records for approve")
|
||||
return fmt.Errorf("no join records to approve")
|
||||
}
|
||||
perms := getPerm(argParts[1])
|
||||
res, err := acl.RecordBuilder().BuildRequestAccept(RequestAcceptPayload{
|
||||
|
@ -448,7 +515,7 @@ func (a *AclTestExecutor) Execute(cmd string) (err error) {
|
|||
return err
|
||||
}
|
||||
ownerAcl := a.actualAccounts[a.owner].Acl.(*aclList)
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, ownerAcl.storage, NoOpAcceptorVerifier{})
|
||||
accountAcl, err := BuildAclListWithIdentity(keys, ownerAcl.storage, recordverifier.NewValidateFull())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -488,6 +555,25 @@ func (a *AclTestExecutor) Execute(cmd string) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "invite_join":
|
||||
invite := a.invites[args[0]]
|
||||
inviteJoin, err := acl.RecordBuilder().BuildInviteJoin(InviteJoinPayload{
|
||||
InviteKey: invite,
|
||||
Metadata: []byte(account),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invId, err := acl.AclState().GetInviteIdByPrivKey(invite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = addRec(WrapAclRecord(inviteJoin))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.expectedAccounts[account].status = StatusActive
|
||||
a.expectedAccounts[account].perms = acl.AclState().invites[invId].Permissions
|
||||
case "remove":
|
||||
identities := strings.Split(args[0], ",")
|
||||
var pubKeys []crypto.PubKey
|
||||
|
|
|
@ -53,6 +53,7 @@ func TestAclExecutor(t *testing.T) {
|
|||
{"a.init::a", nil},
|
||||
// creating an invite
|
||||
{"a.invite::invId", nil},
|
||||
{"a.invite_anyone::oldInvId,r", nil},
|
||||
// cannot self join
|
||||
{"a.join::invId", ErrInsufficientPermissions},
|
||||
// now b can join
|
||||
|
@ -89,7 +90,7 @@ func TestAclExecutor(t *testing.T) {
|
|||
{"g.join::inv1Id", nil},
|
||||
{"g.cancel::g", nil},
|
||||
// e cannot approve cancelled request
|
||||
{"e.approve::g,rw", fmt.Errorf("no join records for approve")},
|
||||
{"e.approve::g,rw", fmt.Errorf("no join records to approve")},
|
||||
{"g.join::inv1Id", nil},
|
||||
{"e.decline::g", nil},
|
||||
// g cannot cancel declined request
|
||||
|
@ -128,6 +129,27 @@ func TestAclExecutor(t *testing.T) {
|
|||
{"a.changes::guest,none", ErrInsufficientPermissions},
|
||||
// can't change permission of existing user to guest, should be only possible to create it with add
|
||||
{"a.changes::r,g", ErrInsufficientPermissions},
|
||||
{"a.invite_anyone::invAnyoneId,rw", nil},
|
||||
{"new.invite_join::invAnyoneId", nil},
|
||||
// invite keys persist after user removal
|
||||
{"a.remove::new", nil},
|
||||
{"new1.invite_join::invAnyoneId", nil},
|
||||
{"a.revoke::invAnyoneId", nil},
|
||||
{"new2.invite_join::invAnyoneId", ErrNoSuchInvite},
|
||||
{"a.invite_change::oldInvId,a", nil},
|
||||
{"new2.invite_join::oldInvId", nil},
|
||||
{"new2.add::new3,r,new3m", nil},
|
||||
{"a.batch::revoke:oldInvId;invite_anyone:someId,a", nil},
|
||||
{"new4.invite_join::someId", nil},
|
||||
{"new4.add::super,r,superm", nil},
|
||||
// check that users can't join using request to join for anyone can join links
|
||||
{"new5.join::someId", ErrNoSuchInvite},
|
||||
{"a.invite::requestJoinId", nil},
|
||||
{"joiner.join::requestJoinId", nil},
|
||||
// check that users can join under a different link even after they created a request to join
|
||||
{"joiner.invite_join::someId", nil},
|
||||
// check that they can't be approved after they joined under a different link
|
||||
{"a.approve::joiner,rw", fmt.Errorf("no join records to approve")},
|
||||
}
|
||||
for _, cmd := range cmds {
|
||||
err := a.Execute(cmd.cmd)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/util/cidutil"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
|
@ -26,17 +27,6 @@ type RWLocker interface {
|
|||
RUnlock()
|
||||
}
|
||||
|
||||
type AcceptorVerifier interface {
|
||||
VerifyAcceptor(rec *consensusproto.RawRecord) (err error)
|
||||
}
|
||||
|
||||
type NoOpAcceptorVerifier struct {
|
||||
}
|
||||
|
||||
func (n NoOpAcceptorVerifier) VerifyAcceptor(rec *consensusproto.RawRecord) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
type AclList interface {
|
||||
RWLocker
|
||||
Id() string
|
||||
|
@ -75,6 +65,7 @@ type aclList struct {
|
|||
keyStorage crypto.KeyStorage
|
||||
aclState *AclState
|
||||
storage Storage
|
||||
verifier recordverifier.AcceptorVerifier
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
@ -84,10 +75,10 @@ type internalDeps struct {
|
|||
keyStorage crypto.KeyStorage
|
||||
stateBuilder *aclStateBuilder
|
||||
recordBuilder AclRecordBuilder
|
||||
acceptorVerifier AcceptorVerifier
|
||||
acceptorVerifier recordverifier.AcceptorVerifier
|
||||
}
|
||||
|
||||
func BuildAclListWithIdentity(acc *accountdata.AccountKeys, storage Storage, verifier AcceptorVerifier) (AclList, error) {
|
||||
func BuildAclListWithIdentity(acc *accountdata.AccountKeys, storage Storage, verifier recordverifier.AcceptorVerifier) (AclList, error) {
|
||||
keyStorage := crypto.NewKeyStorage()
|
||||
deps := internalDeps{
|
||||
storage: storage,
|
||||
|
@ -159,6 +150,7 @@ func build(deps internalDeps) (list AclList, err error) {
|
|||
stateBuilder: stateBuilder,
|
||||
recordBuilder: recBuilder,
|
||||
storage: storage,
|
||||
verifier: deps.acceptorVerifier,
|
||||
id: id,
|
||||
}
|
||||
stateBuilder.Init(id)
|
||||
|
@ -186,6 +178,7 @@ func (a *aclList) ValidateRawRecord(rawRec *consensusproto.RawRecord, afterValid
|
|||
return
|
||||
}
|
||||
stateCopy := a.aclState.Copy()
|
||||
stateCopy.contentValidator = newContentValidator(stateCopy.keyStore, stateCopy, recordverifier.NewValidateFull())
|
||||
err = stateCopy.ApplyRecord(record)
|
||||
if err != nil || afterValid == nil {
|
||||
return
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/anyproto/any-sync/commonspace/headsync/headstorage"
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
@ -186,8 +187,8 @@ func TestAclList_InviteRevoke(t *testing.T) {
|
|||
// checking acl state
|
||||
require.True(t, ownerState().Permissions(ownerState().pubKey).IsOwner())
|
||||
require.True(t, ownerState().Permissions(accountState().pubKey).NoPermissions())
|
||||
require.Empty(t, ownerState().inviteKeys)
|
||||
require.Empty(t, accountState().inviteKeys)
|
||||
require.Empty(t, ownerState().invites)
|
||||
require.Empty(t, accountState().invites)
|
||||
}
|
||||
|
||||
func TestAclList_RequestDecline(t *testing.T) {
|
||||
|
@ -279,7 +280,7 @@ func TestAclList_FixAcceptPanic(t *testing.T) {
|
|||
fx := newFixture(t)
|
||||
fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Writer))
|
||||
|
||||
_, err := BuildAclListWithIdentity(fx.accountKeys, fx.ownerAcl.storage, NoOpAcceptorVerifier{})
|
||||
_, err := BuildAclListWithIdentity(fx.accountKeys, fx.ownerAcl.storage, recordverifier.NewValidateFull())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
@ -309,6 +310,27 @@ func TestAclList_MetadataDecrypt(t *testing.T) {
|
|||
require.NotEqual(t, mockMetadata, meta)
|
||||
}
|
||||
|
||||
func TestAclList_ValidateUsesCorrectVerifier(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
var ownerAcl = fx.ownerAcl
|
||||
ownerAcl.aclState.contentValidator.(*contentValidator).verifier = recordverifier.New()
|
||||
ownerAcl.verifier = recordverifier.New()
|
||||
// building invite
|
||||
inv, err := ownerAcl.RecordBuilder().BuildInvite()
|
||||
require.NoError(t, err)
|
||||
isCalled := false
|
||||
ok := ownerAcl.aclState.contentValidator.(*contentValidator).verifier.ShouldValidate()
|
||||
require.False(t, ok)
|
||||
err = fx.ownerAcl.ValidateRawRecord(inv.InviteRec, func(state *AclState) error {
|
||||
isCalled = true
|
||||
// check that we change the validator to the verifying one
|
||||
require.True(t, state.contentValidator.(*contentValidator).verifier.ShouldValidate())
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.True(t, isCalled)
|
||||
}
|
||||
|
||||
func TestAclList_ReadKeyChange(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
var (
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
@ -17,7 +20,7 @@ func newAclWithStoreProvider(root *consensusproto.RawRecordWithId, keys *account
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return BuildAclListWithIdentity(keys, storage, NoOpAcceptorVerifier{})
|
||||
return BuildAclListWithIdentity(keys, storage, recordverifier.NewValidateFull())
|
||||
}
|
||||
|
||||
func newDerivedAclWithStoreProvider(spaceId string, keys *accountdata.AccountKeys, metadata []byte, storeProvider StorageProvider) (AclList, error) {
|
||||
|
@ -43,11 +46,11 @@ func newInMemoryAclWithRoot(keys *accountdata.AccountKeys, root *consensusproto.
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return BuildAclListWithIdentity(keys, st, NoOpAcceptorVerifier{})
|
||||
return BuildAclListWithIdentity(keys, st, recordverifier.NewValidateFull())
|
||||
}
|
||||
|
||||
func buildDerivedRoot(spaceId string, keys *accountdata.AccountKeys, metadata []byte) (root *consensusproto.RawRecordWithId, err error) {
|
||||
builder := NewAclRecordBuilder("", crypto.NewKeyStorage(), keys, NoOpAcceptorVerifier{})
|
||||
builder := NewAclRecordBuilder("", crypto.NewKeyStorage(), keys, recordverifier.NewValidateFull())
|
||||
masterKey, _, err := crypto.GenerateRandomEd25519KeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -68,3 +71,30 @@ func buildDerivedRoot(spaceId string, keys *accountdata.AccountKeys, metadata []
|
|||
Metadata: metadata,
|
||||
})
|
||||
}
|
||||
|
||||
func NewTestAclStateWithUsers(numWriters, numReaders, numInvites int) *AclState {
|
||||
st := &AclState{
|
||||
keys: make(map[string]AclKeys),
|
||||
accountStates: make(map[string]AccountState),
|
||||
invites: make(map[string]Invite),
|
||||
requestRecords: make(map[string]RequestRecord),
|
||||
pendingRequests: make(map[string]string),
|
||||
keyStore: crypto.NewKeyStorage(),
|
||||
}
|
||||
for i := 0; i < numWriters; i++ {
|
||||
st.accountStates[fmt.Sprint("w", i)] = AccountState{
|
||||
Permissions: AclPermissionsWriter,
|
||||
Status: StatusActive,
|
||||
}
|
||||
}
|
||||
for i := 0; i < numReaders; i++ {
|
||||
st.accountStates[fmt.Sprint("r", i)] = AccountState{
|
||||
Permissions: AclPermissionsReader,
|
||||
Status: StatusActive,
|
||||
}
|
||||
}
|
||||
for i := 0; i < numInvites; i++ {
|
||||
st.invites[fmt.Sprint("r", i)] = Invite{}
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ type AclRecord struct {
|
|||
AcceptorTimestamp int64
|
||||
Data []byte
|
||||
Identity crypto.PubKey
|
||||
AcceptorIdentity crypto.PubKey
|
||||
AcceptorSignature []byte
|
||||
Model interface{}
|
||||
Signature []byte
|
||||
}
|
||||
|
@ -83,6 +85,10 @@ func (p AclPermissions) IsOwner() bool {
|
|||
return aclrecordproto.AclUserPermissions(p) == aclrecordproto.AclUserPermissions_Owner
|
||||
}
|
||||
|
||||
func (p AclPermissions) IsGuest() bool {
|
||||
return aclrecordproto.AclUserPermissions(p) == aclrecordproto.AclUserPermissions_Guest
|
||||
}
|
||||
|
||||
func (p AclPermissions) CanWrite() bool {
|
||||
switch aclrecordproto.AclUserPermissions(p) {
|
||||
case aclrecordproto.AclUserPermissions_Admin:
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
||||
|
@ -11,6 +14,8 @@ type ContentValidator interface {
|
|||
ValidatePermissionChanges(ch *aclrecordproto.AclAccountPermissionChanges, authorIdentity crypto.PubKey) (err error)
|
||||
ValidateAccountsAdd(ch *aclrecordproto.AclAccountsAdd, authorIdentity crypto.PubKey) (err error)
|
||||
ValidateInvite(ch *aclrecordproto.AclAccountInvite, authorIdentity crypto.PubKey) (err error)
|
||||
ValidateInviteJoin(ch *aclrecordproto.AclAccountInviteJoin, authorIdentity crypto.PubKey) (err error)
|
||||
ValidateInviteChange(ch *aclrecordproto.AclAccountInviteChange, authorIdentity crypto.PubKey) (err error)
|
||||
ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error)
|
||||
ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, authorIdentity crypto.PubKey) (err error)
|
||||
ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, authorIdentity crypto.PubKey) (err error)
|
||||
|
@ -24,16 +29,21 @@ type ContentValidator interface {
|
|||
type contentValidator struct {
|
||||
keyStore crypto.KeyStorage
|
||||
aclState *AclState
|
||||
verifier recordverifier.AcceptorVerifier
|
||||
}
|
||||
|
||||
func newContentValidator(keyStore crypto.KeyStorage, aclState *AclState) ContentValidator {
|
||||
func newContentValidator(keyStore crypto.KeyStorage, aclState *AclState, verifier recordverifier.AcceptorVerifier) ContentValidator {
|
||||
return &contentValidator{
|
||||
keyStore: keyStore,
|
||||
aclState: aclState,
|
||||
verifier: verifier,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *contentValidator) ValidatePermissionChanges(ch *aclrecordproto.AclAccountPermissionChanges, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
for _, ch := range ch.Changes {
|
||||
err := c.ValidatePermissionChange(ch, authorIdentity)
|
||||
if err != nil {
|
||||
|
@ -44,6 +54,9 @@ func (c *contentValidator) ValidatePermissionChanges(ch *aclrecordproto.AclAccou
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateAccountsAdd(ch *aclrecordproto.AclAccountsAdd, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
|
@ -66,7 +79,54 @@ func (c *contentValidator) ValidateAccountsAdd(ch *aclrecordproto.AclAccountsAdd
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *contentValidator) ValidateInviteJoin(ch *aclrecordproto.AclAccountInviteJoin, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).NoPermissions() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
invite, exists := c.aclState.invites[ch.InviteRecordId]
|
||||
if !exists {
|
||||
return ErrNoSuchInvite
|
||||
}
|
||||
if invite.Type != aclrecordproto.AclInviteType_AnyoneCanJoin {
|
||||
return ErrNoSuchInvite
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).NoPermissions() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
inviteIdentity, err := c.keyStore.PubKeyFromProto(ch.Identity)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !authorIdentity.Equals(inviteIdentity) {
|
||||
return ErrIncorrectIdentity
|
||||
}
|
||||
rawInviteIdentity, err := inviteIdentity.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := invite.Key.Verify(rawInviteIdentity, ch.InviteIdentitySignature)
|
||||
if err != nil {
|
||||
return ErrInvalidSignature
|
||||
}
|
||||
if !ok {
|
||||
return ErrInvalidSignature
|
||||
}
|
||||
if len(ch.Metadata) > MaxMetadataLen {
|
||||
return ErrMetadataTooLarge
|
||||
}
|
||||
if ch.EncryptedReadKey == nil {
|
||||
return ErrIncorrectReadKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *contentValidator) ValidateAclRecordContents(ch *AclRecord) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if ch.PrevId != c.aclState.lastRecordId {
|
||||
return ErrIncorrectRecordSequence
|
||||
}
|
||||
|
@ -90,6 +150,8 @@ func (c *contentValidator) validateAclRecordContent(ch *aclrecordproto.AclConten
|
|||
return c.ValidateInviteRevoke(ch.GetInviteRevoke(), authorIdentity)
|
||||
case ch.GetRequestJoin() != nil:
|
||||
return c.ValidateRequestJoin(ch.GetRequestJoin(), authorIdentity)
|
||||
case ch.GetInviteJoin() != nil:
|
||||
return c.ValidateInviteJoin(ch.GetInviteJoin(), authorIdentity)
|
||||
case ch.GetRequestAccept() != nil:
|
||||
return c.ValidateRequestAccept(ch.GetRequestAccept(), authorIdentity)
|
||||
case ch.GetRequestDecline() != nil:
|
||||
|
@ -110,6 +172,9 @@ func (c *contentValidator) validateAclRecordContent(ch *aclrecordproto.AclConten
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
|
@ -149,18 +214,57 @@ func (c *contentValidator) ValidatePermissionChange(ch *aclrecordproto.AclAccoun
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateInvite(ch *aclrecordproto.AclAccountInvite, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
permissions := AclPermissions(ch.Permissions)
|
||||
if ch.InviteType == aclrecordproto.AclInviteType_AnyoneCanJoin {
|
||||
if permissions.IsOwner() || permissions.NoPermissions() || permissions.IsGuest() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
if ch.EncryptedReadKey == nil {
|
||||
return ErrIncorrectReadKey
|
||||
}
|
||||
}
|
||||
_, err = c.keyStore.PubKeyFromProto(ch.InviteKey)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error) {
|
||||
func (c *contentValidator) ValidateInviteChange(ch *aclrecordproto.AclAccountInviteChange, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
_, exists := c.aclState.inviteKeys[ch.InviteRecordId]
|
||||
invite, exists := c.aclState.invites[ch.InviteRecordId]
|
||||
if !exists {
|
||||
return ErrNoSuchInvite
|
||||
}
|
||||
permissions := AclPermissions(ch.Permissions)
|
||||
if invite.Type != aclrecordproto.AclInviteType_AnyoneCanJoin {
|
||||
return ErrNoSuchInvite
|
||||
}
|
||||
if invite.Permissions == permissions {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
if permissions.IsOwner() || permissions.NoPermissions() || permissions.IsGuest() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
_, exists := c.aclState.invites[ch.InviteRecordId]
|
||||
if !exists {
|
||||
return ErrNoSuchInvite
|
||||
}
|
||||
|
@ -168,13 +272,19 @@ func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInv
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, authorIdentity crypto.PubKey) (err error) {
|
||||
inviteKey, exists := c.aclState.inviteKeys[ch.InviteRecordId]
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
invite, exists := c.aclState.invites[ch.InviteRecordId]
|
||||
if !exists {
|
||||
return ErrNoSuchInvite
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).NoPermissions() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
if invite.Type != aclrecordproto.AclInviteType_RequestToJoin {
|
||||
return ErrNoSuchInvite
|
||||
}
|
||||
inviteIdentity, err := c.keyStore.PubKeyFromProto(ch.InviteIdentity)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -189,7 +299,7 @@ func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequ
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := inviteKey.Verify(rawInviteIdentity, ch.InviteIdentitySignature)
|
||||
ok, err := invite.Key.Verify(rawInviteIdentity, ch.InviteIdentitySignature)
|
||||
if err != nil {
|
||||
return ErrInvalidSignature
|
||||
}
|
||||
|
@ -203,6 +313,9 @@ func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequ
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
|
@ -224,6 +337,9 @@ func (c *contentValidator) ValidateRequestAccept(ch *aclrecordproto.AclAccountRe
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
|
@ -235,6 +351,9 @@ func (c *contentValidator) ValidateRequestDecline(ch *aclrecordproto.AclAccountR
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateRequestCancel(ch *aclrecordproto.AclAccountRequestCancel, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
rec, exists := c.aclState.requestRecords[ch.RecordId]
|
||||
if !exists {
|
||||
return ErrNoSuchRequest
|
||||
|
@ -246,6 +365,9 @@ func (c *contentValidator) ValidateRequestCancel(ch *aclrecordproto.AclAccountRe
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateAccountRemove(ch *aclrecordproto.AclAccountRemove, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
|
@ -275,6 +397,9 @@ func (c *contentValidator) ValidateAccountRemove(ch *aclrecordproto.AclAccountRe
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
if c.aclState.Permissions(authorIdentity).NoPermissions() {
|
||||
return ErrInsufficientPermissions
|
||||
}
|
||||
|
@ -288,10 +413,16 @@ func (c *contentValidator) ValidateRequestRemove(ch *aclrecordproto.AclAccountRe
|
|||
}
|
||||
|
||||
func (c *contentValidator) ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, authorIdentity crypto.PubKey) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
return c.validateReadKeyChange(ch, nil)
|
||||
}
|
||||
|
||||
func (c *contentValidator) validateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, removedUsers map[string]struct{}) (err error) {
|
||||
if !c.verifier.ShouldValidate() {
|
||||
return nil
|
||||
}
|
||||
_, err = c.keyStore.PubKeyFromProto(ch.MetadataPubKey)
|
||||
if err != nil {
|
||||
return ErrNoMetadataKey
|
||||
|
@ -299,23 +430,55 @@ func (c *contentValidator) validateReadKeyChange(ch *aclrecordproto.AclReadKeyCh
|
|||
if ch.EncryptedMetadataPrivKey == nil || ch.EncryptedOldReadKey == nil {
|
||||
return ErrIncorrectReadKey
|
||||
}
|
||||
var (
|
||||
activeUsers []string
|
||||
updatedInvites []string
|
||||
activeInvites []string
|
||||
updatedUsers []string
|
||||
)
|
||||
for _, accState := range c.aclState.accountStates {
|
||||
if accState.Permissions.NoPermissions() {
|
||||
continue
|
||||
}
|
||||
pubKey := mapKeyFromPubKey(accState.PubKey)
|
||||
if _, exists := removedUsers[pubKey]; exists {
|
||||
continue
|
||||
}
|
||||
activeUsers = append(activeUsers, pubKey)
|
||||
}
|
||||
for _, invite := range c.aclState.invites {
|
||||
if invite.Type == aclrecordproto.AclInviteType_AnyoneCanJoin {
|
||||
activeInvites = append(activeInvites, mapKeyFromPubKey(invite.Key))
|
||||
}
|
||||
}
|
||||
for _, encKeys := range ch.AccountKeys {
|
||||
identity, err := c.keyStore.PubKeyFromProto(encKeys.Identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
idKey := mapKeyFromPubKey(identity)
|
||||
_, exists := c.aclState.accountStates[idKey]
|
||||
if !exists {
|
||||
return ErrNoSuchAccount
|
||||
}
|
||||
if removedUsers == nil {
|
||||
continue
|
||||
}
|
||||
_, exists = removedUsers[idKey]
|
||||
if exists {
|
||||
return ErrIncorrectNumberOfAccounts
|
||||
updatedUsers = append(updatedUsers, idKey)
|
||||
}
|
||||
for _, invKeys := range ch.InviteKeys {
|
||||
identity, err := c.keyStore.PubKeyFromProto(invKeys.Identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
idKey := mapKeyFromPubKey(identity)
|
||||
updatedInvites = append(updatedInvites, idKey)
|
||||
}
|
||||
if len(activeUsers) != len(updatedUsers) {
|
||||
return ErrIncorrectNumberOfAccounts
|
||||
}
|
||||
if len(activeInvites) != len(updatedInvites) {
|
||||
return ErrIncorrectNumberOfAccounts
|
||||
}
|
||||
slices.Sort(updatedUsers)
|
||||
slices.Sort(updatedInvites)
|
||||
slices.Sort(activeUsers)
|
||||
slices.Sort(activeInvites)
|
||||
if !slices.Equal(activeUsers, updatedUsers) || !slices.Equal(activeInvites, updatedInvites) {
|
||||
return ErrIncorrectNumberOfAccounts
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
58
commonspace/object/acl/recordverifier/recordverifier.go
Normal file
58
commonspace/object/acl/recordverifier/recordverifier.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package recordverifier
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
||||
const CName = "common.acl.recordverifier"
|
||||
|
||||
type AcceptorVerifier interface {
|
||||
VerifyAcceptor(rec *consensusproto.RawRecord) (err error)
|
||||
ShouldValidate() bool
|
||||
}
|
||||
|
||||
type RecordVerifier interface {
|
||||
app.Component
|
||||
AcceptorVerifier
|
||||
}
|
||||
|
||||
func New() RecordVerifier {
|
||||
return &recordVerifier{
|
||||
store: crypto.NewKeyStorage(),
|
||||
}
|
||||
}
|
||||
|
||||
type recordVerifier struct {
|
||||
store crypto.KeyStorage
|
||||
}
|
||||
|
||||
func (r *recordVerifier) Init(a *app.App) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *recordVerifier) Name() (name string) {
|
||||
return CName
|
||||
}
|
||||
|
||||
func (r *recordVerifier) VerifyAcceptor(rec *consensusproto.RawRecord) (err error) {
|
||||
identity, err := r.store.PubKeyFromProto(rec.AcceptorIdentity)
|
||||
if err != nil {
|
||||
identity, err = crypto.UnmarshalEd25519PublicKey(rec.AcceptorIdentity)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get acceptor identity: %w", err)
|
||||
}
|
||||
}
|
||||
verified, err := identity.Verify(rec.Payload, rec.AcceptorSignature)
|
||||
if !verified || err != nil {
|
||||
return fmt.Errorf("failed to verify acceptor: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *recordVerifier) ShouldValidate() bool {
|
||||
return false
|
||||
}
|
106
commonspace/object/acl/recordverifier/recordverifier_test.go
Normal file
106
commonspace/object/acl/recordverifier/recordverifier_test.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package recordverifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
"github.com/anyproto/any-sync/testutil/accounttest"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
*recordVerifier
|
||||
app *app.App
|
||||
networkPrivKey crypto.PrivKey
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
accService := &accounttest.AccountTestService{}
|
||||
a := &app.App{}
|
||||
verifier := New().(*recordVerifier)
|
||||
a.Register(accService).
|
||||
Register(verifier)
|
||||
require.NoError(t, a.Start(context.Background()))
|
||||
return &fixture{
|
||||
recordVerifier: verifier,
|
||||
app: a,
|
||||
networkPrivKey: accService.Account().SignKey,
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordVerifier_VerifyAcceptor(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
identity, err := fx.networkPrivKey.GetPublic().Marshall()
|
||||
require.NoError(t, err)
|
||||
testPayload := []byte("test payload")
|
||||
acceptorSignature, err := fx.networkPrivKey.Sign(testPayload)
|
||||
require.NoError(t, err)
|
||||
rawRecord := &consensusproto.RawRecord{
|
||||
AcceptorIdentity: identity,
|
||||
Payload: testPayload,
|
||||
AcceptorSignature: acceptorSignature,
|
||||
}
|
||||
err = fx.VerifyAcceptor(rawRecord)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRecordVerifier_VerifyAcceptor_InvalidSignature(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
identity, err := fx.networkPrivKey.GetPublic().Marshall()
|
||||
require.NoError(t, err)
|
||||
testPayload := []byte("test payload")
|
||||
rawRecord := &consensusproto.RawRecord{
|
||||
AcceptorIdentity: identity,
|
||||
Payload: testPayload,
|
||||
AcceptorSignature: []byte("invalid signature"),
|
||||
}
|
||||
err = fx.VerifyAcceptor(rawRecord)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRecordVerifier_VerifyAcceptor_ModifiedPayload(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
identity, err := fx.networkPrivKey.GetPublic().Marshall()
|
||||
require.NoError(t, err)
|
||||
testPayload := []byte("test payload")
|
||||
acceptorSignature, err := fx.networkPrivKey.Sign(testPayload)
|
||||
require.NoError(t, err)
|
||||
rawRecord := &consensusproto.RawRecord{
|
||||
AcceptorIdentity: identity,
|
||||
Payload: []byte("modified payload"),
|
||||
AcceptorSignature: acceptorSignature,
|
||||
}
|
||||
err = fx.VerifyAcceptor(rawRecord)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRecordVerifier_VerifyAcceptor_InvalidIdentity(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
testPayload := []byte("test payload")
|
||||
acceptorSignature, err := fx.networkPrivKey.Sign(testPayload)
|
||||
require.NoError(t, err)
|
||||
rawRecord := &consensusproto.RawRecord{
|
||||
AcceptorIdentity: []byte("invalid identity"),
|
||||
Payload: testPayload,
|
||||
AcceptorSignature: acceptorSignature,
|
||||
}
|
||||
err = fx.VerifyAcceptor(rawRecord)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRecordVerifier_VerifyAcceptor_EmptySignature(t *testing.T) {
|
||||
fx := newFixture(t)
|
||||
identity, err := fx.networkPrivKey.GetPublic().Marshall()
|
||||
require.NoError(t, err)
|
||||
rawRecord := &consensusproto.RawRecord{
|
||||
AcceptorIdentity: identity,
|
||||
Payload: []byte("test payload"),
|
||||
AcceptorSignature: nil,
|
||||
}
|
||||
err = fx.VerifyAcceptor(rawRecord)
|
||||
require.Error(t, err)
|
||||
}
|
28
commonspace/object/acl/recordverifier/validatefull.go
Normal file
28
commonspace/object/acl/recordverifier/validatefull.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package recordverifier
|
||||
|
||||
import (
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
)
|
||||
|
||||
type ValidateFull struct{}
|
||||
|
||||
func NewValidateFull() RecordVerifier {
|
||||
return &ValidateFull{}
|
||||
}
|
||||
|
||||
func (a *ValidateFull) Init(_ *app.App) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ValidateFull) Name() string {
|
||||
return CName
|
||||
}
|
||||
|
||||
func (a *ValidateFull) VerifyAcceptor(_ *consensusproto.RawRecord) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ValidateFull) ShouldValidate() bool {
|
||||
return true
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/app/logger"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/syncacl/headupdater"
|
||||
"github.com/anyproto/any-sync/commonspace/spacestorage"
|
||||
"github.com/anyproto/any-sync/commonspace/sync"
|
||||
|
@ -67,7 +68,8 @@ func (s *syncAcl) Init(a *app.App) (err error) {
|
|||
return err
|
||||
}
|
||||
acc := a.MustComponent(accountservice.CName).(accountservice.Service)
|
||||
s.AclList, err = list.BuildAclListWithIdentity(acc.Account(), aclStorage, list.NoOpAcceptorVerifier{})
|
||||
verifier := a.MustComponent(recordverifier.CName).(recordverifier.RecordVerifier)
|
||||
s.AclList, err = list.BuildAclListWithIdentity(acc.Account(), aclStorage, verifier)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/keyvalue/keyvaluestorage"
|
||||
"github.com/anyproto/any-sync/commonspace/object/keyvalue/keyvaluestorage/innerstorage"
|
||||
"github.com/anyproto/any-sync/commonspace/spacepayloads"
|
||||
|
@ -280,7 +281,7 @@ func newFixture(t *testing.T, keys *accountdata.AccountKeys, spacePayload spaces
|
|||
require.NoError(t, err)
|
||||
aclStorage, err := storage.AclStorage()
|
||||
require.NoError(t, err)
|
||||
aclList, err := list.BuildAclListWithIdentity(keys, aclStorage, list.NoOpAcceptorVerifier{})
|
||||
aclList, err := list.BuildAclListWithIdentity(keys, aclStorage, recordverifier.NewValidateFull())
|
||||
require.NoError(t, err)
|
||||
storageId := "kv.storage"
|
||||
rpcHandler := rpctest.NewTestServer()
|
||||
|
|
|
@ -36,6 +36,9 @@ type Change struct {
|
|||
OrderId string
|
||||
SnapshotCounter int
|
||||
|
||||
// using this on build stage
|
||||
rawChange *treechangeproto.RawTreeChangeWithId
|
||||
|
||||
// iterator helpers
|
||||
visited bool
|
||||
branchesFinished bool
|
||||
|
|
|
@ -130,14 +130,13 @@ type objectTree struct {
|
|||
difSnapshotBuf []*treechangeproto.RawTreeChangeWithId
|
||||
newChangesBuf []*Change
|
||||
newSnapshotsBuf []*Change
|
||||
notSeenIdxBuf []int
|
||||
|
||||
snapshotPath []string
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (ot *objectTree) rebuildFromStorage(theirHeads, theirSnapshotPath []string, newChanges []*Change) (err error) {
|
||||
func (ot *objectTree) rebuildFromStorage(theirHeads, theirSnapshotPath []string, newChanges []*Change) (added []*Change, err error) {
|
||||
var (
|
||||
ourPath []string
|
||||
oldTree = ot.tree
|
||||
|
@ -146,10 +145,10 @@ func (ot *objectTree) rebuildFromStorage(theirHeads, theirSnapshotPath []string,
|
|||
// TODO: add error handling
|
||||
ourPath, err = ot.SnapshotPath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("rebuild from storage: %w", err)
|
||||
return nil, fmt.Errorf("rebuild from storage: %w", err)
|
||||
}
|
||||
}
|
||||
ot.tree, err = ot.treeBuilder.Build(treeBuilderOpts{
|
||||
ot.tree, added, err = ot.treeBuilder.buildWithAdded(treeBuilderOpts{
|
||||
theirHeads: theirHeads,
|
||||
ourSnapshotPath: ourPath,
|
||||
theirSnapshotPath: theirSnapshotPath,
|
||||
|
@ -178,7 +177,7 @@ func (ot *objectTree) rebuildFromStorage(theirHeads, theirSnapshotPath []string,
|
|||
|
||||
// it is a good question whether we need to validate everything
|
||||
// because maybe we can trust the stuff that is already in the storage
|
||||
return ot.validateTree(nil)
|
||||
return added, ot.validateTree(nil)
|
||||
}
|
||||
|
||||
func (ot *objectTree) Id() string {
|
||||
|
@ -406,7 +405,7 @@ func (ot *objectTree) AddRawChangesWithUpdater(ctx context.Context, changes RawC
|
|||
}
|
||||
|
||||
rollback := func() {
|
||||
rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
_, rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
if rebuildErr != nil {
|
||||
log.Error("failed to rebuild after adding to storage", zap.Strings("heads", ot.Heads()), zap.Error(rebuildErr))
|
||||
}
|
||||
|
@ -436,7 +435,6 @@ func (ot *objectTree) AddRawChanges(ctx context.Context, changesPayload RawChang
|
|||
func (ot *objectTree) addChangesToTree(ctx context.Context, changesPayload RawChangesPayload) (addResult AddResult, err error) {
|
||||
// resetting buffers
|
||||
ot.newChangesBuf = ot.newChangesBuf[:0]
|
||||
ot.notSeenIdxBuf = ot.notSeenIdxBuf[:0]
|
||||
ot.difSnapshotBuf = ot.difSnapshotBuf[:0]
|
||||
ot.newSnapshotsBuf = ot.newSnapshotsBuf[:0]
|
||||
|
||||
|
@ -453,7 +451,7 @@ func (ot *objectTree) addChangesToTree(ctx context.Context, changesPayload RawCh
|
|||
)
|
||||
|
||||
// filtering changes, verifying and unmarshalling them
|
||||
for idx, ch := range changesPayload.RawChanges {
|
||||
for _, ch := range changesPayload.RawChanges {
|
||||
// not unmarshalling the changes if they were already added either as unattached or attached
|
||||
if _, exists := ot.tree.attached[ch.Id]; exists {
|
||||
continue
|
||||
|
@ -468,16 +466,16 @@ func (ot *objectTree) addChangesToTree(ctx context.Context, changesPayload RawCh
|
|||
return
|
||||
}
|
||||
}
|
||||
change.rawChange = ch
|
||||
|
||||
if change.IsSnapshot {
|
||||
ot.newSnapshotsBuf = append(ot.newSnapshotsBuf, change)
|
||||
}
|
||||
ot.newChangesBuf = append(ot.newChangesBuf, change)
|
||||
ot.notSeenIdxBuf = append(ot.notSeenIdxBuf, idx)
|
||||
}
|
||||
|
||||
// if no new changes, then returning
|
||||
if len(ot.notSeenIdxBuf) == 0 {
|
||||
if len(ot.newChangesBuf) == 0 {
|
||||
addResult = AddResult{
|
||||
OldHeads: prevHeadsCopy,
|
||||
Heads: prevHeadsCopy,
|
||||
|
@ -491,7 +489,7 @@ func (ot *objectTree) addChangesToTree(ctx context.Context, changesPayload RawCh
|
|||
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, ot.newChangesBuf, ot.newSnapshotsBuf, ot.notSeenIdxBuf)
|
||||
filteredHeads, ot.newChangesBuf, ot.newSnapshotsBuf = ot.validator.FilterChanges(ot.aclList, ot.newChangesBuf, ot.newSnapshotsBuf)
|
||||
if filteredHeads {
|
||||
// if we filtered some of the heads, then we don't know which heads to use
|
||||
headsToUse = []string{}
|
||||
|
@ -553,22 +551,23 @@ func (ot *objectTree) addChangesToTree(ctx context.Context, changesPayload RawCh
|
|||
}
|
||||
log := log.With(zap.String("treeId", ot.id))
|
||||
if shouldRebuildFromStorage {
|
||||
err = ot.rebuildFromStorage(headsToUse, changesPayload.SnapshotPath, ot.newChangesBuf)
|
||||
var added []*Change
|
||||
added, err = ot.rebuildFromStorage(headsToUse, changesPayload.SnapshotPath, ot.newChangesBuf)
|
||||
if err != nil {
|
||||
log.Error("failed to rebuild with new heads", zap.Strings("headsToUse", headsToUse), zap.Error(err))
|
||||
// rebuilding without new changes
|
||||
rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
_, rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
if rebuildErr != nil {
|
||||
log.Error("failed to rebuild from storage", zap.Strings("heads", ot.Heads()), zap.Error(rebuildErr))
|
||||
}
|
||||
return
|
||||
}
|
||||
addResult, err = ot.createAddResult(prevHeadsCopy, Rebuild, changesPayload.RawChanges)
|
||||
addResult, err = ot.createAddResult(prevHeadsCopy, Rebuild, added)
|
||||
if err != nil {
|
||||
log.Error("failed to create add result", zap.Strings("headsToUse", headsToUse), zap.Error(err))
|
||||
// that means that some unattached changes were somehow corrupted in memory
|
||||
// this shouldn't happen but if that happens, then rebuilding from storage
|
||||
rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
_, rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
if rebuildErr != nil {
|
||||
log.Error("failed to rebuild after add result", zap.Strings("heads", ot.Heads()), zap.Error(rebuildErr))
|
||||
}
|
||||
|
@ -595,12 +594,12 @@ func (ot *objectTree) addChangesToTree(ctx context.Context, changesPayload RawCh
|
|||
err = fmt.Errorf("%w: %w", ErrHasInvalidChanges, err)
|
||||
return
|
||||
}
|
||||
addResult, err = ot.createAddResult(prevHeadsCopy, mode, changesPayload.RawChanges)
|
||||
addResult, err = ot.createAddResult(prevHeadsCopy, mode, treeChangesAdded)
|
||||
if err != nil {
|
||||
// that means that some unattached changes were somehow corrupted in memory
|
||||
// this shouldn't happen but if that happens, then rebuilding from storage
|
||||
rollback(treeChangesAdded)
|
||||
rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
_, rebuildErr := ot.rebuildFromStorage(nil, nil, nil)
|
||||
if rebuildErr != nil {
|
||||
log.Error("failed to rebuild after add result (add to tree)", zap.Strings("heads", ot.Heads()), zap.Error(rebuildErr))
|
||||
}
|
||||
|
@ -610,43 +609,27 @@ func (ot *objectTree) addChangesToTree(ctx context.Context, changesPayload RawCh
|
|||
}
|
||||
}
|
||||
|
||||
func (ot *objectTree) createAddResult(oldHeads []string, mode Mode, rawChanges []*treechangeproto.RawTreeChangeWithId) (addResult AddResult, err error) {
|
||||
headsCopy := func() []string {
|
||||
newHeads := make([]string, 0, len(ot.tree.Heads()))
|
||||
newHeads = append(newHeads, ot.tree.Heads()...)
|
||||
return newHeads
|
||||
}
|
||||
|
||||
// returns changes that we added to the tree as attached this round
|
||||
// they can include not only the changes that were added now,
|
||||
// but also the changes that were previously in the tree
|
||||
getAddedChanges := func() (added []StorageChange, err error) {
|
||||
for _, idx := range ot.notSeenIdxBuf {
|
||||
rawChange := rawChanges[idx]
|
||||
if ch, exists := ot.tree.attached[rawChange.Id]; exists {
|
||||
ot.flusher.MarkNewChange(ch)
|
||||
added = append(added, StorageChange{
|
||||
RawChange: rawChange.RawChange,
|
||||
PrevIds: ch.PreviousIds,
|
||||
Id: ch.Id,
|
||||
SnapshotCounter: ch.SnapshotCounter,
|
||||
SnapshotId: ch.SnapshotId,
|
||||
OrderId: ch.OrderId,
|
||||
ChangeSize: len(rawChange.RawChange),
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ot *objectTree) createAddResult(oldHeads []string, mode Mode, changes []*Change) (addResult AddResult, err error) {
|
||||
newHeads := make([]string, 0, len(ot.tree.Heads()))
|
||||
newHeads = append(newHeads, ot.tree.Heads()...)
|
||||
var added []StorageChange
|
||||
added, err = getAddedChanges()
|
||||
if err != nil {
|
||||
return
|
||||
for _, ch := range changes {
|
||||
rawChange := ch.rawChange
|
||||
ot.flusher.MarkNewChange(ch)
|
||||
added = append(added, StorageChange{
|
||||
RawChange: rawChange.RawChange,
|
||||
PrevIds: ch.PreviousIds,
|
||||
Id: ch.Id,
|
||||
SnapshotCounter: ch.SnapshotCounter,
|
||||
SnapshotId: ch.SnapshotId,
|
||||
OrderId: ch.OrderId,
|
||||
ChangeSize: len(rawChange.RawChange),
|
||||
})
|
||||
ch.rawChange = nil
|
||||
}
|
||||
addResult = AddResult{
|
||||
OldHeads: oldHeads,
|
||||
Heads: headsCopy(),
|
||||
Heads: newHeads,
|
||||
Added: added,
|
||||
Mode: mode,
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/anyproto/any-sync/commonspace/headsync/headstorage"
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
|
||||
)
|
||||
|
@ -390,7 +391,7 @@ func TestObjectTree(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
prevId = rec.Id
|
||||
}
|
||||
beforeAcl, err := list.BuildAclListWithIdentity(account.Keys, beforeStorage, list.NoOpAcceptorVerifier{})
|
||||
beforeAcl, err := list.BuildAclListWithIdentity(account.Keys, beforeStorage, recordverifier.NewValidateFull())
|
||||
require.NoError(t, err)
|
||||
err = exec.Execute("a.invite::invId")
|
||||
require.NoError(t, err)
|
||||
|
@ -462,7 +463,7 @@ func TestObjectTree(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
storage, err := list.NewInMemoryStorage(prevAclRecs[0].Id, prevAclRecs)
|
||||
require.NoError(t, err)
|
||||
acl, err := list.BuildAclListWithIdentity(bAccount.Keys, storage, list.NoOpAcceptorVerifier{})
|
||||
acl, err := list.BuildAclListWithIdentity(bAccount.Keys, storage, recordverifier.NewValidateFull())
|
||||
require.NoError(t, err)
|
||||
// creating tree with old storage which doesn't have a new invite record
|
||||
bTree, err := BuildKeyFilterableObjectTree(bStore, acl)
|
||||
|
@ -487,10 +488,8 @@ func TestObjectTree(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
bObjTree := bTree.(*objectTree)
|
||||
// this is just a random slice, so the func works
|
||||
indexes := []int{1, 2, 3, 4, 5}
|
||||
// checking that we filter the changes
|
||||
filtered, filteredChanges, _, _ := bObjTree.validator.FilterChanges(bObjTree.aclList, collectedChanges, nil, indexes)
|
||||
filtered, filteredChanges, _ := bObjTree.validator.FilterChanges(bObjTree.aclList, collectedChanges, nil)
|
||||
require.True(t, filtered)
|
||||
for _, ch := range filteredChanges {
|
||||
require.NotEqual(t, unexpectedId, ch.Id)
|
||||
|
@ -929,6 +928,86 @@ func TestObjectTree(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("test fix incorrect changes added", func(t *testing.T) {
|
||||
treeCtx := prepareTreeContext(t, aclList)
|
||||
treeStorage := treeCtx.treeStorage
|
||||
changeCreator := treeCtx.changeCreator
|
||||
objTree := treeCtx.objTree
|
||||
|
||||
rawChangesFirst := []*treechangeproto.RawTreeChangeWithId{
|
||||
changeCreator.CreateRoot("0", aclList.Head().Id),
|
||||
changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"),
|
||||
}
|
||||
rawChangesSecond := []*treechangeproto.RawTreeChangeWithId{
|
||||
changeCreator.CreateRoot("0", aclList.Head().Id),
|
||||
changeCreator.CreateRaw("2", aclList.Head().Id, "0", true, "0"),
|
||||
}
|
||||
payloadFirst := RawChangesPayload{
|
||||
NewHeads: []string{"1"},
|
||||
RawChanges: rawChangesFirst,
|
||||
}
|
||||
payloadSecond := RawChangesPayload{
|
||||
NewHeads: []string{"2"},
|
||||
RawChanges: rawChangesSecond,
|
||||
}
|
||||
|
||||
res, err := objTree.AddRawChanges(context.Background(), payloadFirst)
|
||||
require.NoError(t, err, "adding changes should be without error")
|
||||
require.Equal(t, []string{"1"}, res.Heads)
|
||||
|
||||
res, err = objTree.AddRawChanges(context.Background(), payloadSecond)
|
||||
require.NoError(t, err, "adding changes should be without error")
|
||||
require.Equal(t, []string{"1", "2"}, res.Heads)
|
||||
|
||||
for _, ch := range append(rawChangesFirst, rawChangesSecond...) {
|
||||
raw, err := treeStorage.Get(context.Background(), ch.Id)
|
||||
assert.NoError(t, err, "storage should have all the changes")
|
||||
assert.Equal(t, ch.Id, raw.RawTreeChangeWithId().Id, "the changes in the storage should be the same")
|
||||
assert.Equal(t, ch.RawChange, raw.RawTreeChangeWithId().RawChange, "the changes in the storage should be the same")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test fix incorrect changes added with snapshot path", func(t *testing.T) {
|
||||
treeCtx := prepareTreeContext(t, aclList)
|
||||
treeStorage := treeCtx.treeStorage
|
||||
changeCreator := treeCtx.changeCreator
|
||||
objTree := treeCtx.objTree
|
||||
|
||||
rawChangesFirst := []*treechangeproto.RawTreeChangeWithId{
|
||||
changeCreator.CreateRoot("0", aclList.Head().Id),
|
||||
changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"),
|
||||
}
|
||||
rawChangesSecond := []*treechangeproto.RawTreeChangeWithId{
|
||||
changeCreator.CreateRoot("0", aclList.Head().Id),
|
||||
changeCreator.CreateRaw("2", aclList.Head().Id, "0", true, "0"),
|
||||
}
|
||||
payloadFirst := RawChangesPayload{
|
||||
NewHeads: []string{"1"},
|
||||
RawChanges: rawChangesFirst,
|
||||
SnapshotPath: []string{"0", "1"},
|
||||
}
|
||||
payloadSecond := RawChangesPayload{
|
||||
NewHeads: []string{"2"},
|
||||
RawChanges: rawChangesSecond,
|
||||
SnapshotPath: []string{"0", "2"},
|
||||
}
|
||||
|
||||
res, err := objTree.AddRawChanges(context.Background(), payloadFirst)
|
||||
require.NoError(t, err, "adding changes should be without error")
|
||||
require.Equal(t, []string{"1"}, res.Heads)
|
||||
|
||||
res, err = objTree.AddRawChanges(context.Background(), payloadSecond)
|
||||
require.NoError(t, err, "adding changes should be without error")
|
||||
require.Equal(t, []string{"1", "2"}, res.Heads)
|
||||
|
||||
for _, ch := range append(rawChangesFirst, rawChangesSecond...) {
|
||||
raw, err := treeStorage.Get(context.Background(), ch.Id)
|
||||
assert.NoError(t, err, "storage should have all the changes")
|
||||
assert.Equal(t, ch.Id, raw.RawTreeChangeWithId().Id, "the changes in the storage should be the same")
|
||||
assert.Equal(t, ch.RawChange, raw.RawTreeChangeWithId().RawChange, "the changes in the storage should be the same")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("add with rollback", func(t *testing.T) {
|
||||
ctx := prepareTreeContext(t, aclList)
|
||||
changeCreator := ctx.changeCreator
|
||||
|
|
|
@ -250,12 +250,11 @@ func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) {
|
|||
keys: make(map[string]crypto.SymKey),
|
||||
newChangesBuf: make([]*Change, 0, 10),
|
||||
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
|
||||
notSeenIdxBuf: make([]int, 0, 10),
|
||||
newSnapshotsBuf: make([]*Change, 0, 10),
|
||||
flusher: deps.flusher,
|
||||
}
|
||||
|
||||
err := objTree.rebuildFromStorage(nil, nil, nil)
|
||||
_, err := objTree.rebuildFromStorage(nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to rebuild from storage: %w", err)
|
||||
}
|
||||
|
@ -288,7 +287,6 @@ func buildHistoryTree(deps objectTreeDeps, params HistoryTreeParams) (ht History
|
|||
keys: make(map[string]crypto.SymKey),
|
||||
newChangesBuf: make([]*Change, 0, 10),
|
||||
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
|
||||
notSeenIdxBuf: make([]int, 0, 10),
|
||||
newSnapshotsBuf: make([]*Change, 0, 10),
|
||||
flusher: deps.flusher,
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ type ObjectTreeValidator interface {
|
|||
ValidateFullTree(tree *Tree, aclList list.AclList) error
|
||||
// ValidateNewChanges should always be entered while holding a read lock on AclList
|
||||
ValidateNewChanges(tree *Tree, aclList list.AclList, newChanges []*Change) error
|
||||
FilterChanges(aclList list.AclList, changes []*Change, snapshots []*Change, indexes []int) (filteredHeads bool, filtered, filteredSnapshots []*Change, newIndexes []int)
|
||||
FilterChanges(aclList list.AclList, changes []*Change, snapshots []*Change) (filteredHeads bool, filtered, filteredSnapshots []*Change)
|
||||
}
|
||||
|
||||
type noOpTreeValidator struct {
|
||||
|
@ -57,14 +57,13 @@ func (n *noOpTreeValidator) ValidateNewChanges(tree *Tree, aclList list.AclList,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *noOpTreeValidator) FilterChanges(aclList list.AclList, changes []*Change, snapshots []*Change, indexes []int) (filteredHeads bool, filtered, filteredSnapshots []*Change, newIndexes []int) {
|
||||
func (n *noOpTreeValidator) FilterChanges(aclList list.AclList, changes []*Change, snapshots []*Change) (filteredHeads bool, filtered, filteredSnapshots []*Change) {
|
||||
if n.filterFunc == nil {
|
||||
return false, changes, snapshots, indexes
|
||||
return false, changes, snapshots
|
||||
}
|
||||
for idx, c := range changes {
|
||||
for _, 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)
|
||||
|
@ -106,24 +105,22 @@ func (v *objectTreeValidator) ValidateNewChanges(tree *Tree, aclList list.AclLis
|
|||
return
|
||||
}
|
||||
|
||||
func (v *objectTreeValidator) FilterChanges(aclList list.AclList, changes []*Change, snapshots []*Change, indexes []int) (filteredHeads bool, filtered, filteredSnapshots []*Change, newIndexes []int) {
|
||||
func (v *objectTreeValidator) FilterChanges(aclList list.AclList, changes []*Change, snapshots []*Change) (filteredHeads bool, filtered, filteredSnapshots []*Change) {
|
||||
if !v.shouldFilter {
|
||||
return false, changes, snapshots, indexes
|
||||
return false, changes, snapshots
|
||||
}
|
||||
aclList.RLock()
|
||||
defer aclList.RUnlock()
|
||||
state := aclList.AclState()
|
||||
for idx, c := range changes {
|
||||
for _, c := range changes {
|
||||
// this has to be a root
|
||||
if c.PreviousIds == nil {
|
||||
newIndexes = append(newIndexes, indexes[idx])
|
||||
filtered = append(filtered, c)
|
||||
filteredSnapshots = append(filteredSnapshots, c)
|
||||
continue
|
||||
}
|
||||
// only taking changes which we can read and for which we have acl heads
|
||||
if keys, exists := state.Keys()[c.ReadKeyId]; aclList.HasHead(c.AclHeadId) && exists && keys.ReadKey != nil {
|
||||
newIndexes = append(newIndexes, indexes[idx])
|
||||
filtered = append(filtered, c)
|
||||
if c.IsSnapshot {
|
||||
filteredSnapshots = append(filteredSnapshots, c)
|
||||
|
|
|
@ -53,6 +53,9 @@ func (t *Tree) Root() *Change {
|
|||
}
|
||||
|
||||
func (t *Tree) AddFast(changes ...*Change) []*Change {
|
||||
if len(changes) == 0 {
|
||||
return nil
|
||||
}
|
||||
defer t.clearUnattached()
|
||||
t.addedBuf = t.addedBuf[:0]
|
||||
for _, c := range changes {
|
||||
|
|
|
@ -49,6 +49,10 @@ func TestTree_Add(t *testing.T) {
|
|||
assert.Equal(t, tr.root.Id, "root")
|
||||
assert.Equal(t, []string{"root"}, tr.Heads())
|
||||
})
|
||||
t.Run("empty tree add should not panic", func(t *testing.T) {
|
||||
tr := &Tree{}
|
||||
tr.AddFast()
|
||||
})
|
||||
t.Run("linear add", func(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
res, _ := tr.Add(
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
|
||||
var (
|
||||
log = logger.NewNamedSugared("common.commonspace.objecttree")
|
||||
ErrEmpty = errors.New("logs empty")
|
||||
ErrEmpty = errors.New("database is empty")
|
||||
)
|
||||
|
||||
type treeBuilder struct {
|
||||
|
@ -47,10 +47,6 @@ func newTreeBuilder(storage Storage, builder ChangeBuilder) *treeBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
func (tb *treeBuilder) Build(opts treeBuilderOpts) (*Tree, error) {
|
||||
return tb.build(opts)
|
||||
}
|
||||
|
||||
func (tb *treeBuilder) BuildFull() (*Tree, error) {
|
||||
return tb.build(treeBuilderOpts{full: true})
|
||||
}
|
||||
|
@ -61,7 +57,7 @@ var (
|
|||
totalLowest atomic.Int32
|
||||
)
|
||||
|
||||
func (tb *treeBuilder) build(opts treeBuilderOpts) (tr *Tree, err error) {
|
||||
func (tb *treeBuilder) buildWithAdded(opts treeBuilderOpts) (*Tree, []*Change, error) {
|
||||
cache := make(map[string]*Change)
|
||||
tb.ctx = context.Background()
|
||||
for _, ch := range opts.newChanges {
|
||||
|
@ -74,12 +70,12 @@ func (tb *treeBuilder) build(opts treeBuilderOpts) (tr *Tree, err error) {
|
|||
if opts.useHeadsSnapshot {
|
||||
maxOrder, lowest, err := tb.lowestSnapshots(nil, opts.ourHeads, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(lowest) != 1 {
|
||||
snapshot, err = tb.commonSnapshot(lowest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
snapshot = lowest[0]
|
||||
|
@ -94,28 +90,29 @@ func (tb *treeBuilder) build(opts treeBuilderOpts) (tr *Tree, err error) {
|
|||
if len(opts.ourSnapshotPath) == 0 {
|
||||
common, err := tb.storage.CommonSnapshot(tb.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
snapshot = common
|
||||
} else {
|
||||
our := opts.ourSnapshotPath[0]
|
||||
_, lowest, err := tb.lowestSnapshots(cache, opts.theirHeads, our)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(lowest) != 1 {
|
||||
snapshot, err = tb.commonSnapshot(lowest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
snapshot = lowest[0]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
snapshot, err = commonSnapshotForTwoPaths(opts.ourSnapshotPath, opts.theirSnapshotPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -124,10 +121,13 @@ func (tb *treeBuilder) build(opts treeBuilderOpts) (tr *Tree, err error) {
|
|||
totalSnapshots.Store(totalSnapshots.Load() + 1)
|
||||
snapshotCh, err := tb.storage.Get(tb.ctx, snapshot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get common snapshot %s: %w", snapshot, err)
|
||||
return nil, nil, fmt.Errorf("failed to get common snapshot %s: %w", snapshot, err)
|
||||
}
|
||||
rawChange := &treechangeproto.RawTreeChangeWithId{}
|
||||
var changes []*Change
|
||||
var (
|
||||
changes = make([]*Change, 0, 10)
|
||||
newChanges = make([]*Change, 0, 10)
|
||||
)
|
||||
err = tb.storage.GetAfterOrder(tb.ctx, snapshotCh.OrderId, func(ctx context.Context, storageChange StorageChange) (shouldContinue bool, err error) {
|
||||
if order != "" && storageChange.OrderId > order {
|
||||
return false, nil
|
||||
|
@ -141,18 +141,44 @@ func (tb *treeBuilder) build(opts treeBuilderOpts) (tr *Tree, err error) {
|
|||
ch.OrderId = storageChange.OrderId
|
||||
ch.SnapshotCounter = storageChange.SnapshotCounter
|
||||
changes = append(changes, ch)
|
||||
if _, contains := cache[ch.Id]; contains {
|
||||
delete(cache, ch.Id)
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get changes after order: %w", err)
|
||||
return nil, nil, fmt.Errorf("failed to get changes after order: %w", err)
|
||||
}
|
||||
tr = &Tree{}
|
||||
changes = append(changes, opts.newChanges...)
|
||||
tr.AddFast(changes...)
|
||||
// getting the filtered new changes, we know that we don't have them in storage
|
||||
for _, change := range cache {
|
||||
newChanges = append(newChanges, change)
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil, nil, ErrEmpty
|
||||
}
|
||||
tr := &Tree{}
|
||||
changes = append(changes, newChanges...)
|
||||
added := tr.AddFast(changes...)
|
||||
if opts.useHeadsSnapshot {
|
||||
// this is important for history, because by default we get everything after the snapshot
|
||||
tr.LeaveOnlyBefore(opts.ourHeads)
|
||||
}
|
||||
return tr, nil
|
||||
if len(newChanges) > 0 {
|
||||
newChanges = newChanges[:0]
|
||||
for _, change := range added {
|
||||
// only those that are both added and are new we deem newChanges
|
||||
if _, contains := cache[change.Id]; contains {
|
||||
newChanges = append(newChanges, change)
|
||||
}
|
||||
}
|
||||
return tr, newChanges, nil
|
||||
}
|
||||
return tr, nil, nil
|
||||
}
|
||||
|
||||
func (tb *treeBuilder) build(opts treeBuilderOpts) (tr *Tree, err error) {
|
||||
tr, _, err = tb.buildWithAdded(opts)
|
||||
return
|
||||
}
|
||||
|
||||
func (tb *treeBuilder) lowestSnapshots(cache map[string]*Change, heads []string, ourSnapshot string) (maxOrder string, snapshots []string, err error) {
|
||||
|
@ -199,7 +225,7 @@ func (tb *treeBuilder) lowestSnapshots(cache map[string]*Change, heads []string,
|
|||
current = append(current, next...)
|
||||
next = next[:0]
|
||||
for _, id := range current {
|
||||
if ch, ok := cache[id]; ok {
|
||||
if ch, ok := cache[id]; ok && ch.SnapshotId != "" {
|
||||
if ch.visited {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -25,9 +25,13 @@ func (t *TreeStatsCollector) Register(tree *syncTree) {
|
|||
|
||||
func (t *TreeStatsCollector) Collect() []TreeStats {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
stats := make([]TreeStats, 0, len(t.trees))
|
||||
trees := make([]*syncTree, 0, len(t.trees))
|
||||
for _, tree := range t.trees {
|
||||
trees = append(trees, tree)
|
||||
}
|
||||
t.mutex.Unlock()
|
||||
stats := make([]TreeStats, 0, len(t.trees))
|
||||
for _, tree := range trees {
|
||||
tree.Lock()
|
||||
stats = append(stats, TreeStats{
|
||||
TreeLen: tree.Len(),
|
||||
|
|
|
@ -37,6 +37,7 @@ type SpaceDescription struct {
|
|||
AclPayload []byte
|
||||
SpaceSettingsId string
|
||||
SpaceSettingsPayload []byte
|
||||
AclRecords []*spacesyncproto.AclRecord
|
||||
}
|
||||
|
||||
func NewSpaceId(id string, repKey uint64) string {
|
||||
|
@ -95,6 +96,19 @@ func (s *space) Description(ctx context.Context) (desc SpaceDescription, err err
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.aclList.RLock()
|
||||
defer s.aclList.RUnlock()
|
||||
recs, err := s.aclList.RecordsAfter(ctx, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
aclRecs := make([]*spacesyncproto.AclRecord, 0, len(recs))
|
||||
for _, rec := range recs {
|
||||
aclRecs = append(aclRecs, &spacesyncproto.AclRecord{
|
||||
Id: rec.Id,
|
||||
AclPayload: rec.Payload,
|
||||
})
|
||||
}
|
||||
root := s.aclList.Root()
|
||||
settingsStorage, err := s.storage.TreeStorage(ctx, state.SettingsId)
|
||||
if err != nil {
|
||||
|
@ -114,6 +128,7 @@ func (s *space) Description(ctx context.Context) (desc SpaceDescription, err err
|
|||
AclPayload: root.Payload,
|
||||
SpaceSettingsId: settingsRoot.Id,
|
||||
SpaceSettingsPayload: settingsRoot.RawChange,
|
||||
AclRecords: aclRecs,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
|
||||
"github.com/anyproto/any-sync/commonspace/spacestorage"
|
||||
|
@ -96,7 +97,7 @@ func StoragePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload sp
|
|||
|
||||
// building acl root
|
||||
keyStorage := crypto.NewKeyStorage()
|
||||
aclBuilder := list.NewAclRecordBuilder("", keyStorage, nil, list.NoOpAcceptorVerifier{})
|
||||
aclBuilder := list.NewAclRecordBuilder("", keyStorage, nil, recordverifier.NewValidateFull())
|
||||
aclRoot, err := aclBuilder.BuildRoot(list.RootContent{
|
||||
PrivKey: payload.SigningKey,
|
||||
MasterKey: payload.MasterKey,
|
||||
|
@ -187,7 +188,7 @@ func StoragePayloadForSpaceDerive(payload SpaceDerivePayload) (storagePayload sp
|
|||
|
||||
// building acl root
|
||||
keyStorage := crypto.NewKeyStorage()
|
||||
aclBuilder := list.NewAclRecordBuilder("", keyStorage, nil, list.NoOpAcceptorVerifier{})
|
||||
aclBuilder := list.NewAclRecordBuilder("", keyStorage, nil, recordverifier.NewValidateFull())
|
||||
aclRoot, err := aclBuilder.BuildRoot(list.RootContent{
|
||||
PrivKey: payload.SigningKey,
|
||||
MasterKey: payload.MasterKey,
|
||||
|
|
296
commonspace/spacepull_test.go
Normal file
296
commonspace/spacepull_test.go
Normal file
|
@ -0,0 +1,296 @@
|
|||
package commonspace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/commonspace/config"
|
||||
"github.com/anyproto/any-sync/commonspace/credentialprovider"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
|
||||
"github.com/anyproto/any-sync/commonspace/object/treemanager"
|
||||
"github.com/anyproto/any-sync/commonspace/spacepayloads"
|
||||
"github.com/anyproto/any-sync/commonspace/spacestorage"
|
||||
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
|
||||
"github.com/anyproto/any-sync/commonspace/syncstatus"
|
||||
"github.com/anyproto/any-sync/net/peer"
|
||||
"github.com/anyproto/any-sync/net/rpc/rpctest"
|
||||
"github.com/anyproto/any-sync/net/streampool"
|
||||
"github.com/anyproto/any-sync/nodeconf/testconf"
|
||||
"github.com/anyproto/any-sync/testutil/accounttest"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
"github.com/anyproto/any-sync/util/syncqueues"
|
||||
)
|
||||
|
||||
func TestSpaceService_SpacePull(t *testing.T) {
|
||||
var makeClientServer = func(t *testing.T) (fxC, fxS *spacePullFixture, peerId string) {
|
||||
fxC = newSpacePullFixture(t)
|
||||
fxS = newSpacePullFixture(t)
|
||||
peerId = "peer"
|
||||
mcS, mcC := rpctest.MultiConnPair(peerId, peerId+"client")
|
||||
pS, err := peer.NewPeer(mcS, fxC.ts)
|
||||
require.NoError(t, err)
|
||||
fxC.tp.AddPeer(ctx, pS)
|
||||
_, err = peer.NewPeer(mcC, fxS.ts)
|
||||
fxC.managerProvider.peer = pS
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Run("successful space pull", func(t *testing.T) {
|
||||
fxC, fxS, _ := makeClientServer(t)
|
||||
defer fxC.Finish(t)
|
||||
defer fxS.Finish(t)
|
||||
|
||||
spaceId, payload := fxS.createTestSpace(t)
|
||||
|
||||
space, err := fxC.spaceService.NewSpace(ctx, spaceId, Deps{
|
||||
SyncStatus: syncstatus.NewNoOpSyncStatus(),
|
||||
TreeSyncer: &mockTreeSyncer{},
|
||||
AccountService: fxC.account,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, space.Init(ctx))
|
||||
require.NotNil(t, space)
|
||||
require.Equal(t, spaceId, space.Id())
|
||||
|
||||
storage := space.Storage()
|
||||
state, err := storage.StateStorage().GetState(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spaceId, state.SpaceId)
|
||||
require.Equal(t, payload.SpaceHeaderWithId.Id, state.SpaceId)
|
||||
})
|
||||
|
||||
t.Run("space pull with acl records", func(t *testing.T) {
|
||||
fxC, fxS, _ := makeClientServer(t)
|
||||
defer fxC.Finish(t)
|
||||
defer fxS.Finish(t)
|
||||
|
||||
spaceId, recLen, payload := fxS.createTestSpaceWithAclRecords(t)
|
||||
|
||||
space, err := fxC.spaceService.NewSpace(ctx, spaceId, Deps{
|
||||
SyncStatus: syncstatus.NewNoOpSyncStatus(),
|
||||
TreeSyncer: &mockTreeSyncer{},
|
||||
AccountService: fxC.account,
|
||||
recordVerifier: recordverifier.NewValidateFull(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, space)
|
||||
require.NoError(t, space.Init(ctx))
|
||||
records, err := space.Acl().RecordsAfter(ctx, "")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, recLen, len(records))
|
||||
storage := space.Storage()
|
||||
state, err := storage.StateStorage().GetState(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spaceId, state.SpaceId)
|
||||
require.Equal(t, payload.SpaceHeaderWithId.Id, state.SpaceId)
|
||||
})
|
||||
}
|
||||
|
||||
type spacePullFixture struct {
|
||||
*spaceService
|
||||
app *app.App
|
||||
ctrl *gomock.Controller
|
||||
ts *rpctest.TestServer
|
||||
tp *rpctest.TestPool
|
||||
tmpDir string
|
||||
account *accounttest.AccountTestService
|
||||
configService *testconf.StubConf
|
||||
storage spacestorage.SpaceStorageProvider
|
||||
managerProvider *mockPeerManagerProvider
|
||||
}
|
||||
|
||||
func newSpacePullFixture(t *testing.T) (fx *spacePullFixture) {
|
||||
ts := rpctest.NewTestServer()
|
||||
fx = &spacePullFixture{
|
||||
spaceService: New().(*spaceService),
|
||||
ctrl: gomock.NewController(t),
|
||||
app: new(app.App),
|
||||
ts: ts,
|
||||
tp: rpctest.NewTestPool(),
|
||||
tmpDir: t.TempDir(),
|
||||
account: &accounttest.AccountTestService{},
|
||||
configService: &testconf.StubConf{},
|
||||
storage: &spaceStorageProvider{rootPath: t.TempDir()},
|
||||
managerProvider: &mockPeerManagerProvider{},
|
||||
}
|
||||
|
||||
configGetter := &mockSpaceConfigGetter{}
|
||||
|
||||
fx.app.Register(configGetter).
|
||||
Register(mockCoordinatorClient{}).
|
||||
Register(mockNodeClient{}).
|
||||
Register(credentialprovider.NewNoOp()).
|
||||
Register(streampool.New()).
|
||||
Register(newStreamOpener("spaceId")).
|
||||
Register(fx.account).
|
||||
Register(fx.storage).
|
||||
Register(fx.managerProvider).
|
||||
Register(syncqueues.New()).
|
||||
Register(&mockTreeManager{}).
|
||||
Register(fx.spaceService).
|
||||
Register(fx.tp).
|
||||
Register(fx.ts).
|
||||
Register(fx.configService)
|
||||
|
||||
require.NoError(t, spacesyncproto.DRPCRegisterSpaceSync(ts, &testSpaceSyncServer{spaceService: fx.spaceService}))
|
||||
require.NoError(t, fx.app.Start(ctx))
|
||||
|
||||
return fx
|
||||
}
|
||||
|
||||
func (fx *spacePullFixture) Finish(t *testing.T) {
|
||||
fx.ctrl.Finish()
|
||||
}
|
||||
|
||||
func (fx *spacePullFixture) createTestSpace(t *testing.T) (string, spacestorage.SpaceStorageCreatePayload) {
|
||||
keys := fx.account.Account()
|
||||
masterKey, _, err := crypto.GenerateRandomEd25519KeyPair()
|
||||
require.NoError(t, err)
|
||||
metaKey, _, err := crypto.GenerateRandomEd25519KeyPair()
|
||||
require.NoError(t, err)
|
||||
readKey := crypto.NewAES()
|
||||
metadata := []byte("metadata")
|
||||
|
||||
payload := spacepayloads.SpaceCreatePayload{
|
||||
SigningKey: keys.SignKey,
|
||||
SpaceType: "test",
|
||||
ReplicationKey: 1,
|
||||
SpacePayload: nil,
|
||||
MasterKey: masterKey,
|
||||
ReadKey: readKey,
|
||||
MetadataKey: metaKey,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
createPayload, err := spacepayloads.StoragePayloadForSpaceCreate(payload)
|
||||
require.NoError(t, err)
|
||||
storage, err := fx.storage.CreateSpaceStorage(ctx, createPayload)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, storage.Close(ctx))
|
||||
|
||||
return createPayload.SpaceHeaderWithId.Id, createPayload
|
||||
}
|
||||
|
||||
func (fx *spacePullFixture) createTestSpaceWithAclRecords(t *testing.T) (string, int, spacestorage.SpaceStorageCreatePayload) {
|
||||
spaceId, payload := fx.createTestSpace(t)
|
||||
keys := fx.account.Account()
|
||||
|
||||
executor := list.NewExternalKeysAclExecutor(spaceId, keys, []byte("metadata"), payload.AclWithId)
|
||||
cmds := []string{
|
||||
"0.init::0",
|
||||
"0.invite::invId1",
|
||||
"0.invite::invId2",
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
err := executor.Execute(cmd)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
allRecords, err := executor.ActualAccounts()["0"].Acl.RecordsAfter(ctx, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
storage, err := fx.storage.WaitSpaceStorage(ctx, spaceId)
|
||||
require.NoError(t, err)
|
||||
defer storage.Close(ctx)
|
||||
|
||||
aclStorage, err := storage.AclStorage()
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, rec := range allRecords[1:] { // Skip first record as it's already there
|
||||
err := aclStorage.AddAll(ctx, []list.StorageRecord{
|
||||
{
|
||||
RawRecord: rec.Payload,
|
||||
Id: rec.Id,
|
||||
PrevId: allRecords[i].Id,
|
||||
Order: i + 2,
|
||||
ChangeSize: len(rec.Payload),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
return spaceId, len(allRecords), payload
|
||||
}
|
||||
|
||||
type testSpaceSyncServer struct {
|
||||
spacesyncproto.DRPCSpaceSyncUnimplementedServer
|
||||
spaceService SpaceService
|
||||
}
|
||||
|
||||
func (t *testSpaceSyncServer) SpacePull(ctx context.Context, req *spacesyncproto.SpacePullRequest) (*spacesyncproto.SpacePullResponse, error) {
|
||||
sp, err := t.spaceService.NewSpace(ctx, req.Id, Deps{
|
||||
SyncStatus: syncstatus.NewNoOpSyncStatus(),
|
||||
TreeSyncer: mockTreeSyncer{},
|
||||
recordVerifier: recordverifier.NewValidateFull(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = sp.Init(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spaceDesc, err := sp.Description(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &spacesyncproto.SpacePullResponse{
|
||||
Payload: &spacesyncproto.SpacePayload{
|
||||
SpaceHeader: spaceDesc.SpaceHeader,
|
||||
AclPayloadId: spaceDesc.AclId,
|
||||
AclPayload: spaceDesc.AclPayload,
|
||||
SpaceSettingsPayload: spaceDesc.SpaceSettingsPayload,
|
||||
SpaceSettingsPayloadId: spaceDesc.SpaceSettingsId,
|
||||
},
|
||||
AclRecords: spaceDesc.AclRecords,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type mockSpaceConfigGetter struct{}
|
||||
|
||||
func (m *mockSpaceConfigGetter) GetStreamConfig() streampool.StreamConfig {
|
||||
return streampool.StreamConfig{}
|
||||
}
|
||||
|
||||
func (m *mockSpaceConfigGetter) Init(a *app.App) error { return nil }
|
||||
func (m *mockSpaceConfigGetter) Name() string { return "config" }
|
||||
func (m *mockSpaceConfigGetter) GetSpace() config.Config {
|
||||
return config.Config{
|
||||
GCTTL: 60,
|
||||
SyncPeriod: 5,
|
||||
KeepTreeDataInMemory: true,
|
||||
}
|
||||
}
|
||||
|
||||
type mockTreeManager struct{}
|
||||
|
||||
func (m *mockTreeManager) Init(a *app.App) error { return nil }
|
||||
func (m *mockTreeManager) Name() string { return treemanager.CName }
|
||||
func (m *mockTreeManager) Run(ctx context.Context) error { return nil }
|
||||
func (m *mockTreeManager) Close(ctx context.Context) error { return nil }
|
||||
func (m *mockTreeManager) GetTree(ctx context.Context, spaceId, treeId string) (objecttree.ObjectTree, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockTreeManager) CreateTree(ctx context.Context, spaceId string) (objecttree.ObjectTree, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockTreeManager) PutTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload) (objecttree.ObjectTree, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockTreeManager) DeleteTree(ctx context.Context, spaceId, treeId string) error { return nil }
|
||||
func (m *mockTreeManager) MarkTreeDeleted(ctx context.Context, spaceId, treeId string) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockTreeManager) ValidateAndPutTree(ctx context.Context, spaceId string, payload treestorage.TreeStorageCreatePayload) error {
|
||||
return nil
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/anyproto/any-sync/accountservice"
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
|
||||
"github.com/anyproto/any-sync/commonspace/object/tree/synctree"
|
||||
"github.com/anyproto/any-sync/commonspace/object/treemanager"
|
||||
|
@ -127,8 +128,9 @@ func (r *RpcServer) getSpace(ctx context.Context, spaceId string) (sp Space, err
|
|||
sp, ok := r.spaces[spaceId]
|
||||
if !ok {
|
||||
sp, err = r.spaceService.NewSpace(ctx, spaceId, Deps{
|
||||
TreeSyncer: NewTreeSyncer(spaceId),
|
||||
SyncStatus: syncstatus.NewNoOpSyncStatus(),
|
||||
TreeSyncer: NewTreeSyncer(spaceId),
|
||||
SyncStatus: syncstatus.NewNoOpSyncStatus(),
|
||||
recordVerifier: recordverifier.NewValidateFull(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -13,6 +13,8 @@ import (
|
|||
|
||||
"github.com/anyproto/any-sync/commonspace/acl/aclclient"
|
||||
"github.com/anyproto/any-sync/commonspace/deletionmanager"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/object/keyvalue"
|
||||
"github.com/anyproto/any-sync/commonspace/object/keyvalue/keyvaluestorage"
|
||||
"github.com/anyproto/any-sync/commonspace/object/treesyncer"
|
||||
|
@ -72,6 +74,7 @@ type Deps struct {
|
|||
SyncStatus syncstatus.StatusUpdater
|
||||
TreeSyncer treesyncer.TreeSyncer
|
||||
AccountService accountservice.Service
|
||||
recordVerifier recordverifier.RecordVerifier
|
||||
Indexer keyvaluestorage.Indexer
|
||||
}
|
||||
|
||||
|
@ -159,7 +162,7 @@ func (s *spaceService) NewSpace(ctx context.Context, id string, deps Deps) (Spac
|
|||
return nil, err
|
||||
}
|
||||
} else {
|
||||
st, err = s.getSpaceStorageFromRemote(ctx, id)
|
||||
st, err = s.getSpaceStorageFromRemote(ctx, id, deps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -188,8 +191,13 @@ func (s *spaceService) NewSpace(ctx context.Context, id string, deps Deps) (Spac
|
|||
if deps.Indexer != nil {
|
||||
keyValueIndexer = deps.Indexer
|
||||
}
|
||||
recordVerifier := recordverifier.New()
|
||||
if deps.recordVerifier != nil {
|
||||
recordVerifier = deps.recordVerifier
|
||||
}
|
||||
spaceApp.Register(state).
|
||||
Register(deps.SyncStatus).
|
||||
Register(recordVerifier).
|
||||
Register(peerManager).
|
||||
Register(st).
|
||||
Register(keyValueIndexer).
|
||||
|
@ -236,7 +244,7 @@ func (s *spaceService) addSpaceStorage(ctx context.Context, spaceDescription Spa
|
|||
return
|
||||
}
|
||||
|
||||
func (s *spaceService) getSpaceStorageFromRemote(ctx context.Context, id string) (st spacestorage.SpaceStorage, err error) {
|
||||
func (s *spaceService) getSpaceStorageFromRemote(ctx context.Context, id string, deps Deps) (st spacestorage.SpaceStorage, err error) {
|
||||
// we can't connect to client if it is a node
|
||||
if s.configurationService.IsResponsible(id) {
|
||||
err = spacesyncproto.ErrSpaceMissing
|
||||
|
@ -275,7 +283,7 @@ func (s *spaceService) getSpaceStorageFromRemote(ctx context.Context, id string)
|
|||
}
|
||||
}
|
||||
for i, p := range peers {
|
||||
if st, err = s.spacePullWithPeer(ctx, p, id); err != nil {
|
||||
if st, err = s.spacePullWithPeer(ctx, p, id, deps); err != nil {
|
||||
if i+1 == len(peers) {
|
||||
return
|
||||
} else {
|
||||
|
@ -288,7 +296,7 @@ func (s *spaceService) getSpaceStorageFromRemote(ctx context.Context, id string)
|
|||
return nil, net.ErrUnableToConnect
|
||||
}
|
||||
|
||||
func (s *spaceService) spacePullWithPeer(ctx context.Context, p peer.Peer, id string) (st spacestorage.SpaceStorage, err error) {
|
||||
func (s *spaceService) spacePullWithPeer(ctx context.Context, p peer.Peer, id string, deps Deps) (st spacestorage.SpaceStorage, err error) {
|
||||
var res *spacesyncproto.SpacePullResponse
|
||||
err = p.DoDrpc(ctx, func(conn drpc.Conn) error {
|
||||
cl := spacesyncproto.NewDRPCSpaceSyncClient(conn)
|
||||
|
@ -300,7 +308,7 @@ func (s *spaceService) spacePullWithPeer(ctx context.Context, p peer.Peer, id st
|
|||
return
|
||||
}
|
||||
|
||||
return s.createSpaceStorage(ctx, spacestorage.SpaceStorageCreatePayload{
|
||||
st, err = s.createSpaceStorage(ctx, spacestorage.SpaceStorageCreatePayload{
|
||||
AclWithId: &consensusproto.RawRecordWithId{
|
||||
Payload: res.Payload.AclPayload,
|
||||
Id: res.Payload.AclPayloadId,
|
||||
|
@ -311,6 +319,35 @@ func (s *spaceService) spacePullWithPeer(ctx context.Context, p peer.Peer, id st
|
|||
},
|
||||
SpaceHeaderWithId: res.Payload.SpaceHeader,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if res.AclRecords != nil {
|
||||
aclSt, err := st.AclStorage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
recordVerifier := recordverifier.New()
|
||||
if deps.recordVerifier != nil {
|
||||
recordVerifier = deps.recordVerifier
|
||||
}
|
||||
acl, err := list.BuildAclListWithIdentity(s.account.Account(), aclSt, recordVerifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
consRecs := make([]*consensusproto.RawRecordWithId, 0, len(res.AclRecords))
|
||||
for _, rec := range res.AclRecords {
|
||||
consRecs = append(consRecs, &consensusproto.RawRecordWithId{
|
||||
Id: rec.Id,
|
||||
Payload: rec.AclPayload,
|
||||
})
|
||||
}
|
||||
err = acl.AddRawRecords(consRecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *spaceService) createSpaceStorage(ctx context.Context, payload spacestorage.SpaceStorageCreatePayload) (spacestorage.SpaceStorage, error) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/anyproto/any-sync/commonspace/headsync/headstorage"
|
||||
"github.com/anyproto/any-sync/commonspace/object/accountdata"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/list"
|
||||
"github.com/anyproto/any-sync/commonspace/object/acl/recordverifier"
|
||||
"github.com/anyproto/any-sync/commonspace/spacestorage/oldstorage"
|
||||
"github.com/anyproto/any-sync/consensus/consensusproto"
|
||||
)
|
||||
|
@ -31,7 +32,7 @@ func migrateAclList(ctx context.Context, oldStorage oldstorage.ListStorage, head
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("migration: failed to generate keys: %w", err)
|
||||
}
|
||||
aclList, err := list.BuildAclListWithIdentity(keys, aclStorage, &list.NoOpAcceptorVerifier{})
|
||||
aclList, err := list.BuildAclListWithIdentity(keys, aclStorage, recordverifier.New())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("migration: failed to build acl list: %w", err)
|
||||
}
|
||||
|
|
|
@ -103,6 +103,12 @@ message SpacePullRequest {
|
|||
// SpacePullResponse is a response with header and acl root
|
||||
message SpacePullResponse {
|
||||
SpacePayload payload = 1;
|
||||
repeated AclRecord aclRecords = 2;
|
||||
}
|
||||
|
||||
message AclRecord {
|
||||
bytes aclPayload = 1;
|
||||
string id = 2;
|
||||
}
|
||||
|
||||
// SpacePayload is a payload for pushing a space
|
||||
|
|
|
@ -749,7 +749,8 @@ func (m *SpacePullRequest) GetId() string {
|
|||
|
||||
// SpacePullResponse is a response with header and acl root
|
||||
type SpacePullResponse struct {
|
||||
Payload *SpacePayload `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
Payload *SpacePayload `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
AclRecords []*AclRecord `protobuf:"bytes,2,rep,name=aclRecords,proto3" json:"aclRecords,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SpacePullResponse) Reset() { *m = SpacePullResponse{} }
|
||||
|
@ -800,6 +801,73 @@ func (m *SpacePullResponse) GetPayload() *SpacePayload {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *SpacePullResponse) GetAclRecords() []*AclRecord {
|
||||
if m != nil {
|
||||
return m.AclRecords
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AclRecord struct {
|
||||
AclPayload []byte `protobuf:"bytes,1,opt,name=aclPayload,proto3" json:"aclPayload,omitempty"`
|
||||
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (m *AclRecord) Reset() { *m = AclRecord{} }
|
||||
func (m *AclRecord) String() string { return proto.CompactTextString(m) }
|
||||
func (*AclRecord) ProtoMessage() {}
|
||||
func (*AclRecord) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{10}
|
||||
}
|
||||
func (m *AclRecord) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *AclRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_AclRecord.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *AclRecord) XXX_MarshalAppend(b []byte, newLen int) ([]byte, error) {
|
||||
b = b[:newLen]
|
||||
_, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
func (m *AclRecord) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_AclRecord.Merge(m, src)
|
||||
}
|
||||
func (m *AclRecord) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *AclRecord) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_AclRecord.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_AclRecord proto.InternalMessageInfo
|
||||
|
||||
func (m *AclRecord) GetAclPayload() []byte {
|
||||
if m != nil {
|
||||
return m.AclPayload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AclRecord) GetId() string {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SpacePayload is a payload for pushing a space
|
||||
type SpacePayload struct {
|
||||
SpaceHeader *RawSpaceHeaderWithId `protobuf:"bytes,1,opt,name=spaceHeader,proto3" json:"spaceHeader,omitempty"`
|
||||
|
@ -813,7 +881,7 @@ func (m *SpacePayload) Reset() { *m = SpacePayload{} }
|
|||
func (m *SpacePayload) String() string { return proto.CompactTextString(m) }
|
||||
func (*SpacePayload) ProtoMessage() {}
|
||||
func (*SpacePayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{10}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{11}
|
||||
}
|
||||
func (m *SpacePayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -899,7 +967,7 @@ func (m *SpaceHeader) Reset() { *m = SpaceHeader{} }
|
|||
func (m *SpaceHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*SpaceHeader) ProtoMessage() {}
|
||||
func (*SpaceHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{11}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{12}
|
||||
}
|
||||
func (m *SpaceHeader) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -988,7 +1056,7 @@ func (m *RawSpaceHeader) Reset() { *m = RawSpaceHeader{} }
|
|||
func (m *RawSpaceHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*RawSpaceHeader) ProtoMessage() {}
|
||||
func (*RawSpaceHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{12}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{13}
|
||||
}
|
||||
func (m *RawSpaceHeader) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1049,7 +1117,7 @@ func (m *RawSpaceHeaderWithId) Reset() { *m = RawSpaceHeaderWithId{} }
|
|||
func (m *RawSpaceHeaderWithId) String() string { return proto.CompactTextString(m) }
|
||||
func (*RawSpaceHeaderWithId) ProtoMessage() {}
|
||||
func (*RawSpaceHeaderWithId) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{13}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{14}
|
||||
}
|
||||
func (m *RawSpaceHeaderWithId) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1113,7 +1181,7 @@ func (m *SpaceSettingsContent) Reset() { *m = SpaceSettingsContent{} }
|
|||
func (m *SpaceSettingsContent) String() string { return proto.CompactTextString(m) }
|
||||
func (*SpaceSettingsContent) ProtoMessage() {}
|
||||
func (*SpaceSettingsContent) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{14}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{15}
|
||||
}
|
||||
func (m *SpaceSettingsContent) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1204,7 +1272,7 @@ func (m *ObjectDelete) Reset() { *m = ObjectDelete{} }
|
|||
func (m *ObjectDelete) String() string { return proto.CompactTextString(m) }
|
||||
func (*ObjectDelete) ProtoMessage() {}
|
||||
func (*ObjectDelete) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{15}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{16}
|
||||
}
|
||||
func (m *ObjectDelete) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1258,7 +1326,7 @@ func (m *StoreHeader) Reset() { *m = StoreHeader{} }
|
|||
func (m *StoreHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*StoreHeader) ProtoMessage() {}
|
||||
func (*StoreHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{16}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{17}
|
||||
}
|
||||
func (m *StoreHeader) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1318,7 +1386,7 @@ func (m *SpaceDelete) Reset() { *m = SpaceDelete{} }
|
|||
func (m *SpaceDelete) String() string { return proto.CompactTextString(m) }
|
||||
func (*SpaceDelete) ProtoMessage() {}
|
||||
func (*SpaceDelete) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{17}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{18}
|
||||
}
|
||||
func (m *SpaceDelete) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1372,7 +1440,7 @@ func (m *SpaceSettingsSnapshot) Reset() { *m = SpaceSettingsSnapshot{} }
|
|||
func (m *SpaceSettingsSnapshot) String() string { return proto.CompactTextString(m) }
|
||||
func (*SpaceSettingsSnapshot) ProtoMessage() {}
|
||||
func (*SpaceSettingsSnapshot) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{18}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{19}
|
||||
}
|
||||
func (m *SpaceSettingsSnapshot) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1433,7 +1501,7 @@ func (m *SettingsData) Reset() { *m = SettingsData{} }
|
|||
func (m *SettingsData) String() string { return proto.CompactTextString(m) }
|
||||
func (*SettingsData) ProtoMessage() {}
|
||||
func (*SettingsData) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{19}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{20}
|
||||
}
|
||||
func (m *SettingsData) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1493,7 +1561,7 @@ func (m *SpaceSubscription) Reset() { *m = SpaceSubscription{} }
|
|||
func (m *SpaceSubscription) String() string { return proto.CompactTextString(m) }
|
||||
func (*SpaceSubscription) ProtoMessage() {}
|
||||
func (*SpaceSubscription) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{20}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{21}
|
||||
}
|
||||
func (m *SpaceSubscription) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1554,7 +1622,7 @@ func (m *AclAddRecordRequest) Reset() { *m = AclAddRecordRequest{} }
|
|||
func (m *AclAddRecordRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*AclAddRecordRequest) ProtoMessage() {}
|
||||
func (*AclAddRecordRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{21}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{22}
|
||||
}
|
||||
func (m *AclAddRecordRequest) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1615,7 +1683,7 @@ func (m *AclAddRecordResponse) Reset() { *m = AclAddRecordResponse{} }
|
|||
func (m *AclAddRecordResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*AclAddRecordResponse) ProtoMessage() {}
|
||||
func (*AclAddRecordResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{22}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{23}
|
||||
}
|
||||
func (m *AclAddRecordResponse) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1677,7 +1745,7 @@ func (m *AclGetRecordsRequest) Reset() { *m = AclGetRecordsRequest{} }
|
|||
func (m *AclGetRecordsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*AclGetRecordsRequest) ProtoMessage() {}
|
||||
func (*AclGetRecordsRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{23}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{24}
|
||||
}
|
||||
func (m *AclGetRecordsRequest) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1737,7 +1805,7 @@ func (m *AclGetRecordsResponse) Reset() { *m = AclGetRecordsResponse{} }
|
|||
func (m *AclGetRecordsResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*AclGetRecordsResponse) ProtoMessage() {}
|
||||
func (*AclGetRecordsResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{24}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{25}
|
||||
}
|
||||
func (m *AclGetRecordsResponse) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1790,7 +1858,7 @@ func (m *StoreDiffRequest) Reset() { *m = StoreDiffRequest{} }
|
|||
func (m *StoreDiffRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*StoreDiffRequest) ProtoMessage() {}
|
||||
func (*StoreDiffRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{25}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{26}
|
||||
}
|
||||
func (m *StoreDiffRequest) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1849,7 +1917,7 @@ func (m *StoreDiffResponse) Reset() { *m = StoreDiffResponse{} }
|
|||
func (m *StoreDiffResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*StoreDiffResponse) ProtoMessage() {}
|
||||
func (*StoreDiffResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{26}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{27}
|
||||
}
|
||||
func (m *StoreDiffResponse) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1905,7 +1973,7 @@ func (m *StoreKeyValue) Reset() { *m = StoreKeyValue{} }
|
|||
func (m *StoreKeyValue) String() string { return proto.CompactTextString(m) }
|
||||
func (*StoreKeyValue) ProtoMessage() {}
|
||||
func (*StoreKeyValue) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{27}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{28}
|
||||
}
|
||||
func (m *StoreKeyValue) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -1985,7 +2053,7 @@ func (m *StoreKeyValues) Reset() { *m = StoreKeyValues{} }
|
|||
func (m *StoreKeyValues) String() string { return proto.CompactTextString(m) }
|
||||
func (*StoreKeyValues) ProtoMessage() {}
|
||||
func (*StoreKeyValues) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{28}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{29}
|
||||
}
|
||||
func (m *StoreKeyValues) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -2042,7 +2110,7 @@ func (m *StoreKeyInner) Reset() { *m = StoreKeyInner{} }
|
|||
func (m *StoreKeyInner) String() string { return proto.CompactTextString(m) }
|
||||
func (*StoreKeyInner) ProtoMessage() {}
|
||||
func (*StoreKeyInner) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{29}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{30}
|
||||
}
|
||||
func (m *StoreKeyInner) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -2130,7 +2198,7 @@ func (m *StorageHeader) Reset() { *m = StorageHeader{} }
|
|||
func (m *StorageHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*StorageHeader) ProtoMessage() {}
|
||||
func (*StorageHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{30}
|
||||
return fileDescriptor_80e49f1f4ac27799, []int{31}
|
||||
}
|
||||
func (m *StorageHeader) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -2196,6 +2264,7 @@ func init() {
|
|||
proto.RegisterType((*SpacePushResponse)(nil), "spacesync.SpacePushResponse")
|
||||
proto.RegisterType((*SpacePullRequest)(nil), "spacesync.SpacePullRequest")
|
||||
proto.RegisterType((*SpacePullResponse)(nil), "spacesync.SpacePullResponse")
|
||||
proto.RegisterType((*AclRecord)(nil), "spacesync.AclRecord")
|
||||
proto.RegisterType((*SpacePayload)(nil), "spacesync.SpacePayload")
|
||||
proto.RegisterType((*SpaceHeader)(nil), "spacesync.SpaceHeader")
|
||||
proto.RegisterType((*RawSpaceHeader)(nil), "spacesync.RawSpaceHeader")
|
||||
|
@ -2224,106 +2293,108 @@ func init() {
|
|||
}
|
||||
|
||||
var fileDescriptor_80e49f1f4ac27799 = []byte{
|
||||
// 1572 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x18, 0x4d, 0x6f, 0x1b, 0x45,
|
||||
0x3b, 0xbb, 0x4e, 0x1c, 0xfb, 0x89, 0xe3, 0x6e, 0x26, 0x49, 0xe3, 0xd7, 0xad, 0x5c, 0x6b, 0xf4,
|
||||
0xaa, 0x6f, 0x14, 0xbd, 0xb4, 0x4d, 0x0a, 0x95, 0x5a, 0xe0, 0x90, 0x26, 0x69, 0x63, 0x4a, 0x9a,
|
||||
0x68, 0xdc, 0x0f, 0x09, 0x09, 0xa4, 0xcd, 0xee, 0x24, 0x59, 0xba, 0xde, 0x35, 0x3b, 0xe3, 0x36,
|
||||
0x3e, 0x72, 0xe2, 0x04, 0xe2, 0xcc, 0x7f, 0xe0, 0xc0, 0xbf, 0xe0, 0x58, 0x38, 0x71, 0x44, 0xed,
|
||||
0x9d, 0xdf, 0x80, 0x66, 0x76, 0x76, 0x76, 0xd6, 0x1f, 0xa5, 0xa8, 0x70, 0x89, 0xe7, 0x79, 0xe6,
|
||||
0xf9, 0xfe, 0x9c, 0x0d, 0x6c, 0x7a, 0x71, 0xaf, 0x17, 0x47, 0xac, 0xef, 0x7a, 0xf4, 0xba, 0xfc,
|
||||
0xcb, 0x86, 0x91, 0xd7, 0x4f, 0x62, 0x1e, 0x5f, 0x97, 0x7f, 0x59, 0x8e, 0xbd, 0x26, 0x11, 0xa8,
|
||||
0xaa, 0x11, 0x98, 0xc2, 0xe2, 0x3e, 0x75, 0xfd, 0xee, 0x30, 0xf2, 0x88, 0x1b, 0x9d, 0x52, 0x84,
|
||||
0x60, 0xf6, 0x24, 0x89, 0x7b, 0x0d, 0xab, 0x6d, 0xad, 0xcf, 0x12, 0x79, 0x46, 0x75, 0xb0, 0x79,
|
||||
0xdc, 0xb0, 0x25, 0xc6, 0xe6, 0x31, 0x5a, 0x81, 0xb9, 0x30, 0xe8, 0x05, 0xbc, 0x51, 0x6a, 0x5b,
|
||||
0xeb, 0x8b, 0x24, 0x05, 0x50, 0x13, 0x2a, 0x34, 0xa4, 0x3d, 0x1a, 0x71, 0xd6, 0x98, 0x6d, 0x5b,
|
||||
0xeb, 0x15, 0xa2, 0x61, 0x7c, 0x0e, 0x75, 0xad, 0x86, 0xb2, 0x41, 0xc8, 0x85, 0x9e, 0x33, 0x97,
|
||||
0x9d, 0x49, 0x3d, 0x35, 0x22, 0xcf, 0xe8, 0x23, 0x43, 0x82, 0xdd, 0x2e, 0xad, 0x2f, 0x6c, 0xb5,
|
||||
0xaf, 0xe5, 0xb6, 0x17, 0x05, 0xec, 0xa5, 0x84, 0xb9, 0x0e, 0x61, 0x95, 0x17, 0x0f, 0x22, 0x6d,
|
||||
0x95, 0x04, 0xf0, 0x87, 0xb0, 0x3a, 0x91, 0x51, 0x38, 0x15, 0xf8, 0x52, 0x7d, 0x95, 0xd8, 0x81,
|
||||
0x2f, 0x0d, 0xa2, 0xae, 0x2f, 0xdd, 0xac, 0x12, 0x79, 0xc6, 0xdf, 0x59, 0x70, 0x21, 0xe7, 0xfe,
|
||||
0x6a, 0x40, 0x19, 0x47, 0x0d, 0x98, 0x97, 0x36, 0x75, 0x32, 0xe6, 0x0c, 0x44, 0x37, 0xa0, 0x9c,
|
||||
0x88, 0x18, 0x66, 0xc6, 0x37, 0x26, 0x19, 0x2f, 0x08, 0x88, 0xa2, 0x43, 0xd7, 0xa1, 0xe2, 0x07,
|
||||
0x27, 0x27, 0x8f, 0x86, 0x7d, 0x2a, 0xad, 0xae, 0x6f, 0x2d, 0x1b, 0x3c, 0xbb, 0xea, 0x8a, 0x68,
|
||||
0x22, 0x7c, 0x0e, 0x8e, 0xe1, 0x4d, 0x3f, 0x8e, 0x18, 0x45, 0x37, 0x61, 0x3e, 0x91, 0x9e, 0xb1,
|
||||
0x86, 0x25, 0xf5, 0xfe, 0x67, 0x6a, 0xd0, 0x48, 0x46, 0x59, 0xd0, 0x6c, 0xbf, 0x8d, 0xe6, 0x5f,
|
||||
0x2d, 0x58, 0x3a, 0x3c, 0xfe, 0x92, 0x7a, 0x5c, 0x88, 0x3b, 0xa0, 0x8c, 0xb9, 0xa7, 0xf4, 0x0d,
|
||||
0xc1, 0xb8, 0x0c, 0xd5, 0x24, 0x8d, 0x58, 0x27, 0x8b, 0x69, 0x8e, 0x10, 0x7c, 0x09, 0xed, 0x87,
|
||||
0xc3, 0x8e, 0x2f, 0xfd, 0xae, 0x92, 0x0c, 0x14, 0x37, 0x7d, 0x77, 0x18, 0xc6, 0xae, 0x2f, 0x8b,
|
||||
0xa8, 0x46, 0x32, 0x50, 0xd4, 0x57, 0x2c, 0x0d, 0xe8, 0xf8, 0x8d, 0x39, 0xc9, 0xa4, 0x61, 0xf4,
|
||||
0x01, 0x40, 0x7a, 0x96, 0x0e, 0x95, 0xa5, 0x43, 0xab, 0x86, 0x43, 0x87, 0xfa, 0x92, 0x18, 0x84,
|
||||
0x98, 0x82, 0xd3, 0x15, 0x34, 0x47, 0x03, 0x76, 0x96, 0xe5, 0x77, 0x33, 0x37, 0x40, 0xb8, 0xb4,
|
||||
0xb0, 0xb5, 0x66, 0xc8, 0x49, 0xa9, 0xd3, 0xeb, 0xdc, 0xb2, 0x16, 0xc0, 0x4e, 0x42, 0x7d, 0x1a,
|
||||
0xf1, 0xc0, 0x0d, 0xa5, 0xb3, 0x35, 0x62, 0x60, 0xf0, 0x32, 0x2c, 0x19, 0x6a, 0xd2, 0xb4, 0x61,
|
||||
0xac, 0x75, 0x87, 0x61, 0xa6, 0x7b, 0xa4, 0x26, 0xf1, 0x3d, 0xcd, 0x28, 0x68, 0x54, 0xbe, 0xff,
|
||||
0xbe, 0x81, 0xf8, 0x6b, 0x1b, 0x6a, 0xe6, 0x0d, 0xda, 0x86, 0x05, 0xc9, 0x23, 0xca, 0x83, 0x26,
|
||||
0x4a, 0xce, 0x15, 0x43, 0x0e, 0x71, 0x5f, 0x74, 0x73, 0x82, 0xa7, 0x01, 0x3f, 0xeb, 0xf8, 0xc4,
|
||||
0xe4, 0x11, 0x4e, 0xbb, 0x5e, 0xa8, 0x04, 0x66, 0x4e, 0xe7, 0x18, 0x84, 0xa1, 0x96, 0x43, 0x3a,
|
||||
0xcf, 0x05, 0x1c, 0xda, 0x82, 0x15, 0x29, 0xb2, 0x4b, 0x39, 0x0f, 0xa2, 0x53, 0x76, 0x54, 0xc8,
|
||||
0xfc, 0xc4, 0x3b, 0x74, 0x0b, 0x2e, 0x4e, 0xc2, 0xeb, 0xa2, 0x98, 0x72, 0x8b, 0x7f, 0xb1, 0x60,
|
||||
0xc1, 0x70, 0x49, 0x94, 0x53, 0x20, 0x13, 0xc4, 0x87, 0x6a, 0x08, 0x69, 0x58, 0x14, 0x2f, 0x0f,
|
||||
0x7a, 0x94, 0x71, 0xb7, 0xd7, 0x97, 0xae, 0x95, 0x48, 0x8e, 0x10, 0xb7, 0x52, 0x87, 0x6e, 0xdb,
|
||||
0x2a, 0xc9, 0x11, 0xe8, 0x2a, 0xd4, 0x45, 0x2d, 0x07, 0x9e, 0xcb, 0x83, 0x38, 0x7a, 0x40, 0x87,
|
||||
0xd2, 0x9b, 0x59, 0x32, 0x82, 0x15, 0xf3, 0x86, 0x51, 0x9a, 0x5a, 0x5d, 0x23, 0xf2, 0x8c, 0xae,
|
||||
0x01, 0x32, 0x42, 0x9c, 0x45, 0xa3, 0x2c, 0x29, 0x26, 0xdc, 0xe0, 0x23, 0xa8, 0x17, 0x13, 0x85,
|
||||
0xda, 0xe3, 0x89, 0xad, 0x15, 0xf3, 0x26, 0xac, 0x0f, 0x4e, 0x23, 0x97, 0x0f, 0x12, 0xaa, 0xd2,
|
||||
0x96, 0x23, 0xf0, 0x2e, 0xac, 0x4c, 0x4a, 0xbd, 0x6c, 0x67, 0xf7, 0x45, 0x41, 0x6a, 0x8e, 0x50,
|
||||
0x75, 0x6b, 0xeb, 0xba, 0xfd, 0xc1, 0x82, 0x95, 0xae, 0x99, 0x86, 0x9d, 0x38, 0xe2, 0x62, 0xe8,
|
||||
0x7e, 0x0c, 0xb5, 0xb4, 0xfd, 0x76, 0x69, 0x48, 0x39, 0x9d, 0x50, 0xc0, 0x87, 0xc6, 0xf5, 0xfe,
|
||||
0x0c, 0x29, 0x90, 0xa3, 0x3b, 0xca, 0x3b, 0xc5, 0x6d, 0x4b, 0xee, 0x8b, 0xa3, 0xe5, 0xaf, 0x99,
|
||||
0x4d, 0xe2, 0xbb, 0xf3, 0x30, 0xf7, 0xdc, 0x0d, 0x07, 0x14, 0xb7, 0xa0, 0x66, 0x2a, 0x19, 0x6b,
|
||||
0xba, 0x0e, 0x2c, 0x74, 0x79, 0x9c, 0x64, 0xf1, 0x9a, 0x3e, 0xe2, 0x44, 0xac, 0x79, 0x9c, 0xb8,
|
||||
0xa7, 0xf4, 0xa1, 0xdb, 0xa3, 0xca, 0x7d, 0x13, 0x85, 0x6f, 0xaa, 0x92, 0x53, 0x9a, 0xfe, 0x0b,
|
||||
0x8b, 0xbe, 0x3c, 0x25, 0x47, 0x94, 0x26, 0x5a, 0x60, 0x11, 0x89, 0x3f, 0x87, 0xd5, 0x42, 0xec,
|
||||
0xba, 0x91, 0xdb, 0x67, 0x67, 0x31, 0x17, 0x1d, 0x97, 0x52, 0xfa, 0x1d, 0x3f, 0x9d, 0xf5, 0x55,
|
||||
0x62, 0x60, 0xc6, 0xc5, 0xdb, 0x93, 0xc4, 0x7f, 0x63, 0x41, 0x2d, 0x13, 0xbd, 0xeb, 0x72, 0x17,
|
||||
0xdd, 0x86, 0x79, 0x2f, 0x4d, 0x8f, 0xda, 0x1f, 0x57, 0x46, 0x03, 0x3a, 0x92, 0x45, 0x92, 0xd1,
|
||||
0x8b, 0x85, 0xcd, 0x94, 0x75, 0x2a, 0x19, 0xed, 0x69, 0xbc, 0x99, 0x17, 0x44, 0x73, 0xe0, 0x67,
|
||||
0x6a, 0xba, 0x75, 0x07, 0xc7, 0xcc, 0x4b, 0x82, 0xbe, 0xe8, 0x0c, 0xd1, 0x96, 0x2a, 0xbe, 0x99,
|
||||
0x8b, 0x1a, 0x46, 0x77, 0xa0, 0xec, 0x7a, 0x82, 0x4a, 0xad, 0x2c, 0x3c, 0xa6, 0xcc, 0x90, 0xb4,
|
||||
0x2d, 0x29, 0x89, 0xe2, 0xc0, 0x1d, 0x58, 0xde, 0xf6, 0xc2, 0x6d, 0xdf, 0x27, 0xd4, 0x8b, 0x13,
|
||||
0xff, 0xaf, 0xb7, 0xb9, 0xb1, 0x88, 0xec, 0xc2, 0x22, 0xc2, 0x9f, 0xc2, 0x4a, 0x51, 0x94, 0x1a,
|
||||
0xcc, 0x4d, 0xa8, 0x24, 0x12, 0xa3, 0x85, 0x69, 0xf8, 0x0d, 0xd2, 0x3e, 0x91, 0xd2, 0xee, 0x53,
|
||||
0x9e, 0x4a, 0x63, 0x6f, 0x65, 0x99, 0xeb, 0x85, 0xfb, 0xf9, 0x63, 0x25, 0x03, 0xf1, 0x26, 0xac,
|
||||
0x8e, 0xc8, 0x52, 0xa6, 0xc9, 0x7d, 0x2b, 0x51, 0x32, 0xa8, 0x35, 0x92, 0x81, 0xf8, 0x0b, 0x70,
|
||||
0x64, 0xb5, 0x8b, 0x95, 0xff, 0x2f, 0x3c, 0x71, 0xf0, 0x3e, 0x2c, 0x19, 0xf2, 0xdf, 0xe1, 0xc9,
|
||||
0x82, 0x7f, 0xb2, 0x60, 0x51, 0x8a, 0x7a, 0x40, 0x87, 0x4f, 0x44, 0x27, 0x8b, 0xa1, 0xf4, 0x8c,
|
||||
0x0e, 0x0b, 0xbd, 0x94, 0x23, 0xc4, 0x7b, 0x50, 0x36, 0xbc, 0x0a, 0x78, 0x0a, 0xa0, 0xff, 0xc3,
|
||||
0x52, 0x36, 0xe6, 0xbb, 0x7a, 0x0c, 0x96, 0x24, 0xc5, 0xf8, 0x85, 0x68, 0xa9, 0x3e, 0xa5, 0x49,
|
||||
0x4e, 0x99, 0x6e, 0xa6, 0x22, 0xd2, 0x8c, 0xd7, 0x5c, 0x21, 0x5e, 0x78, 0x1f, 0xea, 0x05, 0x93,
|
||||
0x19, 0xba, 0x25, 0x6d, 0x4e, 0x01, 0xe5, 0xbc, 0x19, 0xc4, 0x02, 0x35, 0xc9, 0x49, 0xf1, 0x8f,
|
||||
0x86, 0xf7, 0x9d, 0x28, 0xa2, 0x89, 0x58, 0x20, 0xc2, 0x8c, 0xec, 0x05, 0x2d, 0xce, 0x85, 0xa5,
|
||||
0x66, 0x8f, 0x2c, 0x35, 0x1d, 0x8f, 0x92, 0x19, 0x8f, 0xab, 0x50, 0xd7, 0x9b, 0xed, 0x20, 0xf0,
|
||||
0x92, 0x58, 0xba, 0x58, 0x22, 0x23, 0x58, 0x11, 0x6b, 0x55, 0x65, 0xda, 0xcb, 0x1c, 0x81, 0x1c,
|
||||
0x28, 0x3d, 0xa3, 0x43, 0xb9, 0xa9, 0xaa, 0x44, 0x1c, 0xf1, 0x83, 0xd4, 0x5c, 0xf7, 0xf4, 0x1f,
|
||||
0x98, 0xa3, 0x1b, 0x7f, 0x58, 0x50, 0xd9, 0x4b, 0x92, 0x9d, 0xd8, 0xa7, 0x0c, 0xd5, 0x01, 0x1e,
|
||||
0x47, 0xf4, 0xbc, 0x4f, 0x3d, 0x4e, 0x7d, 0x67, 0x06, 0x39, 0xea, 0x6d, 0x73, 0x10, 0x30, 0x16,
|
||||
0x44, 0xa7, 0x8e, 0x85, 0x2e, 0xa8, 0xb1, 0xbb, 0x77, 0x1e, 0x30, 0xce, 0x1c, 0x1b, 0x2d, 0xc3,
|
||||
0x05, 0x89, 0x78, 0x18, 0xf3, 0x4e, 0xb4, 0xe3, 0x7a, 0x67, 0xd4, 0x29, 0x21, 0x04, 0x75, 0x89,
|
||||
0xec, 0xb0, 0x74, 0x3c, 0xfb, 0xce, 0x2c, 0x6a, 0xc0, 0x8a, 0xac, 0x1e, 0xf6, 0x30, 0xe6, 0xaa,
|
||||
0x5a, 0x83, 0xe3, 0x90, 0x3a, 0x73, 0x68, 0x05, 0x1c, 0x42, 0x3d, 0x1a, 0xf4, 0x79, 0x87, 0x75,
|
||||
0xa2, 0xe7, 0x6e, 0x18, 0xf8, 0x4e, 0x59, 0xc8, 0x50, 0x80, 0x5a, 0xc9, 0xce, 0xbc, 0xa0, 0xdc,
|
||||
0x1d, 0xa4, 0xab, 0x9e, 0xaa, 0x8e, 0x72, 0x2a, 0xe8, 0x12, 0xac, 0x3d, 0x8a, 0xe3, 0x03, 0x37,
|
||||
0x1a, 0x2a, 0x1c, 0xbb, 0x97, 0xc4, 0x3d, 0xa1, 0xcc, 0xa9, 0x0a, 0x83, 0xf7, 0x92, 0x24, 0x4e,
|
||||
0x0e, 0x4f, 0x4e, 0x18, 0xe5, 0x8e, 0xbf, 0x71, 0x1b, 0xd6, 0xa6, 0x0c, 0x34, 0xb4, 0x08, 0x55,
|
||||
0x85, 0x3d, 0xa6, 0xce, 0x8c, 0x60, 0x7d, 0x1c, 0x31, 0x8d, 0xb0, 0x36, 0xfe, 0x07, 0x95, 0xec,
|
||||
0xf9, 0x8e, 0x16, 0x60, 0xbe, 0x13, 0x05, 0xe2, 0x0d, 0xea, 0xcc, 0xa0, 0x32, 0xd8, 0x4f, 0x36,
|
||||
0x1d, 0x4b, 0xfe, 0x6e, 0x39, 0xf6, 0xc6, 0x7b, 0x00, 0xf9, 0xb3, 0x18, 0x55, 0x60, 0xf6, 0x51,
|
||||
0x42, 0x85, 0xc4, 0x79, 0x28, 0x6d, 0x7b, 0xa1, 0x63, 0xa1, 0x1a, 0x54, 0xb2, 0x4a, 0x74, 0xec,
|
||||
0xad, 0x6f, 0xcb, 0x50, 0x4d, 0x6d, 0x1a, 0x46, 0x1e, 0xda, 0x81, 0x4a, 0xd6, 0xa7, 0xa8, 0x39,
|
||||
0xb1, 0x79, 0xa5, 0x93, 0xcd, 0x4b, 0x93, 0x1b, 0x3b, 0x1d, 0x03, 0xf7, 0xa0, 0xaa, 0x67, 0x03,
|
||||
0xba, 0x34, 0xda, 0x05, 0xc6, 0x44, 0x6a, 0x5e, 0x9e, 0x7c, 0xa9, 0xe4, 0xdc, 0x57, 0xad, 0xb1,
|
||||
0x97, 0x7d, 0x0a, 0x4e, 0xed, 0xa8, 0xe6, 0xd4, 0x9b, 0x75, 0xeb, 0x86, 0x25, 0x0d, 0xca, 0x1e,
|
||||
0xea, 0x45, 0x83, 0x46, 0xbe, 0x12, 0x8a, 0x06, 0x8d, 0xbe, 0xed, 0x0d, 0x39, 0x61, 0x38, 0x49,
|
||||
0x8e, 0x7e, 0xf1, 0x4f, 0x92, 0x63, 0x3c, 0xf5, 0x09, 0x38, 0xf9, 0x37, 0x57, 0x97, 0x27, 0xd4,
|
||||
0xed, 0xa1, 0xcb, 0x63, 0x8f, 0x25, 0xe3, 0x83, 0xac, 0xf9, 0xc6, 0x5b, 0xe9, 0xe3, 0x7e, 0x96,
|
||||
0x76, 0x99, 0xbb, 0x77, 0x90, 0x86, 0x9e, 0xc2, 0x5a, 0x8e, 0x54, 0x0e, 0xbd, 0xbb, 0x91, 0x37,
|
||||
0x2c, 0x74, 0x08, 0x35, 0x73, 0xc1, 0xa2, 0x96, 0x41, 0x3f, 0x61, 0x89, 0x37, 0xaf, 0x4c, 0xbd,
|
||||
0xd7, 0x71, 0x5c, 0x2c, 0xec, 0x45, 0x34, 0xc2, 0x31, 0xb6, 0x7d, 0x9b, 0xed, 0xe9, 0x04, 0xa9,
|
||||
0xcc, 0xbb, 0xef, 0xff, 0xfc, 0xaa, 0x65, 0xbd, 0x7c, 0xd5, 0xb2, 0x7e, 0x7f, 0xd5, 0xb2, 0xbe,
|
||||
0x7f, 0xdd, 0x9a, 0x79, 0xf9, 0xba, 0x35, 0xf3, 0xdb, 0xeb, 0xd6, 0xcc, 0x67, 0xcd, 0xe9, 0xff,
|
||||
0x91, 0x39, 0x2e, 0xcb, 0x9f, 0x9b, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x8f, 0x50, 0x36,
|
||||
0xb6, 0x11, 0x00, 0x00,
|
||||
// 1609 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcf, 0x6f, 0x1b, 0xc5,
|
||||
0x17, 0xcf, 0xae, 0x13, 0xc7, 0x7e, 0x71, 0xdc, 0xcd, 0xc4, 0x69, 0xfc, 0x75, 0x2b, 0xd7, 0x5a,
|
||||
0x7d, 0x55, 0xa2, 0x08, 0xda, 0x26, 0x2d, 0x95, 0xda, 0xc2, 0x21, 0x4d, 0xd2, 0xc6, 0x94, 0x34,
|
||||
0xd1, 0xb8, 0x3f, 0x24, 0x24, 0x90, 0x36, 0xbb, 0x93, 0x64, 0xe9, 0x7a, 0xd7, 0xec, 0x8c, 0xdb,
|
||||
0x58, 0xe2, 0xc2, 0x89, 0x13, 0x88, 0x33, 0xff, 0x03, 0x07, 0xfe, 0x0b, 0x8e, 0x85, 0x13, 0x47,
|
||||
0xd4, 0xde, 0xf9, 0x1b, 0xd0, 0xcc, 0xce, 0xce, 0xce, 0xae, 0xed, 0x52, 0x51, 0xb8, 0xc4, 0x3b,
|
||||
0x6f, 0xde, 0x8f, 0xcf, 0x7b, 0xf3, 0x7e, 0xcc, 0x04, 0x36, 0xdc, 0xa8, 0xdf, 0x8f, 0x42, 0x3a,
|
||||
0x70, 0x5c, 0x72, 0x55, 0xfc, 0xa5, 0xa3, 0xd0, 0x1d, 0xc4, 0x11, 0x8b, 0xae, 0x8a, 0xbf, 0x34,
|
||||
0xa3, 0x5e, 0x11, 0x04, 0x54, 0x55, 0x04, 0x9b, 0xc0, 0xe2, 0x1e, 0x71, 0xbc, 0xde, 0x28, 0x74,
|
||||
0xb1, 0x13, 0x9e, 0x10, 0x84, 0x60, 0xf6, 0x38, 0x8e, 0xfa, 0x4d, 0xa3, 0x63, 0xac, 0xcd, 0x62,
|
||||
0xf1, 0x8d, 0xea, 0x60, 0xb2, 0xa8, 0x69, 0x0a, 0x8a, 0xc9, 0x22, 0xd4, 0x80, 0xb9, 0xc0, 0xef,
|
||||
0xfb, 0xac, 0x59, 0xea, 0x18, 0x6b, 0x8b, 0x38, 0x59, 0xa0, 0x16, 0x54, 0x48, 0x40, 0xfa, 0x24,
|
||||
0x64, 0xb4, 0x39, 0xdb, 0x31, 0xd6, 0x2a, 0x58, 0xad, 0xed, 0x33, 0xa8, 0x2b, 0x33, 0x84, 0x0e,
|
||||
0x03, 0xc6, 0xed, 0x9c, 0x3a, 0xf4, 0x54, 0xd8, 0xa9, 0x61, 0xf1, 0x8d, 0x3e, 0xd2, 0x34, 0x98,
|
||||
0x9d, 0xd2, 0xda, 0xc2, 0x66, 0xe7, 0x4a, 0x86, 0x3d, 0xaf, 0x60, 0x37, 0x61, 0xcc, 0x6c, 0x70,
|
||||
0x54, 0x6e, 0x34, 0x0c, 0x15, 0x2a, 0xb1, 0xb0, 0xef, 0xc0, 0xca, 0x44, 0x41, 0xee, 0x94, 0xef,
|
||||
0x09, 0xf3, 0x55, 0x6c, 0xfa, 0x9e, 0x00, 0x44, 0x1c, 0x4f, 0xb8, 0x59, 0xc5, 0xe2, 0xdb, 0xfe,
|
||||
0xde, 0x80, 0x73, 0x99, 0xf4, 0x57, 0x43, 0x42, 0x19, 0x6a, 0xc2, 0xbc, 0xc0, 0xd4, 0x4d, 0x85,
|
||||
0xd3, 0x25, 0xba, 0x06, 0xe5, 0x98, 0xc7, 0x30, 0x05, 0xdf, 0x9c, 0x04, 0x9e, 0x33, 0x60, 0xc9,
|
||||
0x87, 0xae, 0x42, 0xc5, 0xf3, 0x8f, 0x8f, 0x1f, 0x8d, 0x06, 0x44, 0xa0, 0xae, 0x6f, 0x2e, 0x6b,
|
||||
0x32, 0x3b, 0x72, 0x0b, 0x2b, 0x26, 0xfb, 0x0c, 0x2c, 0xcd, 0x9b, 0x41, 0x14, 0x52, 0x82, 0xae,
|
||||
0xc3, 0x7c, 0x2c, 0x3c, 0xa3, 0x4d, 0x43, 0xd8, 0xfd, 0xdf, 0xd4, 0xa0, 0xe1, 0x94, 0x33, 0x67,
|
||||
0xd9, 0x7c, 0x1b, 0xcb, 0xbf, 0x19, 0xb0, 0x74, 0x70, 0xf4, 0x25, 0x71, 0x19, 0x57, 0xb7, 0x4f,
|
||||
0x28, 0x75, 0x4e, 0xc8, 0x1b, 0x82, 0x71, 0x11, 0xaa, 0x71, 0x12, 0xb1, 0x6e, 0x1a, 0xd3, 0x8c,
|
||||
0xc0, 0xe5, 0x62, 0x32, 0x08, 0x46, 0x5d, 0x4f, 0xf8, 0x5d, 0xc5, 0xe9, 0x92, 0xef, 0x0c, 0x9c,
|
||||
0x51, 0x10, 0x39, 0x9e, 0x48, 0xa2, 0x1a, 0x4e, 0x97, 0x3c, 0xbf, 0x22, 0x01, 0xa0, 0xeb, 0x35,
|
||||
0xe7, 0x84, 0x90, 0x5a, 0xa3, 0x0f, 0x01, 0x92, 0x6f, 0xe1, 0x50, 0x59, 0x38, 0xb4, 0xa2, 0x39,
|
||||
0x74, 0xa0, 0x36, 0xb1, 0xc6, 0x68, 0x13, 0xb0, 0x7a, 0x9c, 0xe7, 0x70, 0x48, 0x4f, 0xd3, 0xf3,
|
||||
0xdd, 0xc8, 0x00, 0x70, 0x97, 0x16, 0x36, 0x57, 0x35, 0x3d, 0x09, 0x77, 0xb2, 0x9d, 0x21, 0x6b,
|
||||
0x03, 0x6c, 0xc7, 0xc4, 0x23, 0x21, 0xf3, 0x9d, 0x40, 0x38, 0x5b, 0xc3, 0x1a, 0xc5, 0x5e, 0x86,
|
||||
0x25, 0xcd, 0x4c, 0x72, 0x6c, 0xb6, 0xad, 0x6c, 0x07, 0x41, 0x6a, 0xbb, 0x90, 0x93, 0xf6, 0xd7,
|
||||
0x4a, 0x90, 0xf3, 0xc8, 0xf3, 0xfe, 0x07, 0x00, 0x6f, 0x00, 0x38, 0x6e, 0x80, 0x89, 0x1b, 0xc5,
|
||||
0x5e, 0x9a, 0x9d, 0x0d, 0x4d, 0x6a, 0x2b, 0xdd, 0xc4, 0x1a, 0x9f, 0x7d, 0x07, 0xaa, 0x6a, 0x83,
|
||||
0xfb, 0xe8, 0xb8, 0xc1, 0xa1, 0x66, 0xb8, 0x86, 0x35, 0x8a, 0x84, 0x6e, 0x2a, 0xe8, 0xdf, 0x98,
|
||||
0x50, 0xd3, 0xc1, 0xa0, 0x2d, 0x58, 0x10, 0x06, 0x79, 0x46, 0x92, 0x58, 0x42, 0xbf, 0xa4, 0x81,
|
||||
0xc0, 0xce, 0x8b, 0x5e, 0xc6, 0xf0, 0xd4, 0x67, 0xa7, 0x5d, 0x0f, 0xeb, 0x32, 0x05, 0x0c, 0xe6,
|
||||
0x18, 0x06, 0x1b, 0x6a, 0xd9, 0x4a, 0xa5, 0x56, 0x8e, 0x86, 0x36, 0xa1, 0x21, 0x54, 0xf6, 0x08,
|
||||
0x63, 0x7e, 0x78, 0x42, 0x0f, 0x73, 0xc9, 0x36, 0x71, 0x0f, 0xdd, 0x84, 0xf3, 0x93, 0xe8, 0x2a,
|
||||
0x0f, 0xa7, 0xec, 0xda, 0xbf, 0x1a, 0xb0, 0xa0, 0xb9, 0xc4, 0x33, 0xd8, 0x17, 0x39, 0xc1, 0x46,
|
||||
0x32, 0x82, 0x6a, 0xcd, 0xeb, 0x85, 0xf9, 0x7d, 0x42, 0x99, 0xd3, 0x1f, 0x08, 0xd7, 0x4a, 0x38,
|
||||
0x23, 0xf0, 0x5d, 0x61, 0x43, 0x75, 0x8a, 0x2a, 0xce, 0x08, 0xe8, 0x32, 0xd4, 0x79, 0xf9, 0xf8,
|
||||
0xae, 0xc3, 0xfc, 0x28, 0x7c, 0x40, 0x46, 0xc2, 0x9b, 0x59, 0x5c, 0xa0, 0xf2, 0x16, 0x47, 0x09,
|
||||
0x49, 0x50, 0xd7, 0xb0, 0xf8, 0x46, 0x57, 0x00, 0x69, 0x21, 0x4e, 0xa3, 0x51, 0x16, 0x1c, 0x13,
|
||||
0x76, 0xec, 0x43, 0xa8, 0xe7, 0x0f, 0x0a, 0x75, 0xc6, 0x0f, 0xb6, 0x96, 0x3f, 0x37, 0x8e, 0xde,
|
||||
0x3f, 0x09, 0x1d, 0x36, 0x8c, 0x89, 0x3c, 0xb6, 0x8c, 0x60, 0xef, 0x40, 0x63, 0xd2, 0xd1, 0x8b,
|
||||
0x0e, 0xe2, 0xbc, 0xc8, 0x69, 0xcd, 0x08, 0x63, 0xf9, 0xf6, 0xa3, 0x01, 0x8d, 0x9e, 0x7e, 0x0c,
|
||||
0xdb, 0x51, 0xc8, 0x78, 0x9f, 0xff, 0x18, 0x6a, 0x49, 0xc5, 0xef, 0x90, 0x80, 0x30, 0x32, 0xa1,
|
||||
0x66, 0x0e, 0xb4, 0xed, 0xbd, 0x19, 0x9c, 0x63, 0x47, 0xb7, 0xa5, 0x77, 0x52, 0xda, 0x14, 0xd2,
|
||||
0xe7, 0x8b, 0x15, 0xa7, 0x84, 0x75, 0xe6, 0xbb, 0xf3, 0x30, 0xf7, 0xdc, 0x09, 0x86, 0xc4, 0x6e,
|
||||
0x43, 0x4d, 0x37, 0x32, 0x56, 0xe7, 0x5d, 0x58, 0xe8, 0xb1, 0x28, 0x4e, 0xe3, 0x35, 0xbd, 0xab,
|
||||
0xf2, 0x58, 0xb3, 0x28, 0x76, 0x4e, 0xc8, 0x43, 0xa7, 0x4f, 0xa4, 0xfb, 0x3a, 0xc9, 0xbe, 0x2e,
|
||||
0x53, 0x4e, 0x5a, 0xfa, 0x3f, 0x2c, 0x7a, 0xe2, 0x2b, 0x3e, 0x24, 0x24, 0x56, 0x0a, 0xf3, 0x44,
|
||||
0xfb, 0x73, 0x58, 0xc9, 0xc5, 0xae, 0x17, 0x3a, 0x03, 0x7a, 0x1a, 0x31, 0x5e, 0x71, 0x09, 0xa7,
|
||||
0xd7, 0xf5, 0x92, 0xf1, 0x52, 0xc5, 0x1a, 0x65, 0x5c, 0xbd, 0x39, 0x49, 0xfd, 0xb7, 0x06, 0xd4,
|
||||
0x52, 0xd5, 0x3b, 0x0e, 0x73, 0xd0, 0x2d, 0x98, 0x77, 0x93, 0xe3, 0x91, 0x23, 0xeb, 0x52, 0x31,
|
||||
0xa0, 0x85, 0x53, 0xc4, 0x29, 0x3f, 0xbf, 0x23, 0x50, 0x89, 0x4e, 0x1e, 0x46, 0x67, 0x9a, 0x6c,
|
||||
0xea, 0x05, 0x56, 0x12, 0xf6, 0x33, 0xd9, 0x50, 0x7b, 0xc3, 0x23, 0xea, 0xc6, 0xfe, 0x80, 0x57,
|
||||
0x06, 0x2f, 0x4b, 0x19, 0xdf, 0xd4, 0x45, 0xb5, 0x46, 0xb7, 0xa1, 0xec, 0xb8, 0x9c, 0x4b, 0x4e,
|
||||
0x49, 0x7b, 0xcc, 0x98, 0xa6, 0x69, 0x4b, 0x70, 0x62, 0x29, 0x61, 0x77, 0x61, 0x79, 0xcb, 0x0d,
|
||||
0xb6, 0x3c, 0x4f, 0xf6, 0xd6, 0xbf, 0xbd, 0x40, 0x68, 0xb3, 0xcf, 0xcc, 0xcd, 0x3e, 0xfb, 0x53,
|
||||
0x68, 0xe4, 0x55, 0xc9, 0x59, 0xd0, 0x82, 0x4a, 0x2c, 0x28, 0x4a, 0x99, 0x5a, 0xbf, 0x41, 0xdb,
|
||||
0x27, 0x42, 0xdb, 0x7d, 0xc2, 0x64, 0xa7, 0x7f, 0x2b, 0x64, 0x8e, 0x1b, 0xec, 0x65, 0xf7, 0xa3,
|
||||
0x74, 0x69, 0x6f, 0xc0, 0x4a, 0x41, 0x97, 0x84, 0x26, 0x46, 0x7c, 0x32, 0x70, 0x78, 0x50, 0x6b,
|
||||
0x38, 0x5d, 0xda, 0x5f, 0x80, 0x25, 0xb2, 0x9d, 0xdf, 0x32, 0xfe, 0x83, 0x5b, 0x95, 0xbd, 0x07,
|
||||
0x4b, 0x9a, 0xfe, 0x77, 0xb8, 0x25, 0xd9, 0x3f, 0x1b, 0xb0, 0x28, 0x54, 0x3d, 0x20, 0xa3, 0x27,
|
||||
0xbc, 0x92, 0x79, 0x53, 0x7a, 0x46, 0x46, 0xb9, 0x5a, 0xca, 0x08, 0xfc, 0x0a, 0x2a, 0x0a, 0x5e,
|
||||
0x06, 0x3c, 0x59, 0xa0, 0xf7, 0x61, 0x29, 0x6d, 0xf3, 0x3d, 0xd5, 0x06, 0x4b, 0x82, 0x63, 0x7c,
|
||||
0x83, 0x97, 0xd4, 0x80, 0x90, 0x38, 0xe3, 0x4c, 0x26, 0x53, 0x9e, 0xa8, 0xc7, 0x6b, 0x2e, 0x17,
|
||||
0x2f, 0x7b, 0x0f, 0xea, 0x39, 0xc8, 0x14, 0xdd, 0x14, 0x98, 0x93, 0x85, 0x74, 0x5e, 0x0f, 0x62,
|
||||
0x8e, 0x1b, 0x67, 0xac, 0xf6, 0x4f, 0x9a, 0xf7, 0xdd, 0x30, 0x24, 0x31, 0x1f, 0x20, 0x1c, 0x46,
|
||||
0x7a, 0x69, 0xe7, 0xdf, 0xb9, 0xa1, 0x66, 0x16, 0x86, 0x9a, 0x8a, 0x47, 0x49, 0x8f, 0xc7, 0x65,
|
||||
0xa8, 0xab, 0xc9, 0xb6, 0xef, 0xbb, 0x71, 0x24, 0x5c, 0x2c, 0xe1, 0x02, 0x95, 0xc7, 0x5a, 0x66,
|
||||
0x99, 0xf2, 0x32, 0x23, 0x20, 0x0b, 0x4a, 0xcf, 0xc8, 0x48, 0x4c, 0xaa, 0x2a, 0xe6, 0x9f, 0xf6,
|
||||
0x83, 0x04, 0xae, 0x73, 0xf2, 0x2f, 0xf4, 0xd1, 0xf5, 0x3f, 0x0d, 0xa8, 0xec, 0xc6, 0xf1, 0x76,
|
||||
0xe4, 0x11, 0x8a, 0xea, 0x00, 0x8f, 0x43, 0x72, 0x36, 0x20, 0x2e, 0x23, 0x9e, 0x35, 0x83, 0x2c,
|
||||
0x79, 0xb7, 0xd9, 0xf7, 0x29, 0xf5, 0xc3, 0x13, 0xcb, 0x40, 0xe7, 0x64, 0xdb, 0xdd, 0x3d, 0xf3,
|
||||
0x29, 0xa3, 0x96, 0x89, 0x96, 0xe1, 0x9c, 0x20, 0x3c, 0x8c, 0x58, 0x37, 0xdc, 0x76, 0xdc, 0x53,
|
||||
0x62, 0x95, 0x10, 0x82, 0xba, 0x20, 0x76, 0x69, 0xd2, 0x9e, 0x3d, 0x6b, 0x16, 0x35, 0xa1, 0x21,
|
||||
0xb2, 0x87, 0x3e, 0x8c, 0x98, 0xcc, 0x56, 0xff, 0x28, 0x20, 0xd6, 0x1c, 0x6a, 0x80, 0x85, 0x89,
|
||||
0x4b, 0xfc, 0x01, 0xeb, 0xd2, 0x6e, 0xf8, 0xdc, 0x09, 0x7c, 0xcf, 0x2a, 0x73, 0x1d, 0x72, 0x21,
|
||||
0x47, 0xb2, 0x35, 0xcf, 0x39, 0x77, 0x86, 0xc9, 0xa8, 0x27, 0xb2, 0xa2, 0xac, 0x0a, 0xba, 0x00,
|
||||
0xab, 0x8f, 0xa2, 0x68, 0xdf, 0x09, 0x47, 0x92, 0x46, 0xef, 0xc5, 0x51, 0x9f, 0x1b, 0xb3, 0xaa,
|
||||
0x1c, 0xf0, 0x6e, 0x1c, 0x47, 0xf1, 0xc1, 0xf1, 0x31, 0x25, 0xcc, 0xf2, 0xd6, 0x6f, 0xc1, 0xea,
|
||||
0x94, 0x86, 0x86, 0x16, 0xa1, 0x2a, 0xa9, 0x47, 0xc4, 0x9a, 0xe1, 0xa2, 0x8f, 0x43, 0xaa, 0x08,
|
||||
0xc6, 0xfa, 0x7b, 0x50, 0x49, 0x5f, 0x0c, 0x68, 0x01, 0xe6, 0xbb, 0xa1, 0xcf, 0xaf, 0xbd, 0xd6,
|
||||
0x0c, 0x2a, 0x83, 0xf9, 0x64, 0xc3, 0x32, 0xc4, 0xef, 0xa6, 0x65, 0xae, 0x7f, 0x00, 0x90, 0xdd,
|
||||
0xc4, 0x51, 0x05, 0x66, 0x1f, 0xc5, 0x84, 0x6b, 0x9c, 0x87, 0xd2, 0x96, 0x1b, 0x58, 0x06, 0xaa,
|
||||
0x41, 0x25, 0xcd, 0x44, 0xcb, 0xdc, 0xfc, 0xae, 0x0c, 0xd5, 0x04, 0xd3, 0x28, 0x74, 0xd1, 0x36,
|
||||
0x54, 0xd2, 0x3a, 0x45, 0xad, 0x89, 0xc5, 0x2b, 0x9c, 0x6c, 0x5d, 0x98, 0x5c, 0xd8, 0x49, 0x1b,
|
||||
0xb8, 0x07, 0x55, 0xd5, 0x1b, 0xd0, 0x85, 0x62, 0x15, 0x68, 0x1d, 0xa9, 0x75, 0x71, 0xf2, 0xa6,
|
||||
0xd4, 0x73, 0x5f, 0x96, 0xc6, 0x6e, 0xfa, 0xfa, 0x9c, 0x5a, 0x51, 0xad, 0xa9, 0x3b, 0x6b, 0xc6,
|
||||
0x35, 0x43, 0x00, 0x4a, 0xdf, 0x06, 0x79, 0x40, 0x85, 0x87, 0x49, 0x1e, 0x50, 0xf1, 0x39, 0xa1,
|
||||
0xe9, 0x09, 0x82, 0x49, 0x7a, 0xd4, 0x23, 0x63, 0x92, 0x1e, 0xed, 0x75, 0x81, 0xc1, 0xca, 0x9e,
|
||||
0x79, 0x3d, 0x16, 0x13, 0xa7, 0x8f, 0x2e, 0x8e, 0x5d, 0x96, 0xb4, 0x37, 0x60, 0xeb, 0x8d, 0xbb,
|
||||
0xc2, 0xc7, 0xbd, 0xf4, 0xd8, 0xc5, 0xd9, 0xbd, 0x83, 0x36, 0xf4, 0x14, 0x56, 0x33, 0xa2, 0x74,
|
||||
0xe8, 0xdd, 0x41, 0x5e, 0x33, 0xd0, 0x01, 0xd4, 0xf4, 0x01, 0x8b, 0xda, 0xf9, 0xd7, 0x51, 0x71,
|
||||
0x88, 0xb7, 0x2e, 0x4d, 0xdd, 0x57, 0x71, 0x5c, 0xcc, 0xcd, 0x45, 0x54, 0x90, 0x18, 0x9b, 0xbe,
|
||||
0xad, 0xce, 0x74, 0x86, 0x44, 0xe7, 0xdd, 0x1b, 0xbf, 0xbc, 0x6a, 0x1b, 0x2f, 0x5f, 0xb5, 0x8d,
|
||||
0x3f, 0x5e, 0xb5, 0x8d, 0x1f, 0x5e, 0xb7, 0x67, 0x5e, 0xbe, 0x6e, 0xcf, 0xfc, 0xfe, 0xba, 0x3d,
|
||||
0xf3, 0x59, 0x6b, 0xfa, 0x3f, 0x81, 0x8e, 0xca, 0xe2, 0xe7, 0xfa, 0x5f, 0x01, 0x00, 0x00, 0xff,
|
||||
0xff, 0x9d, 0x2c, 0x6b, 0x14, 0x29, 0x12, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *HeadSyncRange) Marshal() (dAtA []byte, err error) {
|
||||
|
@ -2729,6 +2800,20 @@ func (m *SpacePullResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.AclRecords) > 0 {
|
||||
for iNdEx := len(m.AclRecords) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.AclRecords[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintSpacesync(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
}
|
||||
if m.Payload != nil {
|
||||
{
|
||||
size, err := m.Payload.MarshalToSizedBuffer(dAtA[:i])
|
||||
|
@ -2744,6 +2829,43 @@ func (m *SpacePullResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *AclRecord) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *AclRecord) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *AclRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Id) > 0 {
|
||||
i -= len(m.Id)
|
||||
copy(dAtA[i:], m.Id)
|
||||
i = encodeVarintSpacesync(dAtA, i, uint64(len(m.Id)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if len(m.AclPayload) > 0 {
|
||||
i -= len(m.AclPayload)
|
||||
copy(dAtA[i:], m.AclPayload)
|
||||
i = encodeVarintSpacesync(dAtA, i, uint64(len(m.AclPayload)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *SpacePayload) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
|
@ -3849,6 +3971,29 @@ func (m *SpacePullResponse) Size() (n int) {
|
|||
l = m.Payload.Size()
|
||||
n += 1 + l + sovSpacesync(uint64(l))
|
||||
}
|
||||
if len(m.AclRecords) > 0 {
|
||||
for _, e := range m.AclRecords {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovSpacesync(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *AclRecord) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.AclPayload)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSpacesync(uint64(l))
|
||||
}
|
||||
l = len(m.Id)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSpacesync(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -5444,6 +5589,156 @@ func (m *SpacePullResponse) Unmarshal(dAtA []byte) error {
|
|||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AclRecords", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSpacesync
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthSpacesync
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthSpacesync
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.AclRecords = append(m.AclRecords, &AclRecord{})
|
||||
if err := m.AclRecords[len(m.AclRecords)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSpacesync(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthSpacesync
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *AclRecord) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSpacesync
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: AclRecord: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: AclRecord: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AclPayload", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSpacesync
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthSpacesync
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthSpacesync
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.AclPayload = append(m.AclPayload[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.AclPayload == nil {
|
||||
m.AclPayload = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSpacesync
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthSpacesync
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthSpacesync
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Id = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSpacesync(dAtA[iNdEx:])
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"time"
|
||||
|
||||
anystore "github.com/anyproto/any-store"
|
||||
"github.com/anyproto/go-chash"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"storj.io/drpc"
|
||||
|
@ -44,114 +43,12 @@ import (
|
|||
"github.com/anyproto/any-sync/net/streampool/streamhandler"
|
||||
"github.com/anyproto/any-sync/node/nodeclient"
|
||||
"github.com/anyproto/any-sync/nodeconf"
|
||||
"github.com/anyproto/any-sync/nodeconf/testconf"
|
||||
"github.com/anyproto/any-sync/testutil/accounttest"
|
||||
"github.com/anyproto/any-sync/util/crypto"
|
||||
"github.com/anyproto/any-sync/util/syncqueues"
|
||||
)
|
||||
|
||||
type mockConf struct {
|
||||
id string
|
||||
networkId string
|
||||
configuration nodeconf.Configuration
|
||||
}
|
||||
|
||||
func (m *mockConf) NetworkCompatibilityStatus() nodeconf.NetworkCompatibilityStatus {
|
||||
return nodeconf.NetworkCompatibilityStatusOk
|
||||
}
|
||||
|
||||
func (m *mockConf) Init(a *app.App) (err error) {
|
||||
accountKeys := a.MustComponent(accountService.CName).(accountService.Service).Account()
|
||||
networkId := accountKeys.SignKey.GetPublic().Network()
|
||||
node := nodeconf.Node{
|
||||
PeerId: accountKeys.PeerId,
|
||||
Addresses: []string{"127.0.0.1:4430"},
|
||||
Types: []nodeconf.NodeType{nodeconf.NodeTypeTree},
|
||||
}
|
||||
m.id = networkId
|
||||
m.networkId = networkId
|
||||
m.configuration = nodeconf.Configuration{
|
||||
Id: networkId,
|
||||
NetworkId: networkId,
|
||||
Nodes: []nodeconf.Node{node},
|
||||
CreationTime: time.Now(),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) Name() (name string) {
|
||||
return nodeconf.CName
|
||||
}
|
||||
|
||||
func (m *mockConf) Run(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) Close(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) Id() string {
|
||||
return m.id
|
||||
}
|
||||
|
||||
func (m *mockConf) Configuration() nodeconf.Configuration {
|
||||
return m.configuration
|
||||
}
|
||||
|
||||
func (m *mockConf) NodeIds(spaceId string) []string {
|
||||
var nodeIds []string
|
||||
for _, node := range m.configuration.Nodes {
|
||||
nodeIds = append(nodeIds, node.PeerId)
|
||||
}
|
||||
return nodeIds
|
||||
}
|
||||
|
||||
func (m *mockConf) IsResponsible(spaceId string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *mockConf) FilePeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) ConsensusPeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) CoordinatorPeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) NamingNodePeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) PaymentProcessingNodePeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) PeerAddresses(peerId string) (addrs []string, ok bool) {
|
||||
if peerId == m.configuration.Nodes[0].PeerId {
|
||||
return m.configuration.Nodes[0].Addresses, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *mockConf) CHash() chash.CHash {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConf) Partition(spaceId string) (part int) {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *mockConf) NodeTypes(nodeId string) []nodeconf.NodeType {
|
||||
if nodeId == m.configuration.Nodes[0].PeerId {
|
||||
return m.configuration.Nodes[0].Types
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ nodeclient.NodeClient = (*mockNodeClient)(nil)
|
||||
|
||||
type mockNodeClient struct {
|
||||
|
@ -174,6 +71,7 @@ func (m mockNodeClient) AclAddRecord(ctx context.Context, spaceId string, rec *c
|
|||
}
|
||||
|
||||
type mockPeerManager struct {
|
||||
peer peer.Peer
|
||||
}
|
||||
|
||||
func (p *mockPeerManager) BroadcastMessage(ctx context.Context, msg drpc.Message) error {
|
||||
|
@ -193,6 +91,9 @@ func (p *mockPeerManager) Name() (name string) {
|
|||
}
|
||||
|
||||
func (p *mockPeerManager) GetResponsiblePeers(ctx context.Context) (peers []peer.Peer, err error) {
|
||||
if p.peer != nil {
|
||||
return []peer.Peer{p.peer}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -218,6 +119,7 @@ func (m *testPeerManagerProvider) NewPeerManager(ctx context.Context, spaceId st
|
|||
}
|
||||
|
||||
type mockPeerManagerProvider struct {
|
||||
peer peer.Peer
|
||||
}
|
||||
|
||||
func (m *mockPeerManagerProvider) Init(a *app.App) (err error) {
|
||||
|
@ -229,7 +131,7 @@ func (m *mockPeerManagerProvider) Name() (name string) {
|
|||
}
|
||||
|
||||
func (m *mockPeerManagerProvider) NewPeerManager(ctx context.Context, spaceId string) (sm peermanager.PeerManager, err error) {
|
||||
return &mockPeerManager{}, nil
|
||||
return &mockPeerManager{m.peer}, nil
|
||||
}
|
||||
|
||||
type mockPool struct {
|
||||
|
@ -594,7 +496,10 @@ func (s *streamOpener) NewReadMessage() drpc.Message {
|
|||
}
|
||||
|
||||
func (s *streamOpener) Init(a *app.App) (err error) {
|
||||
s.spaceGetter = a.MustComponent(RpcName).(*RpcServer)
|
||||
sp := a.Component(RpcName)
|
||||
if sp != nil {
|
||||
s.spaceGetter = sp.(*RpcServer)
|
||||
}
|
||||
s.streamPool = a.MustComponent(streampool.CName).(streampool.StreamPool)
|
||||
return nil
|
||||
}
|
||||
|
@ -654,7 +559,7 @@ func newFixture(t *testing.T) *spaceFixture {
|
|||
app: &app.App{},
|
||||
config: &mockConfig{},
|
||||
account: &accounttest.AccountTestService{},
|
||||
configurationService: &mockConf{},
|
||||
configurationService: &testconf.StubConf{},
|
||||
streamOpener: newStreamOpener("spaceId"),
|
||||
peerManagerProvider: &testPeerManagerProvider{},
|
||||
storageProvider: &spaceStorageProvider{rootPath: t.TempDir()},
|
||||
|
@ -699,7 +604,7 @@ func newPeerFixture(t *testing.T, spaceId string, keys *accountdata.AccountKeys,
|
|||
app: &app.App{},
|
||||
config: &mockConfig{},
|
||||
account: accounttest.NewWithAcc(keys),
|
||||
configurationService: &mockConf{},
|
||||
configurationService: &testconf.StubConf{},
|
||||
storageProvider: provider,
|
||||
streamOpener: newStreamOpener(spaceId),
|
||||
peerManagerProvider: &testPeerManagerProvider{},
|
||||
|
|
|
@ -43,7 +43,7 @@ func (s *spaceStorageProvider) WaitSpaceStorage(ctx context.Context, id string)
|
|||
}
|
||||
dbPath := path.Join(s.rootPath, id)
|
||||
if _, err := os.Stat(dbPath); err != nil {
|
||||
return nil, err
|
||||
return nil, spacestorage.ErrSpaceStorageMissing
|
||||
}
|
||||
db, err := anystore.Open(ctx, dbPath, nil)
|
||||
if err != nil {
|
||||
|
|
14
go.mod
14
go.mod
|
@ -6,7 +6,7 @@ toolchain go1.24.1
|
|||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0
|
||||
github.com/anyproto/any-store v0.1.11
|
||||
github.com/anyproto/any-store v0.2.2
|
||||
github.com/anyproto/go-chash v0.1.0
|
||||
github.com/anyproto/go-slip10 v1.0.0
|
||||
github.com/anyproto/go-slip21 v1.0.0
|
||||
|
@ -30,7 +30,7 @@ require (
|
|||
github.com/multiformats/go-multibase v0.2.0
|
||||
github.com/multiformats/go-multihash v0.2.3
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/quic-go/quic-go v0.51.0
|
||||
github.com/quic-go/quic-go v0.52.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
github.com/zeebo/blake3 v0.2.4
|
||||
|
@ -38,7 +38,7 @@ require (
|
|||
go.uber.org/mock v0.5.2
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.38.0
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/time v0.11.0
|
||||
|
@ -65,7 +65,7 @@ require (
|
|||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
github.com/ipfs/go-bitfield v1.1.0 // indirect
|
||||
|
@ -112,9 +112,9 @@ require (
|
|||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
lukechampine.com/blake3 v1.4.0 // indirect
|
||||
modernc.org/libc v1.61.13 // indirect
|
||||
modernc.org/libc v1.65.7 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.8.2 // indirect
|
||||
modernc.org/sqlite v1.36.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.37.0 // indirect
|
||||
zombiezen.com/go/sqlite v1.4.0 // indirect
|
||||
)
|
||||
|
|
44
go.sum
44
go.sum
|
@ -6,8 +6,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
|
|||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0=
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
|
||||
github.com/anyproto/any-store v0.1.11 h1:xoaDVF8FJEI6V37fMw/R3ptBCLHj0kYiImwWxC1Ryu8=
|
||||
github.com/anyproto/any-store v0.1.11/go.mod h1:X3UkQ2zLATYNED3gFhY2VcdfDOeJvpEQ0PmDO90A9Yo=
|
||||
github.com/anyproto/any-store v0.2.2 h1:/sN0oS6dX+oYO6qgW3qrx43hmKa5U7zLiJZHcesh5KE=
|
||||
github.com/anyproto/any-store v0.2.2/go.mod h1:9o+4sliO3+Lw2HGwyk9oBmoR83c8mwQ7TK7yfyGptPs=
|
||||
github.com/anyproto/go-chash v0.1.0 h1:I9meTPjXFRfXZHRJzjOHC/XF7Q5vzysKkiT/grsogXY=
|
||||
github.com/anyproto/go-chash v0.1.0/go.mod h1:0UjNQi3PDazP0fINpFYu6VKhuna+W/V+1vpXHAfNgLY=
|
||||
github.com/anyproto/go-slip10 v1.0.0 h1:uAEtSuudR3jJBOfkOXf3bErxVoxbuKwdoJN55M1i6IA=
|
||||
|
@ -100,8 +100,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
|
@ -293,8 +293,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
|
|||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
||||
github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
|
||||
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg=
|
||||
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
|
@ -379,8 +379,8 @@ golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||
|
@ -457,26 +457,26 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
||||
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
||||
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
||||
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
|
||||
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
|
||||
modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
|
||||
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
||||
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
|
||||
modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
|
||||
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ=
|
||||
modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
|
||||
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
|
||||
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
|
|
@ -31,13 +31,14 @@ var (
|
|||
// ProtoVersion 4 - new sync compatible version
|
||||
// ProtoVersion 5 - sync with no entry space
|
||||
// ProtoVersion 6 - sync with key value messages
|
||||
CompatibleVersion = uint32(4)
|
||||
ProtoVersion = uint32(5)
|
||||
KeyValueVersion = uint32(6)
|
||||
// ProtoVersion 7 - sync with new invites
|
||||
CompatibleVersion = uint32(5)
|
||||
ProtoVersion = uint32(6)
|
||||
NewInvitesVersion = uint32(7)
|
||||
)
|
||||
|
||||
var (
|
||||
compatibleVersions = []uint32{CompatibleVersion, ProtoVersion, KeyValueVersion}
|
||||
compatibleVersions = []uint32{CompatibleVersion, ProtoVersion, NewInvitesVersion}
|
||||
)
|
||||
|
||||
func New() SecureService {
|
||||
|
|
115
nodeconf/testconf/nodeconf.go
Normal file
115
nodeconf/testconf/nodeconf.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package testconf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/anyproto/go-chash"
|
||||
|
||||
accountService "github.com/anyproto/any-sync/accountservice"
|
||||
"github.com/anyproto/any-sync/app"
|
||||
"github.com/anyproto/any-sync/nodeconf"
|
||||
)
|
||||
|
||||
type StubConf struct {
|
||||
id string
|
||||
networkId string
|
||||
configuration nodeconf.Configuration
|
||||
}
|
||||
|
||||
func (m *StubConf) NetworkCompatibilityStatus() nodeconf.NetworkCompatibilityStatus {
|
||||
return nodeconf.NetworkCompatibilityStatusOk
|
||||
}
|
||||
|
||||
func (m *StubConf) Init(a *app.App) (err error) {
|
||||
accountKeys := a.MustComponent(accountService.CName).(accountService.Service).Account()
|
||||
networkId := accountKeys.SignKey.GetPublic().Network()
|
||||
node := nodeconf.Node{
|
||||
PeerId: accountKeys.PeerId,
|
||||
Addresses: []string{"127.0.0.1:4430"},
|
||||
Types: []nodeconf.NodeType{nodeconf.NodeTypeTree},
|
||||
}
|
||||
m.id = networkId
|
||||
m.networkId = networkId
|
||||
m.configuration = nodeconf.Configuration{
|
||||
Id: networkId,
|
||||
NetworkId: networkId,
|
||||
Nodes: []nodeconf.Node{node},
|
||||
CreationTime: time.Now(),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) Name() (name string) {
|
||||
return nodeconf.CName
|
||||
}
|
||||
|
||||
func (m *StubConf) Run(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) Close(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) Id() string {
|
||||
return m.id
|
||||
}
|
||||
|
||||
func (m *StubConf) Configuration() nodeconf.Configuration {
|
||||
return m.configuration
|
||||
}
|
||||
|
||||
func (m *StubConf) NodeIds(spaceId string) []string {
|
||||
var nodeIds []string
|
||||
for _, node := range m.configuration.Nodes {
|
||||
nodeIds = append(nodeIds, node.PeerId)
|
||||
}
|
||||
return nodeIds
|
||||
}
|
||||
|
||||
func (m *StubConf) IsResponsible(spaceId string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *StubConf) FilePeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) ConsensusPeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) CoordinatorPeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) NamingNodePeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) PaymentProcessingNodePeers() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) PeerAddresses(peerId string) (addrs []string, ok bool) {
|
||||
if peerId == m.configuration.Nodes[0].PeerId {
|
||||
return m.configuration.Nodes[0].Addresses, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *StubConf) CHash() chash.CHash {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StubConf) Partition(spaceId string) (part int) {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StubConf) NodeTypes(nodeId string) []nodeconf.NodeType {
|
||||
if nodeId == m.configuration.Nodes[0].PeerId {
|
||||
return m.configuration.Nodes[0].Types
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
//
|
||||
// mockgen -destination=mock/mock_paymentserviceclient.go -package=mock_paymentserviceclient github.com/anyproto/any-sync/paymentservice/paymentserviceclient AnyPpClientService
|
||||
//
|
||||
|
||||
// Package mock_paymentserviceclient is a generated GoMock package.
|
||||
package mock_paymentserviceclient
|
||||
|
||||
|
@ -22,7 +21,6 @@ import (
|
|||
type MockAnyPpClientService struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockAnyPpClientServiceMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockAnyPpClientServiceMockRecorder is the mock recorder for MockAnyPpClientService.
|
||||
|
@ -43,122 +41,122 @@ func (m *MockAnyPpClientService) EXPECT() *MockAnyPpClientServiceMockRecorder {
|
|||
}
|
||||
|
||||
// BuySubscription mocks base method.
|
||||
func (m *MockAnyPpClientService) BuySubscription(ctx context.Context, in *paymentserviceproto.BuySubscriptionRequestSigned) (*paymentserviceproto.BuySubscriptionResponse, error) {
|
||||
func (m *MockAnyPpClientService) BuySubscription(arg0 context.Context, arg1 *paymentserviceproto.BuySubscriptionRequestSigned) (*paymentserviceproto.BuySubscriptionResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "BuySubscription", ctx, in)
|
||||
ret := m.ctrl.Call(m, "BuySubscription", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.BuySubscriptionResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// BuySubscription indicates an expected call of BuySubscription.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) BuySubscription(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) BuySubscription(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuySubscription", reflect.TypeOf((*MockAnyPpClientService)(nil).BuySubscription), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuySubscription", reflect.TypeOf((*MockAnyPpClientService)(nil).BuySubscription), arg0, arg1)
|
||||
}
|
||||
|
||||
// FinalizeSubscription mocks base method.
|
||||
func (m *MockAnyPpClientService) FinalizeSubscription(ctx context.Context, in *paymentserviceproto.FinalizeSubscriptionRequestSigned) (*paymentserviceproto.FinalizeSubscriptionResponse, error) {
|
||||
func (m *MockAnyPpClientService) FinalizeSubscription(arg0 context.Context, arg1 *paymentserviceproto.FinalizeSubscriptionRequestSigned) (*paymentserviceproto.FinalizeSubscriptionResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FinalizeSubscription", ctx, in)
|
||||
ret := m.ctrl.Call(m, "FinalizeSubscription", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.FinalizeSubscriptionResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FinalizeSubscription indicates an expected call of FinalizeSubscription.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) FinalizeSubscription(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) FinalizeSubscription(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalizeSubscription", reflect.TypeOf((*MockAnyPpClientService)(nil).FinalizeSubscription), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalizeSubscription", reflect.TypeOf((*MockAnyPpClientService)(nil).FinalizeSubscription), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetAllTiers mocks base method.
|
||||
func (m *MockAnyPpClientService) GetAllTiers(ctx context.Context, in *paymentserviceproto.GetTiersRequestSigned) (*paymentserviceproto.GetTiersResponse, error) {
|
||||
func (m *MockAnyPpClientService) GetAllTiers(arg0 context.Context, arg1 *paymentserviceproto.GetTiersRequestSigned) (*paymentserviceproto.GetTiersResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAllTiers", ctx, in)
|
||||
ret := m.ctrl.Call(m, "GetAllTiers", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.GetTiersResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAllTiers indicates an expected call of GetAllTiers.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetAllTiers(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetAllTiers(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllTiers", reflect.TypeOf((*MockAnyPpClientService)(nil).GetAllTiers), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllTiers", reflect.TypeOf((*MockAnyPpClientService)(nil).GetAllTiers), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetSubscriptionPortalLink mocks base method.
|
||||
func (m *MockAnyPpClientService) GetSubscriptionPortalLink(ctx context.Context, in *paymentserviceproto.GetSubscriptionPortalLinkRequestSigned) (*paymentserviceproto.GetSubscriptionPortalLinkResponse, error) {
|
||||
func (m *MockAnyPpClientService) GetSubscriptionPortalLink(arg0 context.Context, arg1 *paymentserviceproto.GetSubscriptionPortalLinkRequestSigned) (*paymentserviceproto.GetSubscriptionPortalLinkResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSubscriptionPortalLink", ctx, in)
|
||||
ret := m.ctrl.Call(m, "GetSubscriptionPortalLink", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.GetSubscriptionPortalLinkResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetSubscriptionPortalLink indicates an expected call of GetSubscriptionPortalLink.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetSubscriptionPortalLink(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetSubscriptionPortalLink(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubscriptionPortalLink", reflect.TypeOf((*MockAnyPpClientService)(nil).GetSubscriptionPortalLink), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubscriptionPortalLink", reflect.TypeOf((*MockAnyPpClientService)(nil).GetSubscriptionPortalLink), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetSubscriptionStatus mocks base method.
|
||||
func (m *MockAnyPpClientService) GetSubscriptionStatus(ctx context.Context, in *paymentserviceproto.GetSubscriptionRequestSigned) (*paymentserviceproto.GetSubscriptionResponse, error) {
|
||||
func (m *MockAnyPpClientService) GetSubscriptionStatus(arg0 context.Context, arg1 *paymentserviceproto.GetSubscriptionRequestSigned) (*paymentserviceproto.GetSubscriptionResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSubscriptionStatus", ctx, in)
|
||||
ret := m.ctrl.Call(m, "GetSubscriptionStatus", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.GetSubscriptionResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetSubscriptionStatus indicates an expected call of GetSubscriptionStatus.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetSubscriptionStatus(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetSubscriptionStatus(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubscriptionStatus", reflect.TypeOf((*MockAnyPpClientService)(nil).GetSubscriptionStatus), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubscriptionStatus", reflect.TypeOf((*MockAnyPpClientService)(nil).GetSubscriptionStatus), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetVerificationEmail mocks base method.
|
||||
func (m *MockAnyPpClientService) GetVerificationEmail(ctx context.Context, in *paymentserviceproto.GetVerificationEmailRequestSigned) (*paymentserviceproto.GetVerificationEmailResponse, error) {
|
||||
func (m *MockAnyPpClientService) GetVerificationEmail(arg0 context.Context, arg1 *paymentserviceproto.GetVerificationEmailRequestSigned) (*paymentserviceproto.GetVerificationEmailResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetVerificationEmail", ctx, in)
|
||||
ret := m.ctrl.Call(m, "GetVerificationEmail", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.GetVerificationEmailResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetVerificationEmail indicates an expected call of GetVerificationEmail.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetVerificationEmail(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) GetVerificationEmail(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVerificationEmail", reflect.TypeOf((*MockAnyPpClientService)(nil).GetVerificationEmail), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVerificationEmail", reflect.TypeOf((*MockAnyPpClientService)(nil).GetVerificationEmail), arg0, arg1)
|
||||
}
|
||||
|
||||
// Init mocks base method.
|
||||
func (m *MockAnyPpClientService) Init(a *app.App) error {
|
||||
func (m *MockAnyPpClientService) Init(arg0 *app.App) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Init", a)
|
||||
ret := m.ctrl.Call(m, "Init", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Init indicates an expected call of Init.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) Init(a any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) Init(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockAnyPpClientService)(nil).Init), a)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockAnyPpClientService)(nil).Init), arg0)
|
||||
}
|
||||
|
||||
// IsNameValid mocks base method.
|
||||
func (m *MockAnyPpClientService) IsNameValid(ctx context.Context, in *paymentserviceproto.IsNameValidRequest) (*paymentserviceproto.IsNameValidResponse, error) {
|
||||
func (m *MockAnyPpClientService) IsNameValid(arg0 context.Context, arg1 *paymentserviceproto.IsNameValidRequest) (*paymentserviceproto.IsNameValidResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "IsNameValid", ctx, in)
|
||||
ret := m.ctrl.Call(m, "IsNameValid", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.IsNameValidResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// IsNameValid indicates an expected call of IsNameValid.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) IsNameValid(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) IsNameValid(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNameValid", reflect.TypeOf((*MockAnyPpClientService)(nil).IsNameValid), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNameValid", reflect.TypeOf((*MockAnyPpClientService)(nil).IsNameValid), arg0, arg1)
|
||||
}
|
||||
|
||||
// Name mocks base method.
|
||||
|
@ -176,31 +174,31 @@ func (mr *MockAnyPpClientServiceMockRecorder) Name() *gomock.Call {
|
|||
}
|
||||
|
||||
// VerifyAppStoreReceipt mocks base method.
|
||||
func (m *MockAnyPpClientService) VerifyAppStoreReceipt(ctx context.Context, in *paymentserviceproto.VerifyAppStoreReceiptRequestSigned) (*paymentserviceproto.VerifyAppStoreReceiptResponse, error) {
|
||||
func (m *MockAnyPpClientService) VerifyAppStoreReceipt(arg0 context.Context, arg1 *paymentserviceproto.VerifyAppStoreReceiptRequestSigned) (*paymentserviceproto.VerifyAppStoreReceiptResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "VerifyAppStoreReceipt", ctx, in)
|
||||
ret := m.ctrl.Call(m, "VerifyAppStoreReceipt", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.VerifyAppStoreReceiptResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// VerifyAppStoreReceipt indicates an expected call of VerifyAppStoreReceipt.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) VerifyAppStoreReceipt(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) VerifyAppStoreReceipt(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyAppStoreReceipt", reflect.TypeOf((*MockAnyPpClientService)(nil).VerifyAppStoreReceipt), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyAppStoreReceipt", reflect.TypeOf((*MockAnyPpClientService)(nil).VerifyAppStoreReceipt), arg0, arg1)
|
||||
}
|
||||
|
||||
// VerifyEmail mocks base method.
|
||||
func (m *MockAnyPpClientService) VerifyEmail(ctx context.Context, in *paymentserviceproto.VerifyEmailRequestSigned) (*paymentserviceproto.VerifyEmailResponse, error) {
|
||||
func (m *MockAnyPpClientService) VerifyEmail(arg0 context.Context, arg1 *paymentserviceproto.VerifyEmailRequestSigned) (*paymentserviceproto.VerifyEmailResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "VerifyEmail", ctx, in)
|
||||
ret := m.ctrl.Call(m, "VerifyEmail", arg0, arg1)
|
||||
ret0, _ := ret[0].(*paymentserviceproto.VerifyEmailResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// VerifyEmail indicates an expected call of VerifyEmail.
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) VerifyEmail(ctx, in any) *gomock.Call {
|
||||
func (mr *MockAnyPpClientServiceMockRecorder) VerifyEmail(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyEmail", reflect.TypeOf((*MockAnyPpClientService)(nil).VerifyEmail), ctx, in)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyEmail", reflect.TypeOf((*MockAnyPpClientService)(nil).VerifyEmail), arg0, arg1)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ package crypto
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/anyproto/any-sync/util/strkey"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
"github.com/anyproto/any-sync/util/strkey"
|
||||
)
|
||||
|
||||
func EncodeKeyToString[T Key](key T) (str string, err error) {
|
||||
|
@ -55,3 +57,11 @@ func DecodePeerId(peerId string) (PubKey, error) {
|
|||
}
|
||||
return UnmarshalEd25519PublicKey(raw)
|
||||
}
|
||||
|
||||
func DecodeNetworkId(networkId string) (PubKey, error) {
|
||||
pubKeyRaw, err := strkey.Decode(strkey.NetworkAddressVersionByte, networkId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return UnmarshalEd25519PublicKey(pubKeyRaw)
|
||||
}
|
||||
|
|
18
util/crypto/decode_test.go
Normal file
18
util/crypto/decode_test.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDecodeNetworkId(t *testing.T) {
|
||||
_, pubKey, err := GenerateRandomEd25519KeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
networkId := pubKey.Network()
|
||||
require.Equal(t, uint8('N'), networkId[0])
|
||||
decodedKey, err := DecodeNetworkId(networkId)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, pubKey, decodedKey)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue