diff --git a/Makefile b/Makefile index aa0fa75f..003fbbf7 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ proto: protoc --gogofaster_out=$(PKGMAP):. --go-drpc_out=protolib=github.com/gogo/protobuf:. net/streampool/testservice/protos/*.proto protoc --gogofaster_out=:. net/secureservice/handshake/handshakeproto/protos/*.proto protoc --gogofaster_out=$(PKGMAP):. --go-drpc_out=protolib=github.com/gogo/protobuf:. coordinator/coordinatorproto/protos/*.proto + protoc --gogofaster_out=:. --go-drpc_out=protolib=github.com/gogo/protobuf:. consensus/consensusproto/protos/*.proto deps: go mod download diff --git a/README.md b/README.md new file mode 100644 index 00000000..62e78090 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Any-Sync +Any-Sync is an open-source protocol designed to create high-performance, local-first, peer-to-peer, end-to-end encrypted applications that facilitate seamless collaboration among multiple users and devices. + +By utilizing this protocol, users can rest assured that they retain complete control over their data and digital experience. They are empowered to freely transition between various service providers, or even opt to self-host the applications. + +This ensures utmost flexibility and autonomy for users in managing their personal information and digital interactions. + +## Introduction +Most existing information management tools are implemented on centralized client-server architecture or designed for an offline-first single-user usage. Either way there are trade-offs for users: they can face restricted freedoms and privacy violations or compromise on the functionality of tools to avoid this. + +We believe this goes against fundamental digital freedoms and that a new generation of software is needed that will respect these freedoms, while providing best in-class user experience. + +Our goal with `any-sync` is to develop a protocol that will enable the deployment of this software. + +Features: +- Conflict-free data replication across multiple devices and agents +- Built-in end-to-end encryption +- Cryptographically verifiable history of changes +- Adoption to frequent operations (high performance) +- Reliable and scalable infrastructure +- Simultaneous support of p2p and remote communication + +## Protocol explanation +Plese read the [overview](https://tech.anytype.io/any-sync/overview) of protocol entities and design. + +## Implementation + +You can find the various parts of the protocol implemented in Go in the following repositories: +- [`any-sync-node`](https://github.com/anyproto/any-sync-node) — implementation of a sync node responsible for storing spaces and objects. +- [`any-sync-filenode`](https://github.com/anyproto/any-sync-filenode) — implementation of a file node responsible for storing files. +- [`any-sync-coordinator`](https://github.com/anyproto/any-sync-coordinator) — implementation of a coordinator node responsible for network configuration management. + +## Contribution +Thank you for your desire to develop Anytype together. + +Currently, we're not ready to accept PRs, but we will in the nearest future. + +Follow us on [Github](https://github.com/anyproto) and join the [Contributors Community](https://github.com/orgs/anyproto/discussions). + +--- +Made by Any — a Swiss association 🇨🇭 + +Licensed under [MIT License](./LICENSE). \ No newline at end of file diff --git a/accountservice/mock_accountservice/helper.go b/accountservice/mock_accountservice/helper.go index cec6f34b..2dbe031e 100644 --- a/accountservice/mock_accountservice/helper.go +++ b/accountservice/mock_accountservice/helper.go @@ -3,7 +3,7 @@ package mock_accountservice import ( "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/commonspace/object/accountdata" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" ) func NewAccountServiceWithAccount(ctrl *gomock.Controller, acc *accountdata.AccountKeys) *MockService { diff --git a/accountservice/mock_accountservice/mock_accountservice.go b/accountservice/mock_accountservice/mock_accountservice.go index a9bbff7f..7da43f91 100644 --- a/accountservice/mock_accountservice/mock_accountservice.go +++ b/accountservice/mock_accountservice/mock_accountservice.go @@ -9,7 +9,7 @@ import ( app "github.com/anyproto/any-sync/app" accountdata "github.com/anyproto/any-sync/commonspace/object/accountdata" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockService is a mock of Service interface. diff --git a/app/app.go b/app/app.go index a290a82b..5aeda7f3 100644 --- a/app/app.go +++ b/app/app.go @@ -55,11 +55,13 @@ type ComponentStatable interface { // App is the central part of the application // It contains and manages all components type App struct { + parent *App components []Component mu sync.RWMutex startStat Stat stopStat Stat deviceState int + versionName string anySyncVersion string } @@ -77,6 +79,19 @@ func (app *App) Version() string { return GitSummary } +// SetVersionName sets the custom application version +func (app *App) SetVersionName(v string) { + app.versionName = v +} + +// VersionName returns a string with the settled app version or auto-generated version if it didn't set +func (app *App) VersionName() string { + if app.versionName != "" { + return app.versionName + } + return AppName + ":" + GitSummary + "/any-sync:" + app.AnySyncVersion() +} + type Stat struct { SpentMsPerComp map[string]int64 SpentMsTotal int64 @@ -109,6 +124,16 @@ func VersionDescription() string { return fmt.Sprintf("build on %s from %s at #%s(%s)", BuildDate, GitBranch, GitCommit, GitState) } +// ChildApp creates a child container which has access to parent's components +// It doesn't call Start on any of the parent's components +func (app *App) ChildApp() *App { + return &App{ + parent: app, + deviceState: app.deviceState, + anySyncVersion: app.AnySyncVersion(), + } +} + // Register adds service to registry // All components will be started in the order they were registered func (app *App) Register(s Component) *App { @@ -128,10 +153,14 @@ func (app *App) Register(s Component) *App { func (app *App) Component(name string) Component { app.mu.RLock() defer app.mu.RUnlock() - for _, s := range app.components { - if s.Name() == name { - return s + current := app + for current != nil { + for _, s := range current.components { + if s.Name() == name { + return s + } } + current = current.parent } return nil } @@ -149,10 +178,14 @@ func (app *App) MustComponent(name string) Component { func MustComponent[i any](app *App) i { app.mu.RLock() defer app.mu.RUnlock() - for _, s := range app.components { - if v, ok := s.(i); ok { - return v + current := app + for current != nil { + for _, s := range current.components { + if v, ok := s.(i); ok { + return v + } } + current = current.parent } empty := new(i) panic(fmt.Errorf("component with interface %T is not found", empty)) @@ -162,9 +195,13 @@ func MustComponent[i any](app *App) i { func (app *App) ComponentNames() (names []string) { app.mu.RLock() defer app.mu.RUnlock() - names = make([]string, len(app.components)) - for i, c := range app.components { - names[i] = c.Name() + names = make([]string, 0, len(app.components)) + current := app + for current != nil { + for _, c := range current.components { + names = append(names, c.Name()) + } + current = current.parent } return } @@ -225,6 +262,15 @@ func (app *App) Start(ctx context.Context) (err error) { return } +// IterateComponents iterates over all registered components. It's safe for concurrent use. +func (app *App) IterateComponents(fn func(Component)) { + app.mu.RLock() + defer app.mu.RUnlock() + for _, s := range app.components { + fn(s) + } +} + func stackAllGoroutines() []byte { buf := make([]byte, 1024) for { diff --git a/app/app_test.go b/app/app_test.go index cdc52445..5882feab 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -34,6 +34,40 @@ func TestAppServiceRegistry(t *testing.T) { names := app.ComponentNames() assert.Equal(t, names, []string{"c1", "r1", "s1"}) }) + t.Run("Child MustComponent", func(t *testing.T) { + app := app.ChildApp() + app.Register(newTestService(testTypeComponent, "x1", nil, nil)) + for _, name := range []string{"c1", "r1", "s1", "x1"} { + assert.NotPanics(t, func() { app.MustComponent(name) }, name) + } + assert.Panics(t, func() { app.MustComponent("not-registered") }) + }) + t.Run("Child ComponentNames", func(t *testing.T) { + app := app.ChildApp() + app.Register(newTestService(testTypeComponent, "x1", nil, nil)) + names := app.ComponentNames() + assert.Equal(t, names, []string{"x1", "c1", "r1", "s1"}) + }) + t.Run("Child override", func(t *testing.T) { + app := app.ChildApp() + app.Register(newTestService(testTypeRunnable, "s1", nil, nil)) + _ = app.MustComponent("s1").(*testRunnable) + }) +} + +func TestApp_IterateComponents(t *testing.T) { + app := new(App) + + app.Register(newTestService(testTypeRunnable, "c1", nil, nil)) + app.Register(newTestService(testTypeRunnable, "r1", nil, nil)) + app.Register(newTestService(testTypeComponent, "s1", nil, nil)) + + var got []string + app.IterateComponents(func(s Component) { + got = append(got, s.Name()) + }) + + assert.ElementsMatch(t, []string{"c1", "r1", "s1"}, got) } func TestAppStart(t *testing.T) { diff --git a/app/ldiff/mock_ldiff/mock_ldiff.go b/app/ldiff/mock_ldiff/mock_ldiff.go index 20a522b7..8e09ade9 100644 --- a/app/ldiff/mock_ldiff/mock_ldiff.go +++ b/app/ldiff/mock_ldiff/mock_ldiff.go @@ -9,7 +9,7 @@ import ( reflect "reflect" ldiff "github.com/anyproto/any-sync/app/ldiff" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockDiff is a mock of Diff interface. diff --git a/app/ocache/entry.go b/app/ocache/entry.go index fbe60e26..f95af1cc 100644 --- a/app/ocache/entry.go +++ b/app/ocache/entry.go @@ -2,9 +2,10 @@ package ocache import ( "context" - "go.uber.org/zap" "sync" "time" + + "go.uber.org/zap" ) type entryState int @@ -25,6 +26,7 @@ type entry struct { value Object close chan struct{} mx sync.Mutex + cancel context.CancelFunc } func newEntry(id string, value Object, state entryState) *entry { @@ -49,6 +51,20 @@ func (e *entry) isClosing() bool { return e.state == entryStateClosed || e.state == entryStateClosing } +func (e *entry) setCancel(cancel context.CancelFunc) { + e.mx.Lock() + defer e.mx.Unlock() + e.cancel = cancel +} + +func (e *entry) cancelLoad() { + e.mx.Lock() + defer e.mx.Unlock() + if e.cancel != nil { + e.cancel() + } +} + func (e *entry) waitLoad(ctx context.Context, id string) (value Object, err error) { select { case <-ctx.Done(): diff --git a/app/ocache/ocache.go b/app/ocache/ocache.go index 4f130b89..c3bc9a0a 100644 --- a/app/ocache/ocache.go +++ b/app/ocache/ocache.go @@ -3,10 +3,11 @@ package ocache import ( "context" "errors" - "github.com/anyproto/any-sync/app/logger" - "go.uber.org/zap" "sync" "time" + + "github.com/anyproto/any-sync/app/logger" + "go.uber.org/zap" ) var ( @@ -157,7 +158,10 @@ func (c *oCache) Pick(ctx context.Context, id string) (value Object, err error) func (c *oCache) load(ctx context.Context, id string, e *entry) { defer close(e.load) + ctx, cancel := context.WithCancel(ctx) + e.setCancel(cancel) value, err := c.loadFunc(ctx, id) + cancel() c.mu.Lock() defer c.mu.Unlock() @@ -315,6 +319,7 @@ func (c *oCache) Close() (err error) { close(c.closeCh) var toClose []*entry for _, e := range c.data { + e.cancelLoad() toClose = append(toClose, e) } c.mu.Unlock() diff --git a/app/ocache/ocache_test.go b/app/ocache/ocache_test.go index d78345b0..85a9c551 100644 --- a/app/ocache/ocache_test.go +++ b/app/ocache/ocache_test.go @@ -386,6 +386,25 @@ func Test_OCache_Remove(t *testing.T) { }) } +func TestOCacheCancelWhenRemove(t *testing.T) { + c := New(func(ctx context.Context, id string) (value Object, err error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + } + }, WithTTL(time.Millisecond*10)) + stopLoad := make(chan struct{}) + var err error + go func() { + _, err = c.Get(context.TODO(), "id") + stopLoad <- struct{}{} + }() + time.Sleep(time.Millisecond * 10) + c.Close() + <-stopLoad + require.Equal(t, context.Canceled, err) +} + func TestOCacheFuzzy(t *testing.T) { t.Run("test many objects gc, get and remove simultaneously, close after", func(t *testing.T) { tryCloseIds := make(map[string]bool) diff --git a/commonspace/commongetter.go b/commonspace/commongetter.go deleted file mode 100644 index 80476a14..00000000 --- a/commonspace/commongetter.go +++ /dev/null @@ -1,62 +0,0 @@ -package commonspace - -import ( - "context" - "github.com/anyproto/any-sync/commonspace/object/syncobjectgetter" - "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" - "github.com/anyproto/any-sync/commonspace/object/treemanager" - "sync/atomic" -) - -type commonGetter struct { - treemanager.TreeManager - spaceId string - reservedObjects []syncobjectgetter.SyncObject - spaceIsClosed *atomic.Bool -} - -func newCommonGetter(spaceId string, getter treemanager.TreeManager, spaceIsClosed *atomic.Bool) *commonGetter { - return &commonGetter{ - TreeManager: getter, - spaceId: spaceId, - spaceIsClosed: spaceIsClosed, - } -} - -func (c *commonGetter) AddObject(object syncobjectgetter.SyncObject) { - c.reservedObjects = append(c.reservedObjects, object) -} - -func (c *commonGetter) GetTree(ctx context.Context, spaceId, treeId string) (objecttree.ObjectTree, error) { - if c.spaceIsClosed.Load() { - return nil, ErrSpaceClosed - } - if obj := c.getReservedObject(treeId); obj != nil { - return obj.(objecttree.ObjectTree), nil - } - return c.TreeManager.GetTree(ctx, spaceId, treeId) -} - -func (c *commonGetter) getReservedObject(id string) syncobjectgetter.SyncObject { - for _, obj := range c.reservedObjects { - if obj != nil && obj.Id() == id { - return obj - } - } - return nil -} - -func (c *commonGetter) GetObject(ctx context.Context, objectId string) (obj syncobjectgetter.SyncObject, err error) { - if c.spaceIsClosed.Load() { - return nil, ErrSpaceClosed - } - if obj := c.getReservedObject(objectId); obj != nil { - return obj, nil - } - t, err := c.TreeManager.GetTree(ctx, c.spaceId, objectId) - if err != nil { - return - } - obj = t.(syncobjectgetter.SyncObject) - return -} diff --git a/commonspace/config.go b/commonspace/config/config.go similarity index 91% rename from commonspace/config.go rename to commonspace/config/config.go index e5485068..cce4b548 100644 --- a/commonspace/config.go +++ b/commonspace/config/config.go @@ -1,4 +1,4 @@ -package commonspace +package config type ConfigGetter interface { GetSpace() Config diff --git a/commonspace/credentialprovider/credentialprovider.go b/commonspace/credentialprovider/credentialprovider.go index ce570904..5f12b065 100644 --- a/commonspace/credentialprovider/credentialprovider.go +++ b/commonspace/credentialprovider/credentialprovider.go @@ -3,6 +3,7 @@ package credentialprovider import ( "context" + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/commonspace/spacesyncproto" ) @@ -13,12 +14,21 @@ func NewNoOp() CredentialProvider { } type CredentialProvider interface { + app.Component GetCredential(ctx context.Context, spaceHeader *spacesyncproto.RawSpaceHeaderWithId) ([]byte, error) } type noOpProvider struct { } +func (n noOpProvider) Init(a *app.App) (err error) { + return nil +} + +func (n noOpProvider) Name() (name string) { + return CName +} + func (n noOpProvider) GetCredential(ctx context.Context, spaceHeader *spacesyncproto.RawSpaceHeaderWithId) ([]byte, error) { return nil, nil } diff --git a/commonspace/credentialprovider/mock_credentialprovider/mock_credentialprovider.go b/commonspace/credentialprovider/mock_credentialprovider/mock_credentialprovider.go index a1f8dd97..18090381 100644 --- a/commonspace/credentialprovider/mock_credentialprovider/mock_credentialprovider.go +++ b/commonspace/credentialprovider/mock_credentialprovider/mock_credentialprovider.go @@ -8,8 +8,9 @@ import ( context "context" reflect "reflect" + app "github.com/anyproto/any-sync/app" spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockCredentialProvider is a mock of CredentialProvider interface. @@ -49,3 +50,31 @@ func (mr *MockCredentialProviderMockRecorder) GetCredential(arg0, arg1 interface mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCredential", reflect.TypeOf((*MockCredentialProvider)(nil).GetCredential), arg0, arg1) } + +// Init mocks base method. +func (m *MockCredentialProvider) Init(arg0 *app.App) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockCredentialProviderMockRecorder) Init(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockCredentialProvider)(nil).Init), arg0) +} + +// Name mocks base method. +func (m *MockCredentialProvider) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockCredentialProviderMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockCredentialProvider)(nil).Name)) +} diff --git a/commonspace/deletion_test.go b/commonspace/deletion_test.go index 64d6fa11..33745884 100644 --- a/commonspace/deletion_test.go +++ b/commonspace/deletion_test.go @@ -73,13 +73,14 @@ func TestSpaceDeleteIds(t *testing.T) { fx.treeManager.space = spc err = spc.Init(ctx) require.NoError(t, err) - + close(fx.treeManager.waitLoad) + var ids []string for i := 0; i < totalObjs; i++ { // creating a tree bytes := make([]byte, 32) rand.Read(bytes) - doc, err := spc.CreateTree(ctx, objecttree.ObjectTreeCreatePayload{ + doc, err := spc.TreeBuilder().CreateTree(ctx, objecttree.ObjectTreeCreatePayload{ PrivKey: acc.SignKey, ChangeType: "some", SpaceId: spc.Id(), @@ -88,7 +89,7 @@ func TestSpaceDeleteIds(t *testing.T) { Timestamp: time.Now().Unix(), }) require.NoError(t, err) - tr, err := spc.PutTree(ctx, doc, nil) + tr, err := spc.TreeBuilder().PutTree(ctx, doc, nil) require.NoError(t, err) ids = append(ids, tr.Id()) tr.Close() @@ -106,7 +107,7 @@ func TestSpaceDeleteIds(t *testing.T) { func createTree(t *testing.T, ctx context.Context, spc Space, acc *accountdata.AccountKeys) string { bytes := make([]byte, 32) rand.Read(bytes) - doc, err := spc.CreateTree(ctx, objecttree.ObjectTreeCreatePayload{ + doc, err := spc.TreeBuilder().CreateTree(ctx, objecttree.ObjectTreeCreatePayload{ PrivKey: acc.SignKey, ChangeType: "some", SpaceId: spc.Id(), @@ -115,7 +116,7 @@ func createTree(t *testing.T, ctx context.Context, spc Space, acc *accountdata.A Timestamp: time.Now().Unix(), }) require.NoError(t, err) - tr, err := spc.PutTree(ctx, doc, nil) + tr, err := spc.TreeBuilder().PutTree(ctx, doc, nil) require.NoError(t, err) tr.Close() return tr.Id() @@ -147,9 +148,10 @@ func TestSpaceDeleteIdsIncorrectSnapshot(t *testing.T) { // adding space to tree manager fx.treeManager.space = spc err = spc.Init(ctx) + close(fx.treeManager.waitLoad) require.NoError(t, err) - settingsObject := spc.(*space).settingsObject + settingsObject := spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject() var ids []string for i := 0; i < totalObjs; i++ { id := createTree(t, ctx, spc, acc) @@ -183,17 +185,19 @@ func TestSpaceDeleteIdsIncorrectSnapshot(t *testing.T) { spc, err = fx.spaceService.NewSpace(ctx, sp) require.NoError(t, err) require.NotNil(t, spc) + fx.treeManager.waitLoad = make(chan struct{}) fx.treeManager.space = spc fx.treeManager.deletedIds = nil err = spc.Init(ctx) require.NoError(t, err) + close(fx.treeManager.waitLoad) // waiting until everything is deleted time.Sleep(3 * time.Second) require.Equal(t, len(ids), len(fx.treeManager.deletedIds)) // checking that new snapshot will contain all the changes - settingsObject = spc.(*space).settingsObject + settingsObject = spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject() settings.DoSnapshot = func(treeLen int) bool { return true } @@ -230,8 +234,9 @@ func TestSpaceDeleteIdsMarkDeleted(t *testing.T) { fx.treeManager.space = spc err = spc.Init(ctx) require.NoError(t, err) + close(fx.treeManager.waitLoad) - settingsObject := spc.(*space).settingsObject + settingsObject := spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject() var ids []string for i := 0; i < totalObjs; i++ { id := createTree(t, ctx, spc, acc) @@ -259,10 +264,12 @@ func TestSpaceDeleteIdsMarkDeleted(t *testing.T) { require.NoError(t, err) require.NotNil(t, spc) fx.treeManager.space = spc + fx.treeManager.waitLoad = make(chan struct{}) fx.treeManager.deletedIds = nil fx.treeManager.markedIds = nil err = spc.Init(ctx) require.NoError(t, err) + close(fx.treeManager.waitLoad) // waiting until everything is deleted time.Sleep(3 * time.Second) diff --git a/commonspace/settings/settingsstate/deletionstate.go b/commonspace/deletionstate/deletionstate.go similarity index 83% rename from commonspace/settings/settingsstate/deletionstate.go rename to commonspace/deletionstate/deletionstate.go index f36f4fd0..f7d1d9a7 100644 --- a/commonspace/settings/settingsstate/deletionstate.go +++ b/commonspace/deletionstate/deletionstate.go @@ -1,16 +1,22 @@ -//go:generate mockgen -destination mock_settingsstate/mock_settingsstate.go github.com/anyproto/any-sync/commonspace/settings/settingsstate ObjectDeletionState,StateBuilder,ChangeFactory -package settingsstate +//go:generate mockgen -destination mock_deletionstate/mock_deletionstate.go github.com/anyproto/any-sync/commonspace/deletionstate ObjectDeletionState +package deletionstate import ( + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/commonspace/spacestorage" "go.uber.org/zap" "sync" ) +var log = logger.NewNamed(CName) + +const CName = "common.commonspace.deletionstate" + type StateUpdateObserver func(ids []string) type ObjectDeletionState interface { + app.Component AddObserver(observer StateUpdateObserver) Add(ids map[string]struct{}) GetQueued() (ids []string) @@ -28,12 +34,20 @@ type objectDeletionState struct { storage spacestorage.SpaceStorage } -func NewObjectDeletionState(log logger.CtxLogger, storage spacestorage.SpaceStorage) ObjectDeletionState { +func (st *objectDeletionState) Init(a *app.App) (err error) { + st.storage = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + return nil +} + +func (st *objectDeletionState) Name() (name string) { + return CName +} + +func New() ObjectDeletionState { return &objectDeletionState{ log: log, queued: map[string]struct{}{}, deleted: map[string]struct{}{}, - storage: storage, } } diff --git a/commonspace/settings/settingsstate/deletionstate_test.go b/commonspace/deletionstate/deletionstate_test.go similarity index 94% rename from commonspace/settings/settingsstate/deletionstate_test.go rename to commonspace/deletionstate/deletionstate_test.go index ca2ea679..2050fc37 100644 --- a/commonspace/settings/settingsstate/deletionstate_test.go +++ b/commonspace/deletionstate/deletionstate_test.go @@ -1,11 +1,10 @@ -package settingsstate +package deletionstate import ( - "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/spacestorage/mock_spacestorage" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "sort" "testing" ) @@ -19,7 +18,8 @@ type fixture struct { func newFixture(t *testing.T) *fixture { ctrl := gomock.NewController(t) spaceStorage := mock_spacestorage.NewMockSpaceStorage(ctrl) - delState := NewObjectDeletionState(logger.NewNamed("test"), spaceStorage).(*objectDeletionState) + delState := New().(*objectDeletionState) + delState.storage = spaceStorage return &fixture{ ctrl: ctrl, delState: delState, diff --git a/commonspace/deletionstate/mock_deletionstate/mock_deletionstate.go b/commonspace/deletionstate/mock_deletionstate/mock_deletionstate.go new file mode 100644 index 00000000..7706b004 --- /dev/null +++ b/commonspace/deletionstate/mock_deletionstate/mock_deletionstate.go @@ -0,0 +1,144 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/anyproto/any-sync/commonspace/deletionstate (interfaces: ObjectDeletionState) + +// Package mock_deletionstate is a generated GoMock package. +package mock_deletionstate + +import ( + reflect "reflect" + + app "github.com/anyproto/any-sync/app" + deletionstate "github.com/anyproto/any-sync/commonspace/deletionstate" + gomock "go.uber.org/mock/gomock" +) + +// MockObjectDeletionState is a mock of ObjectDeletionState interface. +type MockObjectDeletionState struct { + ctrl *gomock.Controller + recorder *MockObjectDeletionStateMockRecorder +} + +// MockObjectDeletionStateMockRecorder is the mock recorder for MockObjectDeletionState. +type MockObjectDeletionStateMockRecorder struct { + mock *MockObjectDeletionState +} + +// NewMockObjectDeletionState creates a new mock instance. +func NewMockObjectDeletionState(ctrl *gomock.Controller) *MockObjectDeletionState { + mock := &MockObjectDeletionState{ctrl: ctrl} + mock.recorder = &MockObjectDeletionStateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockObjectDeletionState) EXPECT() *MockObjectDeletionStateMockRecorder { + return m.recorder +} + +// Add mocks base method. +func (m *MockObjectDeletionState) Add(arg0 map[string]struct{}) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Add", arg0) +} + +// Add indicates an expected call of Add. +func (mr *MockObjectDeletionStateMockRecorder) Add(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockObjectDeletionState)(nil).Add), arg0) +} + +// AddObserver mocks base method. +func (m *MockObjectDeletionState) AddObserver(arg0 deletionstate.StateUpdateObserver) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddObserver", arg0) +} + +// AddObserver indicates an expected call of AddObserver. +func (mr *MockObjectDeletionStateMockRecorder) AddObserver(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddObserver", reflect.TypeOf((*MockObjectDeletionState)(nil).AddObserver), arg0) +} + +// Delete mocks base method. +func (m *MockObjectDeletionState) Delete(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockObjectDeletionStateMockRecorder) Delete(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockObjectDeletionState)(nil).Delete), arg0) +} + +// Exists mocks base method. +func (m *MockObjectDeletionState) Exists(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Exists", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// Exists indicates an expected call of Exists. +func (mr *MockObjectDeletionStateMockRecorder) Exists(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockObjectDeletionState)(nil).Exists), arg0) +} + +// Filter mocks base method. +func (m *MockObjectDeletionState) Filter(arg0 []string) []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Filter", arg0) + ret0, _ := ret[0].([]string) + return ret0 +} + +// Filter indicates an expected call of Filter. +func (mr *MockObjectDeletionStateMockRecorder) Filter(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockObjectDeletionState)(nil).Filter), arg0) +} + +// GetQueued mocks base method. +func (m *MockObjectDeletionState) GetQueued() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetQueued") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetQueued indicates an expected call of GetQueued. +func (mr *MockObjectDeletionStateMockRecorder) GetQueued() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueued", reflect.TypeOf((*MockObjectDeletionState)(nil).GetQueued)) +} + +// Init mocks base method. +func (m *MockObjectDeletionState) Init(arg0 *app.App) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockObjectDeletionStateMockRecorder) Init(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockObjectDeletionState)(nil).Init), arg0) +} + +// Name mocks base method. +func (m *MockObjectDeletionState) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockObjectDeletionStateMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockObjectDeletionState)(nil).Name)) +} diff --git a/commonspace/headsync/diffsyncer.go b/commonspace/headsync/diffsyncer.go index 8b59c743..810ca5db 100644 --- a/commonspace/headsync/diffsyncer.go +++ b/commonspace/headsync/diffsyncer.go @@ -3,49 +3,45 @@ package headsync import ( "context" "fmt" + "time" + "github.com/anyproto/any-sync/app/ldiff" "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/commonspace/credentialprovider" + "github.com/anyproto/any-sync/commonspace/deletionstate" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl" "github.com/anyproto/any-sync/commonspace/object/treemanager" "github.com/anyproto/any-sync/commonspace/peermanager" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate" "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/rpcerr" + "github.com/anyproto/any-sync/util/slice" "go.uber.org/zap" - "time" ) type DiffSyncer interface { Sync(ctx context.Context) error RemoveObjects(ids []string) UpdateHeads(id string, heads []string) - Init(deletionState settingsstate.ObjectDeletionState) + Init() Close() error } -func newDiffSyncer( - spaceId string, - diff ldiff.Diff, - peerManager peermanager.PeerManager, - cache treemanager.TreeManager, - storage spacestorage.SpaceStorage, - clientFactory spacesyncproto.ClientFactory, - syncStatus syncstatus.StatusUpdater, - credentialProvider credentialprovider.CredentialProvider, - log logger.CtxLogger) DiffSyncer { +func newDiffSyncer(hs *headSync) DiffSyncer { return &diffSyncer{ - diff: diff, - spaceId: spaceId, - treeManager: cache, - storage: storage, - peerManager: peerManager, - clientFactory: clientFactory, - credentialProvider: credentialProvider, + diff: hs.diff, + spaceId: hs.spaceId, + treeManager: hs.treeManager, + storage: hs.storage, + peerManager: hs.peerManager, + clientFactory: spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceSyncClient), + credentialProvider: hs.credentialProvider, log: log, - syncStatus: syncStatus, + syncStatus: hs.syncStatus, + deletionState: hs.deletionState, + syncAcl: hs.syncAcl, } } @@ -57,14 +53,14 @@ type diffSyncer struct { storage spacestorage.SpaceStorage clientFactory spacesyncproto.ClientFactory log logger.CtxLogger - deletionState settingsstate.ObjectDeletionState + deletionState deletionstate.ObjectDeletionState credentialProvider credentialprovider.CredentialProvider syncStatus syncstatus.StatusUpdater treeSyncer treemanager.TreeSyncer + syncAcl syncacl.SyncAcl } -func (d *diffSyncer) Init(deletionState settingsstate.ObjectDeletionState) { - d.deletionState = deletionState +func (d *diffSyncer) Init() { d.deletionState.AddObserver(d.RemoveObjects) d.treeSyncer = d.treeManager.NewTreeSyncer(d.spaceId, d.treeManager) } @@ -115,10 +111,17 @@ func (d *diffSyncer) Sync(ctx context.Context) error { func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error) { ctx = logger.CtxWithFields(ctx, zap.String("peerId", p.Id())) + conn, err := p.AcquireDrpcConn(ctx) + if err != nil { + return + } + defer p.ReleaseDrpcConn(conn) + var ( - cl = d.clientFactory.Client(p) + cl = d.clientFactory.Client(conn) rdiff = NewRemoteDiff(d.spaceId, cl) stateCounter = d.syncStatus.StateCounter() + syncAclId = d.syncAcl.Id() ) newIds, changedIds, removedIds, err := d.diff.Diff(ctx, rdiff) @@ -141,17 +144,29 @@ func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error) // not syncing ids which were removed through settings document missingIds := d.deletionState.Filter(newIds) existingIds := append(d.deletionState.Filter(removedIds), d.deletionState.Filter(changedIds)...) - d.syncStatus.RemoveAllExcept(p.Id(), existingIds, stateCounter) + prevExistingLen := len(existingIds) + existingIds = slice.DiscardFromSlice(existingIds, func(s string) bool { + return s == syncAclId + }) + // if we removed acl head from the list + if len(existingIds) < prevExistingLen { + if syncErr := d.syncAcl.SyncWithPeer(ctx, p.Id()); syncErr != nil { + log.Warn("failed to send acl sync message to peer", zap.String("aclId", syncAclId)) + } + } + + // treeSyncer should not get acl id, that's why we filter existing ids before err = d.treeSyncer.SyncAll(ctx, p.Id(), existingIds, missingIds) if err != nil { return err } - d.log.Info("sync done:", zap.Int("newIds", len(newIds)), + d.log.Info("sync done:", + zap.Int("newIds", len(newIds)), zap.Int("changedIds", len(changedIds)), zap.Int("removedIds", len(removedIds)), - zap.Int("already deleted ids", totalLen-len(existingIds)-len(missingIds)), + zap.Int("already deleted ids", totalLen-prevExistingLen-len(missingIds)), zap.String("peerId", p.Id()), ) return diff --git a/commonspace/headsync/diffsyncer_test.go b/commonspace/headsync/diffsyncer_test.go index fb1ad49c..21ab129b 100644 --- a/commonspace/headsync/diffsyncer_test.go +++ b/commonspace/headsync/diffsyncer_test.go @@ -4,28 +4,19 @@ import ( "bytes" "context" "fmt" - "github.com/anyproto/any-sync/app/ldiff" - "github.com/anyproto/any-sync/app/ldiff/mock_ldiff" - "github.com/anyproto/any-sync/app/logger" - "github.com/anyproto/any-sync/commonspace/credentialprovider/mock_credentialprovider" - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" - "github.com/anyproto/any-sync/commonspace/object/acl/liststorage/mock_liststorage" - "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" - mock_treestorage "github.com/anyproto/any-sync/commonspace/object/tree/treestorage/mock_treestorage" - "github.com/anyproto/any-sync/commonspace/object/treemanager/mock_treemanager" - "github.com/anyproto/any-sync/commonspace/peermanager/mock_peermanager" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate/mock_settingsstate" - "github.com/anyproto/any-sync/commonspace/spacestorage/mock_spacestorage" - "github.com/anyproto/any-sync/commonspace/spacesyncproto" - "github.com/anyproto/any-sync/commonspace/spacesyncproto/mock_spacesyncproto" - "github.com/anyproto/any-sync/commonspace/syncstatus" - "github.com/anyproto/any-sync/net/peer" - "github.com/golang/mock/gomock" - "github.com/libp2p/go-libp2p/core/sec" - "github.com/stretchr/testify/require" - "storj.io/drpc" "testing" "time" + + "github.com/anyproto/any-sync/app/ldiff" + "github.com/anyproto/any-sync/commonspace/object/acl/liststorage/mock_liststorage" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/object/tree/treestorage/mock_treestorage" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/anyproto/any-sync/net/peer" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "storj.io/drpc" ) type pushSpaceRequestMatcher struct { @@ -36,60 +27,6 @@ type pushSpaceRequestMatcher struct { spaceHeader *spacesyncproto.RawSpaceHeaderWithId } -func (p pushSpaceRequestMatcher) Matches(x interface{}) bool { - res, ok := x.(*spacesyncproto.SpacePushRequest) - if !ok { - return false - } - - return res.Payload.AclPayloadId == p.aclRootId && res.Payload.SpaceHeader == p.spaceHeader && res.Payload.SpaceSettingsPayloadId == p.settingsId && bytes.Equal(p.credential, res.Credential) -} - -func (p pushSpaceRequestMatcher) String() string { - return "" -} - -type mockPeer struct{} - -func (m mockPeer) Addr() string { - return "" -} - -func (m mockPeer) TryClose(objectTTL time.Duration) (res bool, err error) { - return true, m.Close() -} - -func (m mockPeer) Id() string { - return "mockId" -} - -func (m mockPeer) LastUsage() time.Time { - return time.Time{} -} - -func (m mockPeer) Secure() sec.SecureConn { - return nil -} - -func (m mockPeer) UpdateLastUsage() { -} - -func (m mockPeer) Close() error { - return nil -} - -func (m mockPeer) Closed() <-chan struct{} { - return make(chan struct{}) -} - -func (m mockPeer) Invoke(ctx context.Context, rpc string, enc drpc.Encoding, in, out drpc.Message) error { - return nil -} - -func (m mockPeer) NewStream(ctx context.Context, rpc string, enc drpc.Encoding) (drpc.Stream, error) { - return nil, nil -} - func newPushSpaceRequestMatcher( spaceId string, aclRootId string, @@ -105,81 +42,159 @@ func newPushSpaceRequestMatcher( } } -func TestDiffSyncer_Sync(t *testing.T) { - // setup - ctx := context.Background() - ctrl := gomock.NewController(t) - defer ctrl.Finish() +func (p pushSpaceRequestMatcher) Matches(x interface{}) bool { + res, ok := x.(*spacesyncproto.SpacePushRequest) + if !ok { + return false + } - diffMock := mock_ldiff.NewMockDiff(ctrl) - peerManagerMock := mock_peermanager.NewMockPeerManager(ctrl) - cacheMock := mock_treemanager.NewMockTreeManager(ctrl) - stMock := mock_spacestorage.NewMockSpaceStorage(ctrl) - clientMock := mock_spacesyncproto.NewMockDRPCSpaceSyncClient(ctrl) - factory := spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceSyncClient { - return clientMock + return res.Payload.AclPayloadId == p.aclRootId && res.Payload.SpaceHeader == p.spaceHeader && res.Payload.SpaceSettingsPayloadId == p.settingsId && bytes.Equal(p.credential, res.Credential) +} + +func (p pushSpaceRequestMatcher) String() string { + return "" +} + +type mockPeer struct { +} + +func (m mockPeer) Id() string { + return "peerId" +} + +func (m mockPeer) Context() context.Context { + return context.Background() +} + +func (m mockPeer) AcquireDrpcConn(ctx context.Context) (drpc.Conn, error) { + return nil, nil +} + +func (m mockPeer) ReleaseDrpcConn(conn drpc.Conn) { + return +} + +func (m mockPeer) DoDrpc(ctx context.Context, do func(conn drpc.Conn) error) error { + return nil +} + +func (m mockPeer) IsClosed() bool { + return false +} + +func (m mockPeer) TryClose(objectTTL time.Duration) (res bool, err error) { + return false, err +} + +func (m mockPeer) Close() (err error) { + return nil +} + +func (fx *headSyncFixture) initDiffSyncer(t *testing.T) { + fx.init(t) + fx.diffSyncer = newDiffSyncer(fx.headSync).(*diffSyncer) + fx.diffSyncer.clientFactory = spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceSyncClient { + return fx.clientMock }) - treeSyncerMock := mock_treemanager.NewMockTreeSyncer(ctrl) - credentialProvider := mock_credentialprovider.NewMockCredentialProvider(ctrl) - delState := mock_settingsstate.NewMockObjectDeletionState(ctrl) - spaceId := "spaceId" - aclRootId := "aclRootId" - l := logger.NewNamed(spaceId) - diffSyncer := newDiffSyncer(spaceId, diffMock, peerManagerMock, cacheMock, stMock, factory, syncstatus.NewNoOpSyncStatus(), credentialProvider, l) - delState.EXPECT().AddObserver(gomock.Any()) - cacheMock.EXPECT().NewTreeSyncer(spaceId, gomock.Any()).Return(treeSyncerMock) - diffSyncer.Init(delState) + fx.deletionStateMock.EXPECT().AddObserver(gomock.Any()) + fx.treeManagerMock.EXPECT().NewTreeSyncer(fx.spaceState.SpaceId, fx.treeManagerMock).Return(fx.treeSyncerMock) + fx.diffSyncer.Init() +} + +func TestDiffSyncer(t *testing.T) { + ctx := context.Background() t.Run("diff syncer sync", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() mPeer := mockPeer{} - peerManagerMock.EXPECT(). + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + fx.peerManagerMock.EXPECT(). GetResponsiblePeers(gomock.Any()). Return([]peer.Peer{mPeer}, nil) - diffMock.EXPECT(). - Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))). + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). Return([]string{"new"}, []string{"changed"}, nil, nil) - delState.EXPECT().Filter([]string{"new"}).Return([]string{"new"}).Times(1) - delState.EXPECT().Filter([]string{"changed"}).Return([]string{"changed"}).Times(1) - delState.EXPECT().Filter(nil).Return(nil).Times(1) - treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"changed"}, []string{"new"}).Return(nil) - require.NoError(t, diffSyncer.Sync(ctx)) + fx.deletionStateMock.EXPECT().Filter([]string{"new"}).Return([]string{"new"}).Times(1) + fx.deletionStateMock.EXPECT().Filter([]string{"changed"}).Return([]string{"changed"}).Times(1) + fx.deletionStateMock.EXPECT().Filter(nil).Return(nil).Times(1) + fx.treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"changed"}, []string{"new"}).Return(nil) + require.NoError(t, fx.diffSyncer.Sync(ctx)) + }) + + t.Run("diff syncer sync, acl changed", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + mPeer := mockPeer{} + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + fx.peerManagerMock.EXPECT(). + GetResponsiblePeers(gomock.Any()). + Return([]peer.Peer{mPeer}, nil) + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). + Return([]string{"new"}, []string{"changed"}, nil, nil) + fx.deletionStateMock.EXPECT().Filter([]string{"new"}).Return([]string{"new"}).Times(1) + fx.deletionStateMock.EXPECT().Filter([]string{"changed"}).Return([]string{"changed", "aclId"}).Times(1) + fx.deletionStateMock.EXPECT().Filter(nil).Return(nil).Times(1) + fx.treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"changed"}, []string{"new"}).Return(nil) + fx.aclMock.EXPECT().SyncWithPeer(gomock.Any(), mPeer.Id()).Return(nil) + require.NoError(t, fx.diffSyncer.Sync(ctx)) }) t.Run("diff syncer sync conf error", func(t *testing.T) { - peerManagerMock.EXPECT(). + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + ctx := context.Background() + fx.peerManagerMock.EXPECT(). GetResponsiblePeers(gomock.Any()). Return(nil, fmt.Errorf("some error")) - require.Error(t, diffSyncer.Sync(ctx)) + require.Error(t, fx.diffSyncer.Sync(ctx)) }) t.Run("deletion state remove objects", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() deletedId := "id" - delState.EXPECT().Exists(deletedId).Return(true) + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + fx.deletionStateMock.EXPECT().Exists(deletedId).Return(true) // this should not result in any mock being called - diffSyncer.UpdateHeads(deletedId, []string{"someHead"}) + fx.diffSyncer.UpdateHeads(deletedId, []string{"someHead"}) }) t.Run("update heads updates diff", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() newId := "newId" newHeads := []string{"h1", "h2"} hash := "hash" - diffMock.EXPECT().Set(ldiff.Element{ + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + fx.diffMock.EXPECT().Set(ldiff.Element{ Id: newId, Head: concatStrings(newHeads), }) - diffMock.EXPECT().Hash().Return(hash) - delState.EXPECT().Exists(newId).Return(false) - stMock.EXPECT().WriteSpaceHash(hash) - diffSyncer.UpdateHeads(newId, newHeads) + fx.diffMock.EXPECT().Hash().Return(hash) + fx.deletionStateMock.EXPECT().Exists(newId).Return(false) + fx.storageMock.EXPECT().WriteSpaceHash(hash) + fx.diffSyncer.UpdateHeads(newId, newHeads) }) t.Run("diff syncer sync space missing", func(t *testing.T) { - aclStorageMock := mock_liststorage.NewMockListStorage(ctrl) - settingsStorage := mock_treestorage.NewMockTreeStorage(ctrl) + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + aclStorageMock := mock_liststorage.NewMockListStorage(fx.ctrl) + settingsStorage := mock_treestorage.NewMockTreeStorage(fx.ctrl) settingsId := "settingsId" - aclRoot := &aclrecordproto.RawAclRecordWithId{ + aclRootId := "aclRootId" + aclRoot := &consensusproto.RawRecordWithId{ Id: aclRootId, } settingsRoot := &treechangeproto.RawTreeChangeWithId{ @@ -189,55 +204,63 @@ func TestDiffSyncer_Sync(t *testing.T) { spaceSettingsId := "spaceSettingsId" credential := []byte("credential") - peerManagerMock.EXPECT(). + fx.peerManagerMock.EXPECT(). GetResponsiblePeers(gomock.Any()). Return([]peer.Peer{mockPeer{}}, nil) - diffMock.EXPECT(). - Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))). + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). Return(nil, nil, nil, spacesyncproto.ErrSpaceMissing) - stMock.EXPECT().AclStorage().Return(aclStorageMock, nil) - stMock.EXPECT().SpaceHeader().Return(spaceHeader, nil) - stMock.EXPECT().SpaceSettingsId().Return(spaceSettingsId) - stMock.EXPECT().TreeStorage(spaceSettingsId).Return(settingsStorage, nil) + fx.storageMock.EXPECT().AclStorage().Return(aclStorageMock, nil) + fx.storageMock.EXPECT().SpaceHeader().Return(spaceHeader, nil) + fx.storageMock.EXPECT().SpaceSettingsId().Return(spaceSettingsId) + fx.storageMock.EXPECT().TreeStorage(spaceSettingsId).Return(settingsStorage, nil) settingsStorage.EXPECT().Root().Return(settingsRoot, nil) aclStorageMock.EXPECT(). Root(). Return(aclRoot, nil) - credentialProvider.EXPECT(). + fx.credentialProviderMock.EXPECT(). GetCredential(gomock.Any(), spaceHeader). Return(credential, nil) - clientMock.EXPECT(). - SpacePush(gomock.Any(), newPushSpaceRequestMatcher(spaceId, aclRootId, settingsId, credential, spaceHeader)). + fx.clientMock.EXPECT(). + SpacePush(gomock.Any(), newPushSpaceRequestMatcher(fx.spaceState.SpaceId, aclRootId, settingsId, credential, spaceHeader)). Return(nil, nil) - peerManagerMock.EXPECT().SendPeer(gomock.Any(), "mockId", gomock.Any()) + fx.peerManagerMock.EXPECT().SendPeer(gomock.Any(), "peerId", gomock.Any()) - require.NoError(t, diffSyncer.Sync(ctx)) + require.NoError(t, fx.diffSyncer.Sync(ctx)) }) t.Run("diff syncer sync unexpected", func(t *testing.T) { - peerManagerMock.EXPECT(). + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + fx.peerManagerMock.EXPECT(). GetResponsiblePeers(gomock.Any()). Return([]peer.Peer{mockPeer{}}, nil) - diffMock.EXPECT(). - Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))). + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). Return(nil, nil, nil, spacesyncproto.ErrUnexpected) - require.NoError(t, diffSyncer.Sync(ctx)) + require.NoError(t, fx.diffSyncer.Sync(ctx)) }) t.Run("diff syncer sync space is deleted error", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.initDiffSyncer(t) + defer fx.stop() mPeer := mockPeer{} - peerManagerMock.EXPECT(). + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + fx.peerManagerMock.EXPECT(). GetResponsiblePeers(gomock.Any()). Return([]peer.Peer{mPeer}, nil) - diffMock.EXPECT(). - Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))). + fx.diffMock.EXPECT(). + Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))). Return(nil, nil, nil, spacesyncproto.ErrSpaceIsDeleted) - stMock.EXPECT().SpaceSettingsId().Return("settingsId") - treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"settingsId"}, nil).Return(nil) + fx.storageMock.EXPECT().SpaceSettingsId().Return("settingsId") + fx.treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"settingsId"}, nil).Return(nil) - require.NoError(t, diffSyncer.Sync(ctx)) + require.NoError(t, fx.diffSyncer.Sync(ctx)) }) } diff --git a/commonspace/headsync/headsync.go b/commonspace/headsync/headsync.go index ddca3e50..d7a1d74f 100644 --- a/commonspace/headsync/headsync.go +++ b/commonspace/headsync/headsync.go @@ -3,123 +3,150 @@ package headsync import ( "context" + "sync/atomic" + "time" + + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/ldiff" "github.com/anyproto/any-sync/app/logger" + config2 "github.com/anyproto/any-sync/commonspace/config" "github.com/anyproto/any-sync/commonspace/credentialprovider" + "github.com/anyproto/any-sync/commonspace/deletionstate" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl" "github.com/anyproto/any-sync/commonspace/object/treemanager" "github.com/anyproto/any-sync/commonspace/peermanager" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate" + "github.com/anyproto/any-sync/commonspace/spacestate" "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/nodeconf" "github.com/anyproto/any-sync/util/periodicsync" + "github.com/anyproto/any-sync/util/slice" "go.uber.org/zap" "golang.org/x/exp/slices" - "strings" - "sync/atomic" - "time" ) +var log = logger.NewNamed(CName) + +const CName = "common.commonspace.headsync" + type TreeHeads struct { Id string Heads []string } type HeadSync interface { - Init(objectIds []string, deletionState settingsstate.ObjectDeletionState) - + app.ComponentRunnable + ExternalIds() []string + DebugAllHeads() (res []TreeHeads) + AllIds() []string UpdateHeads(id string, heads []string) HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) RemoveObjects(ids []string) - AllIds() []string - DebugAllHeads() (res []TreeHeads) - - Close() (err error) } type headSync struct { spaceId string - periodicSync periodicsync.PeriodicSync - storage spacestorage.SpaceStorage - diff ldiff.Diff - log logger.CtxLogger - syncer DiffSyncer - configuration nodeconf.NodeConf spaceIsDeleted *atomic.Bool + syncPeriod int - syncPeriod int + periodicSync periodicsync.PeriodicSync + storage spacestorage.SpaceStorage + diff ldiff.Diff + log logger.CtxLogger + syncer DiffSyncer + configuration nodeconf.NodeConf + peerManager peermanager.PeerManager + treeManager treemanager.TreeManager + credentialProvider credentialprovider.CredentialProvider + syncStatus syncstatus.StatusService + deletionState deletionstate.ObjectDeletionState + syncAcl syncacl.SyncAcl } -func NewHeadSync( - spaceId string, - spaceIsDeleted *atomic.Bool, - syncPeriod int, - configuration nodeconf.NodeConf, - storage spacestorage.SpaceStorage, - peerManager peermanager.PeerManager, - cache treemanager.TreeManager, - syncStatus syncstatus.StatusUpdater, - credentialProvider credentialprovider.CredentialProvider, - log logger.CtxLogger) HeadSync { +func New() HeadSync { + return &headSync{} +} - diff := ldiff.New(16, 16) - l := log.With(zap.String("spaceId", spaceId)) - factory := spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceSyncClient) - syncer := newDiffSyncer(spaceId, diff, peerManager, cache, storage, factory, syncStatus, credentialProvider, l) +var createDiffSyncer = newDiffSyncer + +func (h *headSync) Init(a *app.App) (err error) { + shared := a.MustComponent(spacestate.CName).(*spacestate.SpaceState) + cfg := a.MustComponent("config").(config2.ConfigGetter) + h.syncAcl = a.MustComponent(syncacl.CName).(syncacl.SyncAcl) + h.spaceId = shared.SpaceId + h.spaceIsDeleted = shared.SpaceIsDeleted + h.syncPeriod = cfg.GetSpace().SyncPeriod + h.configuration = a.MustComponent(nodeconf.CName).(nodeconf.NodeConf) + h.log = log.With(zap.String("spaceId", h.spaceId)) + h.storage = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + h.diff = ldiff.New(16, 16) + h.peerManager = a.MustComponent(peermanager.CName).(peermanager.PeerManager) + h.credentialProvider = a.MustComponent(credentialprovider.CName).(credentialprovider.CredentialProvider) + h.syncStatus = a.MustComponent(syncstatus.CName).(syncstatus.StatusService) + h.treeManager = a.MustComponent(treemanager.CName).(treemanager.TreeManager) + h.deletionState = a.MustComponent(deletionstate.CName).(deletionstate.ObjectDeletionState) + h.syncer = createDiffSyncer(h) sync := func(ctx context.Context) (err error) { // for clients cancelling the sync process - if spaceIsDeleted.Load() && !configuration.IsResponsible(spaceId) { + if h.spaceIsDeleted.Load() && !h.configuration.IsResponsible(h.spaceId) { return spacesyncproto.ErrSpaceIsDeleted } - return syncer.Sync(ctx) - } - periodicSync := periodicsync.NewPeriodicSync(syncPeriod, time.Minute, sync, l) - - return &headSync{ - spaceId: spaceId, - storage: storage, - syncer: syncer, - periodicSync: periodicSync, - diff: diff, - log: log, - syncPeriod: syncPeriod, - configuration: configuration, - spaceIsDeleted: spaceIsDeleted, + return h.syncer.Sync(ctx) } + h.periodicSync = periodicsync.NewPeriodicSync(h.syncPeriod, time.Minute, sync, h.log) + h.syncAcl.SetHeadUpdater(h) + // TODO: move to run? + h.syncer.Init() + return nil } -func (d *headSync) Init(objectIds []string, deletionState settingsstate.ObjectDeletionState) { - d.fillDiff(objectIds) - d.syncer.Init(deletionState) - d.periodicSync.Run() +func (h *headSync) Name() (name string) { + return CName } -func (d *headSync) HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) { - if d.spaceIsDeleted.Load() { +func (h *headSync) Run(ctx context.Context) (err error) { + initialIds, err := h.storage.StoredIds() + if err != nil { + return + } + h.fillDiff(initialIds) + h.periodicSync.Run() + return +} + +func (h *headSync) HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) { + if h.spaceIsDeleted.Load() { peerId, err := peer.CtxPeerId(ctx) if err != nil { return nil, err } // stop receiving all request for sync from clients - if !slices.Contains(d.configuration.NodeIds(d.spaceId), peerId) { + if !slices.Contains(h.configuration.NodeIds(h.spaceId), peerId) { return nil, spacesyncproto.ErrSpaceIsDeleted } } - return HandleRangeRequest(ctx, d.diff, req) + return HandleRangeRequest(ctx, h.diff, req) } -func (d *headSync) UpdateHeads(id string, heads []string) { - d.syncer.UpdateHeads(id, heads) +func (h *headSync) UpdateHeads(id string, heads []string) { + h.syncer.UpdateHeads(id, heads) } -func (d *headSync) AllIds() []string { - return d.diff.Ids() +func (h *headSync) AllIds() []string { + return h.diff.Ids() } -func (d *headSync) DebugAllHeads() (res []TreeHeads) { - els := d.diff.Elements() +func (h *headSync) ExternalIds() []string { + settingsId := h.storage.SpaceSettingsId() + return slice.DiscardFromSlice(h.AllIds(), func(id string) bool { + return id == settingsId + }) +} + +func (h *headSync) DebugAllHeads() (res []TreeHeads) { + els := h.diff.Elements() for _, el := range els { idHead := TreeHeads{ Id: el.Id, @@ -130,19 +157,19 @@ func (d *headSync) DebugAllHeads() (res []TreeHeads) { return } -func (d *headSync) RemoveObjects(ids []string) { - d.syncer.RemoveObjects(ids) +func (h *headSync) RemoveObjects(ids []string) { + h.syncer.RemoveObjects(ids) } -func (d *headSync) Close() (err error) { - d.periodicSync.Close() - return d.syncer.Close() +func (h *headSync) Close(ctx context.Context) (err error) { + h.periodicSync.Close() + return h.syncer.Close() } -func (d *headSync) fillDiff(objectIds []string) { +func (h *headSync) fillDiff(objectIds []string) { var els = make([]ldiff.Element, 0, len(objectIds)) for _, id := range objectIds { - st, err := d.storage.TreeStorage(id) + st, err := h.storage.TreeStorage(id) if err != nil { continue } @@ -155,32 +182,12 @@ func (d *headSync) fillDiff(objectIds []string) { Head: concatStrings(heads), }) } - d.diff.Set(els...) - if err := d.storage.WriteSpaceHash(d.diff.Hash()); err != nil { - d.log.Error("can't write space hash", zap.Error(err)) + els = append(els, ldiff.Element{ + Id: h.syncAcl.Id(), + Head: h.syncAcl.Head().Id, + }) + h.diff.Set(els...) + if err := h.storage.WriteSpaceHash(h.diff.Hash()); err != nil { + h.log.Error("can't write space hash", zap.Error(err)) } } - -func concatStrings(strs []string) string { - var ( - b strings.Builder - totalLen int - ) - for _, s := range strs { - totalLen += len(s) - } - - b.Grow(totalLen) - for _, s := range strs { - b.WriteString(s) - } - return b.String() -} - -func splitString(str string) (res []string) { - const cidLen = 59 - for i := 0; i < len(str); i += cidLen { - res = append(res, str[i:i+cidLen]) - } - return -} diff --git a/commonspace/headsync/headsync_test.go b/commonspace/headsync/headsync_test.go index ae2c419a..1f37419a 100644 --- a/commonspace/headsync/headsync_test.go +++ b/commonspace/headsync/headsync_test.go @@ -1,71 +1,190 @@ package headsync import ( + "context" + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/ldiff" "github.com/anyproto/any-sync/app/ldiff/mock_ldiff" - "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/config" + "github.com/anyproto/any-sync/commonspace/credentialprovider" + "github.com/anyproto/any-sync/commonspace/credentialprovider/mock_credentialprovider" + "github.com/anyproto/any-sync/commonspace/deletionstate" + "github.com/anyproto/any-sync/commonspace/deletionstate/mock_deletionstate" "github.com/anyproto/any-sync/commonspace/headsync/mock_headsync" + "github.com/anyproto/any-sync/commonspace/object/acl/list" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl/mock_syncacl" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage/mock_treestorage" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate/mock_settingsstate" + "github.com/anyproto/any-sync/commonspace/object/treemanager" + "github.com/anyproto/any-sync/commonspace/object/treemanager/mock_treemanager" + "github.com/anyproto/any-sync/commonspace/peermanager" + "github.com/anyproto/any-sync/commonspace/peermanager/mock_peermanager" + "github.com/anyproto/any-sync/commonspace/spacestate" + "github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/spacestorage/mock_spacestorage" - "github.com/anyproto/any-sync/util/periodicsync/mock_periodicsync" - "github.com/golang/mock/gomock" + "github.com/anyproto/any-sync/commonspace/spacesyncproto/mock_spacesyncproto" + "github.com/anyproto/any-sync/commonspace/syncstatus" + "github.com/anyproto/any-sync/nodeconf" + "github.com/anyproto/any-sync/nodeconf/mock_nodeconf" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "sync/atomic" "testing" ) -func TestDiffService(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() +type mockConfig struct { +} - spaceId := "spaceId" - l := logger.NewNamed("sync") - pSyncMock := mock_periodicsync.NewMockPeriodicSync(ctrl) - storageMock := mock_spacestorage.NewMockSpaceStorage(ctrl) - treeStorageMock := mock_treestorage.NewMockTreeStorage(ctrl) - diffMock := mock_ldiff.NewMockDiff(ctrl) - syncer := mock_headsync.NewMockDiffSyncer(ctrl) - delState := mock_settingsstate.NewMockObjectDeletionState(ctrl) - syncPeriod := 1 - initId := "initId" +func (m mockConfig) Init(a *app.App) (err error) { + return nil +} - service := &headSync{ - spaceId: spaceId, - storage: storageMock, - periodicSync: pSyncMock, - syncer: syncer, - diff: diffMock, - log: l, - syncPeriod: syncPeriod, +func (m mockConfig) Name() (name string) { + return "config" +} + +func (m mockConfig) GetSpace() config.Config { + return config.Config{} +} + +type headSyncFixture struct { + spaceState *spacestate.SpaceState + ctrl *gomock.Controller + app *app.App + + configurationMock *mock_nodeconf.MockService + storageMock *mock_spacestorage.MockSpaceStorage + peerManagerMock *mock_peermanager.MockPeerManager + credentialProviderMock *mock_credentialprovider.MockCredentialProvider + syncStatus syncstatus.StatusService + treeManagerMock *mock_treemanager.MockTreeManager + deletionStateMock *mock_deletionstate.MockObjectDeletionState + diffSyncerMock *mock_headsync.MockDiffSyncer + treeSyncerMock *mock_treemanager.MockTreeSyncer + diffMock *mock_ldiff.MockDiff + clientMock *mock_spacesyncproto.MockDRPCSpaceSyncClient + aclMock *mock_syncacl.MockSyncAcl + headSync *headSync + diffSyncer *diffSyncer +} + +func newHeadSyncFixture(t *testing.T) *headSyncFixture { + spaceState := &spacestate.SpaceState{ + SpaceId: "spaceId", + SpaceIsDeleted: &atomic.Bool{}, } + ctrl := gomock.NewController(t) + configurationMock := mock_nodeconf.NewMockService(ctrl) + configurationMock.EXPECT().Name().AnyTimes().Return(nodeconf.CName) + storageMock := mock_spacestorage.NewMockSpaceStorage(ctrl) + storageMock.EXPECT().Name().AnyTimes().Return(spacestorage.CName) + peerManagerMock := mock_peermanager.NewMockPeerManager(ctrl) + peerManagerMock.EXPECT().Name().AnyTimes().Return(peermanager.CName) + credentialProviderMock := mock_credentialprovider.NewMockCredentialProvider(ctrl) + credentialProviderMock.EXPECT().Name().AnyTimes().Return(credentialprovider.CName) + syncStatus := syncstatus.NewNoOpSyncStatus() + treeManagerMock := mock_treemanager.NewMockTreeManager(ctrl) + treeManagerMock.EXPECT().Name().AnyTimes().Return(treemanager.CName) + deletionStateMock := mock_deletionstate.NewMockObjectDeletionState(ctrl) + deletionStateMock.EXPECT().Name().AnyTimes().Return(deletionstate.CName) + diffSyncerMock := mock_headsync.NewMockDiffSyncer(ctrl) + treeSyncerMock := mock_treemanager.NewMockTreeSyncer(ctrl) + diffMock := mock_ldiff.NewMockDiff(ctrl) + clientMock := mock_spacesyncproto.NewMockDRPCSpaceSyncClient(ctrl) + aclMock := mock_syncacl.NewMockSyncAcl(ctrl) + aclMock.EXPECT().Name().AnyTimes().Return(syncacl.CName) + aclMock.EXPECT().SetHeadUpdater(gomock.Any()).AnyTimes() + hs := &headSync{} + a := &app.App{} + a.Register(spaceState). + Register(aclMock). + Register(mockConfig{}). + Register(configurationMock). + Register(storageMock). + Register(peerManagerMock). + Register(credentialProviderMock). + Register(syncStatus). + Register(treeManagerMock). + Register(deletionStateMock). + Register(hs) + return &headSyncFixture{ + spaceState: spaceState, + ctrl: ctrl, + app: a, + configurationMock: configurationMock, + storageMock: storageMock, + peerManagerMock: peerManagerMock, + credentialProviderMock: credentialProviderMock, + syncStatus: syncStatus, + treeManagerMock: treeManagerMock, + deletionStateMock: deletionStateMock, + headSync: hs, + diffSyncerMock: diffSyncerMock, + treeSyncerMock: treeSyncerMock, + diffMock: diffMock, + clientMock: clientMock, + aclMock: aclMock, + } +} - t.Run("init", func(t *testing.T) { - storageMock.EXPECT().TreeStorage(initId).Return(treeStorageMock, nil) - treeStorageMock.EXPECT().Heads().Return([]string{"h1", "h2"}, nil) - syncer.EXPECT().Init(delState) - diffMock.EXPECT().Set(ldiff.Element{ - Id: initId, +func (fx *headSyncFixture) init(t *testing.T) { + createDiffSyncer = func(hs *headSync) DiffSyncer { + return fx.diffSyncerMock + } + fx.diffSyncerMock.EXPECT().Init() + err := fx.headSync.Init(fx.app) + require.NoError(t, err) + fx.headSync.diff = fx.diffMock +} + +func (fx *headSyncFixture) stop() { + fx.ctrl.Finish() +} + +func TestHeadSync(t *testing.T) { + ctx := context.Background() + + t.Run("run close", func(t *testing.T) { + fx := newHeadSyncFixture(t) + fx.init(t) + defer fx.stop() + + ids := []string{"id1"} + treeMock := mock_treestorage.NewMockTreeStorage(fx.ctrl) + fx.storageMock.EXPECT().StoredIds().Return(ids, nil) + fx.storageMock.EXPECT().TreeStorage(ids[0]).Return(treeMock, nil) + fx.aclMock.EXPECT().Id().AnyTimes().Return("aclId") + fx.aclMock.EXPECT().Head().AnyTimes().Return(&list.AclRecord{Id: "headId"}) + treeMock.EXPECT().Heads().Return([]string{"h1", "h2"}, nil) + fx.diffMock.EXPECT().Set(ldiff.Element{ + Id: "id1", Head: "h1h2", }) - hash := "123" - diffMock.EXPECT().Hash().Return(hash) - storageMock.EXPECT().WriteSpaceHash(hash) - pSyncMock.EXPECT().Run() - service.Init([]string{initId}, delState) + fx.diffMock.EXPECT().Hash().Return("hash") + fx.storageMock.EXPECT().WriteSpaceHash("hash").Return(nil) + fx.diffSyncerMock.EXPECT().Sync(gomock.Any()).Return(nil) + fx.diffSyncerMock.EXPECT().Close().Return(nil) + err := fx.headSync.Run(ctx) + require.NoError(t, err) + err = fx.headSync.Close(ctx) + require.NoError(t, err) }) t.Run("update heads", func(t *testing.T) { - syncer.EXPECT().UpdateHeads(initId, []string{"h1", "h2"}) - service.UpdateHeads(initId, []string{"h1", "h2"}) + fx := newHeadSyncFixture(t) + fx.init(t) + defer fx.stop() + + fx.diffSyncerMock.EXPECT().UpdateHeads("id1", []string{"h1"}) + fx.headSync.UpdateHeads("id1", []string{"h1"}) }) t.Run("remove objects", func(t *testing.T) { - syncer.EXPECT().RemoveObjects([]string{"h1", "h2"}) - service.RemoveObjects([]string{"h1", "h2"}) - }) + fx := newHeadSyncFixture(t) + fx.init(t) + defer fx.stop() - t.Run("close", func(t *testing.T) { - pSyncMock.EXPECT().Close() - syncer.EXPECT().Close() - service.Close() + fx.diffSyncerMock.EXPECT().RemoveObjects([]string{"id1"}) + fx.headSync.RemoveObjects([]string{"id1"}) }) } diff --git a/commonspace/headsync/mock_headsync/mock_headsync.go b/commonspace/headsync/mock_headsync/mock_headsync.go index 7df2fe64..b569fde4 100644 --- a/commonspace/headsync/mock_headsync/mock_headsync.go +++ b/commonspace/headsync/mock_headsync/mock_headsync.go @@ -8,8 +8,7 @@ import ( context "context" reflect "reflect" - settingsstate "github.com/anyproto/any-sync/commonspace/settings/settingsstate" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockDiffSyncer is a mock of DiffSyncer interface. @@ -50,15 +49,15 @@ func (mr *MockDiffSyncerMockRecorder) Close() *gomock.Call { } // Init mocks base method. -func (m *MockDiffSyncer) Init(arg0 settingsstate.ObjectDeletionState) { +func (m *MockDiffSyncer) Init() { m.ctrl.T.Helper() - m.ctrl.Call(m, "Init", arg0) + m.ctrl.Call(m, "Init") } // Init indicates an expected call of Init. -func (mr *MockDiffSyncerMockRecorder) Init(arg0 interface{}) *gomock.Call { +func (mr *MockDiffSyncerMockRecorder) Init() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockDiffSyncer)(nil).Init), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockDiffSyncer)(nil).Init)) } // RemoveObjects mocks base method. diff --git a/commonspace/headsync/util.go b/commonspace/headsync/util.go new file mode 100644 index 00000000..549a77f2 --- /dev/null +++ b/commonspace/headsync/util.go @@ -0,0 +1,27 @@ +package headsync + +import "strings" + +func concatStrings(strs []string) string { + var ( + b strings.Builder + totalLen int + ) + for _, s := range strs { + totalLen += len(s) + } + + b.Grow(totalLen) + for _, s := range strs { + b.WriteString(s) + } + return b.String() +} + +func splitString(str string) (res []string) { + const cidLen = 59 + for i := 0; i < len(str); i += cidLen { + res = append(res, str[i:i+cidLen]) + } + return +} diff --git a/commonspace/object/acl/aclrecordproto/aclrecord.pb.go b/commonspace/object/acl/aclrecordproto/aclrecord.pb.go index 6842e829..4c2d4333 100644 --- a/commonspace/object/acl/aclrecordproto/aclrecord.pb.go +++ b/commonspace/object/acl/aclrecordproto/aclrecord.pb.go @@ -22,24 +22,31 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// AclUserPermissions contains different possible user roles type AclUserPermissions int32 const ( - AclUserPermissions_Admin AclUserPermissions = 0 - AclUserPermissions_Writer AclUserPermissions = 1 - AclUserPermissions_Reader AclUserPermissions = 2 + AclUserPermissions_None AclUserPermissions = 0 + AclUserPermissions_Owner AclUserPermissions = 1 + AclUserPermissions_Admin AclUserPermissions = 2 + AclUserPermissions_Writer AclUserPermissions = 3 + AclUserPermissions_Reader AclUserPermissions = 4 ) var AclUserPermissions_name = map[int32]string{ - 0: "Admin", - 1: "Writer", - 2: "Reader", + 0: "None", + 1: "Owner", + 2: "Admin", + 3: "Writer", + 4: "Reader", } var AclUserPermissions_value = map[string]int32{ - "Admin": 0, - "Writer": 1, - "Reader": 2, + "None": 0, + "Owner": 1, + "Admin": 2, + "Writer": 3, + "Reader": 4, } func (x AclUserPermissions) String() string { @@ -50,202 +57,7 @@ func (AclUserPermissions) EnumDescriptor() ([]byte, []int) { return fileDescriptor_c8e9f754f34e929b, []int{0} } -type RawAclRecord struct { - Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` - Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` - AcceptorIdentity []byte `protobuf:"bytes,3,opt,name=acceptorIdentity,proto3" json:"acceptorIdentity,omitempty"` - AcceptorSignature []byte `protobuf:"bytes,4,opt,name=acceptorSignature,proto3" json:"acceptorSignature,omitempty"` -} - -func (m *RawAclRecord) Reset() { *m = RawAclRecord{} } -func (m *RawAclRecord) String() string { return proto.CompactTextString(m) } -func (*RawAclRecord) ProtoMessage() {} -func (*RawAclRecord) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{0} -} -func (m *RawAclRecord) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RawAclRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RawAclRecord.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 *RawAclRecord) XXX_Merge(src proto.Message) { - xxx_messageInfo_RawAclRecord.Merge(m, src) -} -func (m *RawAclRecord) XXX_Size() int { - return m.Size() -} -func (m *RawAclRecord) XXX_DiscardUnknown() { - xxx_messageInfo_RawAclRecord.DiscardUnknown(m) -} - -var xxx_messageInfo_RawAclRecord proto.InternalMessageInfo - -func (m *RawAclRecord) GetPayload() []byte { - if m != nil { - return m.Payload - } - return nil -} - -func (m *RawAclRecord) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *RawAclRecord) GetAcceptorIdentity() []byte { - if m != nil { - return m.AcceptorIdentity - } - return nil -} - -func (m *RawAclRecord) GetAcceptorSignature() []byte { - if m != nil { - return m.AcceptorSignature - } - return nil -} - -type RawAclRecordWithId struct { - Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` -} - -func (m *RawAclRecordWithId) Reset() { *m = RawAclRecordWithId{} } -func (m *RawAclRecordWithId) String() string { return proto.CompactTextString(m) } -func (*RawAclRecordWithId) ProtoMessage() {} -func (*RawAclRecordWithId) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{1} -} -func (m *RawAclRecordWithId) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RawAclRecordWithId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RawAclRecordWithId.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 *RawAclRecordWithId) XXX_Merge(src proto.Message) { - xxx_messageInfo_RawAclRecordWithId.Merge(m, src) -} -func (m *RawAclRecordWithId) XXX_Size() int { - return m.Size() -} -func (m *RawAclRecordWithId) XXX_DiscardUnknown() { - xxx_messageInfo_RawAclRecordWithId.DiscardUnknown(m) -} - -var xxx_messageInfo_RawAclRecordWithId proto.InternalMessageInfo - -func (m *RawAclRecordWithId) GetPayload() []byte { - if m != nil { - return m.Payload - } - return nil -} - -func (m *RawAclRecordWithId) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -type AclRecord struct { - PrevId string `protobuf:"bytes,1,opt,name=prevId,proto3" json:"prevId,omitempty"` - Identity []byte `protobuf:"bytes,2,opt,name=identity,proto3" json:"identity,omitempty"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - ReadKeyId string `protobuf:"bytes,4,opt,name=readKeyId,proto3" json:"readKeyId,omitempty"` - Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,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_c8e9f754f34e929b, []int{2} -} -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_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) GetPrevId() string { - if m != nil { - return m.PrevId - } - return "" -} - -func (m *AclRecord) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *AclRecord) GetData() []byte { - if m != nil { - return m.Data - } - return nil -} - -func (m *AclRecord) GetReadKeyId() string { - if m != nil { - return m.ReadKeyId - } - return "" -} - -func (m *AclRecord) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - +// AclRoot is a root of access control list type AclRoot struct { Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` MasterKey []byte `protobuf:"bytes,2,opt,name=masterKey,proto3" json:"masterKey,omitempty"` @@ -259,7 +71,7 @@ func (m *AclRoot) Reset() { *m = AclRoot{} } func (m *AclRoot) String() string { return proto.CompactTextString(m) } func (*AclRoot) ProtoMessage() {} func (*AclRoot) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{3} + return fileDescriptor_c8e9f754f34e929b, []int{0} } func (m *AclRoot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -330,14 +142,586 @@ func (m *AclRoot) GetIdentitySignature() []byte { return nil } +// AclAccountInvite contains the public invite key, the private part of which is sent to the user directly +type AclAccountInvite struct { + InviteKey []byte `protobuf:"bytes,1,opt,name=inviteKey,proto3" json:"inviteKey,omitempty"` +} + +func (m *AclAccountInvite) Reset() { *m = AclAccountInvite{} } +func (m *AclAccountInvite) String() string { return proto.CompactTextString(m) } +func (*AclAccountInvite) ProtoMessage() {} +func (*AclAccountInvite) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{1} +} +func (m *AclAccountInvite) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountInvite) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountInvite.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 *AclAccountInvite) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountInvite.Merge(m, src) +} +func (m *AclAccountInvite) XXX_Size() int { + return m.Size() +} +func (m *AclAccountInvite) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountInvite.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountInvite proto.InternalMessageInfo + +func (m *AclAccountInvite) GetInviteKey() []byte { + if m != nil { + return m.InviteKey + } + return nil +} + +// AclAccountRequestJoin contains the reference to the invite record and the data of the person who wants to join, confirmed by the private invite key +type AclAccountRequestJoin struct { + InviteIdentity []byte `protobuf:"bytes,1,opt,name=inviteIdentity,proto3" json:"inviteIdentity,omitempty"` + InviteRecordId string `protobuf:"bytes,2,opt,name=inviteRecordId,proto3" json:"inviteRecordId,omitempty"` + InviteIdentitySignature []byte `protobuf:"bytes,3,opt,name=inviteIdentitySignature,proto3" json:"inviteIdentitySignature,omitempty"` + Metadata []byte `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"` +} + +func (m *AclAccountRequestJoin) Reset() { *m = AclAccountRequestJoin{} } +func (m *AclAccountRequestJoin) String() string { return proto.CompactTextString(m) } +func (*AclAccountRequestJoin) ProtoMessage() {} +func (*AclAccountRequestJoin) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{2} +} +func (m *AclAccountRequestJoin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountRequestJoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountRequestJoin.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 *AclAccountRequestJoin) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountRequestJoin.Merge(m, src) +} +func (m *AclAccountRequestJoin) XXX_Size() int { + return m.Size() +} +func (m *AclAccountRequestJoin) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountRequestJoin.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountRequestJoin proto.InternalMessageInfo + +func (m *AclAccountRequestJoin) GetInviteIdentity() []byte { + if m != nil { + return m.InviteIdentity + } + return nil +} + +func (m *AclAccountRequestJoin) GetInviteRecordId() string { + if m != nil { + return m.InviteRecordId + } + return "" +} + +func (m *AclAccountRequestJoin) GetInviteIdentitySignature() []byte { + if m != nil { + return m.InviteIdentitySignature + } + return nil +} + +func (m *AclAccountRequestJoin) GetMetadata() []byte { + if m != nil { + return m.Metadata + } + return nil +} + +// AclAccountRequestAccept contains the reference to join record and all read keys, encrypted with the identity of the requestor +type AclAccountRequestAccept struct { + Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + RequestRecordId string `protobuf:"bytes,2,opt,name=requestRecordId,proto3" json:"requestRecordId,omitempty"` + EncryptedReadKeys []*AclReadKeyWithRecord `protobuf:"bytes,3,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` + Permissions AclUserPermissions `protobuf:"varint,4,opt,name=permissions,proto3,enum=aclrecord.AclUserPermissions" json:"permissions,omitempty"` +} + +func (m *AclAccountRequestAccept) Reset() { *m = AclAccountRequestAccept{} } +func (m *AclAccountRequestAccept) String() string { return proto.CompactTextString(m) } +func (*AclAccountRequestAccept) ProtoMessage() {} +func (*AclAccountRequestAccept) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{3} +} +func (m *AclAccountRequestAccept) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountRequestAccept) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountRequestAccept.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 *AclAccountRequestAccept) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountRequestAccept.Merge(m, src) +} +func (m *AclAccountRequestAccept) XXX_Size() int { + return m.Size() +} +func (m *AclAccountRequestAccept) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountRequestAccept.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountRequestAccept proto.InternalMessageInfo + +func (m *AclAccountRequestAccept) GetIdentity() []byte { + if m != nil { + return m.Identity + } + return nil +} + +func (m *AclAccountRequestAccept) GetRequestRecordId() string { + if m != nil { + return m.RequestRecordId + } + return "" +} + +func (m *AclAccountRequestAccept) GetEncryptedReadKeys() []*AclReadKeyWithRecord { + if m != nil { + return m.EncryptedReadKeys + } + return nil +} + +func (m *AclAccountRequestAccept) GetPermissions() AclUserPermissions { + if m != nil { + return m.Permissions + } + return AclUserPermissions_None +} + +// AclAccountRequestDecline contains the reference to join record +type AclAccountRequestDecline struct { + RequestRecordId string `protobuf:"bytes,1,opt,name=requestRecordId,proto3" json:"requestRecordId,omitempty"` +} + +func (m *AclAccountRequestDecline) Reset() { *m = AclAccountRequestDecline{} } +func (m *AclAccountRequestDecline) String() string { return proto.CompactTextString(m) } +func (*AclAccountRequestDecline) ProtoMessage() {} +func (*AclAccountRequestDecline) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{4} +} +func (m *AclAccountRequestDecline) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountRequestDecline) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountRequestDecline.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 *AclAccountRequestDecline) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountRequestDecline.Merge(m, src) +} +func (m *AclAccountRequestDecline) XXX_Size() int { + return m.Size() +} +func (m *AclAccountRequestDecline) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountRequestDecline.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountRequestDecline proto.InternalMessageInfo + +func (m *AclAccountRequestDecline) GetRequestRecordId() string { + if m != nil { + return m.RequestRecordId + } + return "" +} + +// AclAccountInviteRevoke revokes the invite record +type AclAccountInviteRevoke struct { + InviteRecordId string `protobuf:"bytes,1,opt,name=inviteRecordId,proto3" json:"inviteRecordId,omitempty"` +} + +func (m *AclAccountInviteRevoke) Reset() { *m = AclAccountInviteRevoke{} } +func (m *AclAccountInviteRevoke) String() string { return proto.CompactTextString(m) } +func (*AclAccountInviteRevoke) ProtoMessage() {} +func (*AclAccountInviteRevoke) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{5} +} +func (m *AclAccountInviteRevoke) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountInviteRevoke) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountInviteRevoke.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 *AclAccountInviteRevoke) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountInviteRevoke.Merge(m, src) +} +func (m *AclAccountInviteRevoke) XXX_Size() int { + return m.Size() +} +func (m *AclAccountInviteRevoke) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountInviteRevoke.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountInviteRevoke proto.InternalMessageInfo + +func (m *AclAccountInviteRevoke) GetInviteRecordId() string { + if m != nil { + return m.InviteRecordId + } + return "" +} + +// AclReadKeys are a read key with record id +type AclReadKeyWithRecord struct { + RecordId string `protobuf:"bytes,1,opt,name=recordId,proto3" json:"recordId,omitempty"` + EncryptedReadKey []byte `protobuf:"bytes,2,opt,name=encryptedReadKey,proto3" json:"encryptedReadKey,omitempty"` +} + +func (m *AclReadKeyWithRecord) Reset() { *m = AclReadKeyWithRecord{} } +func (m *AclReadKeyWithRecord) String() string { return proto.CompactTextString(m) } +func (*AclReadKeyWithRecord) ProtoMessage() {} +func (*AclReadKeyWithRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{6} +} +func (m *AclReadKeyWithRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclReadKeyWithRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclReadKeyWithRecord.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 *AclReadKeyWithRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclReadKeyWithRecord.Merge(m, src) +} +func (m *AclReadKeyWithRecord) XXX_Size() int { + return m.Size() +} +func (m *AclReadKeyWithRecord) XXX_DiscardUnknown() { + xxx_messageInfo_AclReadKeyWithRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_AclReadKeyWithRecord proto.InternalMessageInfo + +func (m *AclReadKeyWithRecord) GetRecordId() string { + if m != nil { + return m.RecordId + } + return "" +} + +func (m *AclReadKeyWithRecord) GetEncryptedReadKey() []byte { + if m != nil { + return m.EncryptedReadKey + } + return nil +} + +// AclEncryptedReadKeys are new key for specific identity +type AclEncryptedReadKey struct { + Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + EncryptedReadKey []byte `protobuf:"bytes,2,opt,name=encryptedReadKey,proto3" json:"encryptedReadKey,omitempty"` +} + +func (m *AclEncryptedReadKey) Reset() { *m = AclEncryptedReadKey{} } +func (m *AclEncryptedReadKey) String() string { return proto.CompactTextString(m) } +func (*AclEncryptedReadKey) ProtoMessage() {} +func (*AclEncryptedReadKey) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{7} +} +func (m *AclEncryptedReadKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclEncryptedReadKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclEncryptedReadKey.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 *AclEncryptedReadKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclEncryptedReadKey.Merge(m, src) +} +func (m *AclEncryptedReadKey) XXX_Size() int { + return m.Size() +} +func (m *AclEncryptedReadKey) XXX_DiscardUnknown() { + xxx_messageInfo_AclEncryptedReadKey.DiscardUnknown(m) +} + +var xxx_messageInfo_AclEncryptedReadKey proto.InternalMessageInfo + +func (m *AclEncryptedReadKey) GetIdentity() []byte { + if m != nil { + return m.Identity + } + return nil +} + +func (m *AclEncryptedReadKey) GetEncryptedReadKey() []byte { + if m != nil { + return m.EncryptedReadKey + } + return nil +} + +// AclAccountPermissionChange changes permissions of specific account +type AclAccountPermissionChange struct { + Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` + Permissions AclUserPermissions `protobuf:"varint,2,opt,name=permissions,proto3,enum=aclrecord.AclUserPermissions" json:"permissions,omitempty"` +} + +func (m *AclAccountPermissionChange) Reset() { *m = AclAccountPermissionChange{} } +func (m *AclAccountPermissionChange) String() string { return proto.CompactTextString(m) } +func (*AclAccountPermissionChange) ProtoMessage() {} +func (*AclAccountPermissionChange) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{8} +} +func (m *AclAccountPermissionChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountPermissionChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountPermissionChange.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 *AclAccountPermissionChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountPermissionChange.Merge(m, src) +} +func (m *AclAccountPermissionChange) XXX_Size() int { + return m.Size() +} +func (m *AclAccountPermissionChange) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountPermissionChange.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountPermissionChange proto.InternalMessageInfo + +func (m *AclAccountPermissionChange) GetIdentity() []byte { + if m != nil { + return m.Identity + } + return nil +} + +func (m *AclAccountPermissionChange) GetPermissions() AclUserPermissions { + if m != nil { + return m.Permissions + } + return AclUserPermissions_None +} + +// AclReadKeyChange changes the key for a space +type AclReadKeyChange struct { + AccountKeys []*AclEncryptedReadKey `protobuf:"bytes,1,rep,name=accountKeys,proto3" json:"accountKeys,omitempty"` +} + +func (m *AclReadKeyChange) Reset() { *m = AclReadKeyChange{} } +func (m *AclReadKeyChange) String() string { return proto.CompactTextString(m) } +func (*AclReadKeyChange) ProtoMessage() {} +func (*AclReadKeyChange) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{9} +} +func (m *AclReadKeyChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclReadKeyChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclReadKeyChange.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 *AclReadKeyChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclReadKeyChange.Merge(m, src) +} +func (m *AclReadKeyChange) XXX_Size() int { + return m.Size() +} +func (m *AclReadKeyChange) XXX_DiscardUnknown() { + xxx_messageInfo_AclReadKeyChange.DiscardUnknown(m) +} + +var xxx_messageInfo_AclReadKeyChange proto.InternalMessageInfo + +func (m *AclReadKeyChange) GetAccountKeys() []*AclEncryptedReadKey { + if m != nil { + return m.AccountKeys + } + return nil +} + +// AclAccountRemove removes an account and changes read key for space +type AclAccountRemove struct { + Identities [][]byte `protobuf:"bytes,1,rep,name=identities,proto3" json:"identities,omitempty"` + AccountKeys []*AclEncryptedReadKey `protobuf:"bytes,2,rep,name=accountKeys,proto3" json:"accountKeys,omitempty"` +} + +func (m *AclAccountRemove) Reset() { *m = AclAccountRemove{} } +func (m *AclAccountRemove) String() string { return proto.CompactTextString(m) } +func (*AclAccountRemove) ProtoMessage() {} +func (*AclAccountRemove) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{10} +} +func (m *AclAccountRemove) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountRemove) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountRemove.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 *AclAccountRemove) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountRemove.Merge(m, src) +} +func (m *AclAccountRemove) XXX_Size() int { + return m.Size() +} +func (m *AclAccountRemove) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountRemove.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountRemove proto.InternalMessageInfo + +func (m *AclAccountRemove) GetIdentities() [][]byte { + if m != nil { + return m.Identities + } + return nil +} + +func (m *AclAccountRemove) GetAccountKeys() []*AclEncryptedReadKey { + if m != nil { + return m.AccountKeys + } + return nil +} + +// AclAccountRequestRemove adds a request to remove an account +type AclAccountRequestRemove struct { +} + +func (m *AclAccountRequestRemove) Reset() { *m = AclAccountRequestRemove{} } +func (m *AclAccountRequestRemove) String() string { return proto.CompactTextString(m) } +func (*AclAccountRequestRemove) ProtoMessage() {} +func (*AclAccountRequestRemove) Descriptor() ([]byte, []int) { + return fileDescriptor_c8e9f754f34e929b, []int{11} +} +func (m *AclAccountRequestRemove) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AclAccountRequestRemove) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AclAccountRequestRemove.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 *AclAccountRequestRemove) XXX_Merge(src proto.Message) { + xxx_messageInfo_AclAccountRequestRemove.Merge(m, src) +} +func (m *AclAccountRequestRemove) XXX_Size() int { + return m.Size() +} +func (m *AclAccountRequestRemove) XXX_DiscardUnknown() { + xxx_messageInfo_AclAccountRequestRemove.DiscardUnknown(m) +} + +var xxx_messageInfo_AclAccountRequestRemove proto.InternalMessageInfo + +// AclContentValue contains possible values for Acl type AclContentValue struct { // Types that are valid to be assigned to Value: // - // *AclContentValue_UserAdd - // *AclContentValue_UserRemove - // *AclContentValue_UserPermissionChange - // *AclContentValue_UserInvite - // *AclContentValue_UserJoin + // *AclContentValue_Invite + // *AclContentValue_InviteRevoke + // *AclContentValue_RequestJoin + // *AclContentValue_RequestAccept + // *AclContentValue_PermissionChange + // *AclContentValue_AccountRemove + // *AclContentValue_ReadKeyChange + // *AclContentValue_RequestDecline + // *AclContentValue_AccountRequestRemove Value isAclContentValue_Value `protobuf_oneof:"value"` } @@ -345,7 +729,7 @@ func (m *AclContentValue) Reset() { *m = AclContentValue{} } func (m *AclContentValue) String() string { return proto.CompactTextString(m) } func (*AclContentValue) ProtoMessage() {} func (*AclContentValue) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{4} + return fileDescriptor_c8e9f754f34e929b, []int{12} } func (m *AclContentValue) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -380,27 +764,43 @@ type isAclContentValue_Value interface { Size() int } -type AclContentValue_UserAdd struct { - UserAdd *AclUserAdd `protobuf:"bytes,1,opt,name=userAdd,proto3,oneof" json:"userAdd,omitempty"` +type AclContentValue_Invite struct { + Invite *AclAccountInvite `protobuf:"bytes,1,opt,name=invite,proto3,oneof" json:"invite,omitempty"` } -type AclContentValue_UserRemove struct { - UserRemove *AclUserRemove `protobuf:"bytes,2,opt,name=userRemove,proto3,oneof" json:"userRemove,omitempty"` +type AclContentValue_InviteRevoke struct { + InviteRevoke *AclAccountInviteRevoke `protobuf:"bytes,2,opt,name=inviteRevoke,proto3,oneof" json:"inviteRevoke,omitempty"` } -type AclContentValue_UserPermissionChange struct { - UserPermissionChange *AclUserPermissionChange `protobuf:"bytes,3,opt,name=userPermissionChange,proto3,oneof" json:"userPermissionChange,omitempty"` +type AclContentValue_RequestJoin struct { + RequestJoin *AclAccountRequestJoin `protobuf:"bytes,3,opt,name=requestJoin,proto3,oneof" json:"requestJoin,omitempty"` } -type AclContentValue_UserInvite struct { - UserInvite *AclUserInvite `protobuf:"bytes,4,opt,name=userInvite,proto3,oneof" json:"userInvite,omitempty"` +type AclContentValue_RequestAccept struct { + RequestAccept *AclAccountRequestAccept `protobuf:"bytes,4,opt,name=requestAccept,proto3,oneof" json:"requestAccept,omitempty"` } -type AclContentValue_UserJoin struct { - UserJoin *AclUserJoin `protobuf:"bytes,5,opt,name=userJoin,proto3,oneof" json:"userJoin,omitempty"` +type AclContentValue_PermissionChange struct { + PermissionChange *AclAccountPermissionChange `protobuf:"bytes,5,opt,name=permissionChange,proto3,oneof" json:"permissionChange,omitempty"` +} +type AclContentValue_AccountRemove struct { + AccountRemove *AclAccountRemove `protobuf:"bytes,6,opt,name=accountRemove,proto3,oneof" json:"accountRemove,omitempty"` +} +type AclContentValue_ReadKeyChange struct { + ReadKeyChange *AclReadKeyChange `protobuf:"bytes,7,opt,name=readKeyChange,proto3,oneof" json:"readKeyChange,omitempty"` +} +type AclContentValue_RequestDecline struct { + RequestDecline *AclAccountRequestDecline `protobuf:"bytes,8,opt,name=requestDecline,proto3,oneof" json:"requestDecline,omitempty"` +} +type AclContentValue_AccountRequestRemove struct { + AccountRequestRemove *AclAccountRequestRemove `protobuf:"bytes,9,opt,name=accountRequestRemove,proto3,oneof" json:"accountRequestRemove,omitempty"` } -func (*AclContentValue_UserAdd) isAclContentValue_Value() {} -func (*AclContentValue_UserRemove) isAclContentValue_Value() {} -func (*AclContentValue_UserPermissionChange) isAclContentValue_Value() {} -func (*AclContentValue_UserInvite) isAclContentValue_Value() {} -func (*AclContentValue_UserJoin) isAclContentValue_Value() {} +func (*AclContentValue_Invite) isAclContentValue_Value() {} +func (*AclContentValue_InviteRevoke) isAclContentValue_Value() {} +func (*AclContentValue_RequestJoin) isAclContentValue_Value() {} +func (*AclContentValue_RequestAccept) isAclContentValue_Value() {} +func (*AclContentValue_PermissionChange) isAclContentValue_Value() {} +func (*AclContentValue_AccountRemove) isAclContentValue_Value() {} +func (*AclContentValue_ReadKeyChange) isAclContentValue_Value() {} +func (*AclContentValue_RequestDecline) isAclContentValue_Value() {} +func (*AclContentValue_AccountRequestRemove) isAclContentValue_Value() {} func (m *AclContentValue) GetValue() isAclContentValue_Value { if m != nil { @@ -409,37 +809,65 @@ func (m *AclContentValue) GetValue() isAclContentValue_Value { return nil } -func (m *AclContentValue) GetUserAdd() *AclUserAdd { - if x, ok := m.GetValue().(*AclContentValue_UserAdd); ok { - return x.UserAdd +func (m *AclContentValue) GetInvite() *AclAccountInvite { + if x, ok := m.GetValue().(*AclContentValue_Invite); ok { + return x.Invite } return nil } -func (m *AclContentValue) GetUserRemove() *AclUserRemove { - if x, ok := m.GetValue().(*AclContentValue_UserRemove); ok { - return x.UserRemove +func (m *AclContentValue) GetInviteRevoke() *AclAccountInviteRevoke { + if x, ok := m.GetValue().(*AclContentValue_InviteRevoke); ok { + return x.InviteRevoke } return nil } -func (m *AclContentValue) GetUserPermissionChange() *AclUserPermissionChange { - if x, ok := m.GetValue().(*AclContentValue_UserPermissionChange); ok { - return x.UserPermissionChange +func (m *AclContentValue) GetRequestJoin() *AclAccountRequestJoin { + if x, ok := m.GetValue().(*AclContentValue_RequestJoin); ok { + return x.RequestJoin } return nil } -func (m *AclContentValue) GetUserInvite() *AclUserInvite { - if x, ok := m.GetValue().(*AclContentValue_UserInvite); ok { - return x.UserInvite +func (m *AclContentValue) GetRequestAccept() *AclAccountRequestAccept { + if x, ok := m.GetValue().(*AclContentValue_RequestAccept); ok { + return x.RequestAccept } return nil } -func (m *AclContentValue) GetUserJoin() *AclUserJoin { - if x, ok := m.GetValue().(*AclContentValue_UserJoin); ok { - return x.UserJoin +func (m *AclContentValue) GetPermissionChange() *AclAccountPermissionChange { + if x, ok := m.GetValue().(*AclContentValue_PermissionChange); ok { + return x.PermissionChange + } + return nil +} + +func (m *AclContentValue) GetAccountRemove() *AclAccountRemove { + if x, ok := m.GetValue().(*AclContentValue_AccountRemove); ok { + return x.AccountRemove + } + return nil +} + +func (m *AclContentValue) GetReadKeyChange() *AclReadKeyChange { + if x, ok := m.GetValue().(*AclContentValue_ReadKeyChange); ok { + return x.ReadKeyChange + } + return nil +} + +func (m *AclContentValue) GetRequestDecline() *AclAccountRequestDecline { + if x, ok := m.GetValue().(*AclContentValue_RequestDecline); ok { + return x.RequestDecline + } + return nil +} + +func (m *AclContentValue) GetAccountRequestRemove() *AclAccountRequestRemove { + if x, ok := m.GetValue().(*AclContentValue_AccountRequestRemove); ok { + return x.AccountRequestRemove } return nil } @@ -447,14 +875,19 @@ func (m *AclContentValue) GetUserJoin() *AclUserJoin { // XXX_OneofWrappers is for the internal use of the proto package. func (*AclContentValue) XXX_OneofWrappers() []interface{} { return []interface{}{ - (*AclContentValue_UserAdd)(nil), - (*AclContentValue_UserRemove)(nil), - (*AclContentValue_UserPermissionChange)(nil), - (*AclContentValue_UserInvite)(nil), - (*AclContentValue_UserJoin)(nil), + (*AclContentValue_Invite)(nil), + (*AclContentValue_InviteRevoke)(nil), + (*AclContentValue_RequestJoin)(nil), + (*AclContentValue_RequestAccept)(nil), + (*AclContentValue_PermissionChange)(nil), + (*AclContentValue_AccountRemove)(nil), + (*AclContentValue_ReadKeyChange)(nil), + (*AclContentValue_RequestDecline)(nil), + (*AclContentValue_AccountRequestRemove)(nil), } } +// AclData contains different acl content type AclData struct { AclContent []*AclContentValue `protobuf:"bytes,1,rep,name=aclContent,proto3" json:"aclContent,omitempty"` } @@ -463,7 +896,7 @@ func (m *AclData) Reset() { *m = AclData{} } func (m *AclData) String() string { return proto.CompactTextString(m) } func (*AclData) ProtoMessage() {} func (*AclData) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{5} + return fileDescriptor_c8e9f754f34e929b, []int{13} } func (m *AclData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -499,644 +932,22 @@ func (m *AclData) GetAclContent() []*AclContentValue { return nil } -type AclState struct { - ReadKeyIds []string `protobuf:"bytes,1,rep,name=readKeyIds,proto3" json:"readKeyIds,omitempty"` - UserStates []*AclUserState `protobuf:"bytes,2,rep,name=userStates,proto3" json:"userStates,omitempty"` - Invites map[string]*AclUserInvite `protobuf:"bytes,3,rep,name=invites,proto3" json:"invites,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (m *AclState) Reset() { *m = AclState{} } -func (m *AclState) String() string { return proto.CompactTextString(m) } -func (*AclState) ProtoMessage() {} -func (*AclState) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{6} -} -func (m *AclState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclState.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 *AclState) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclState.Merge(m, src) -} -func (m *AclState) XXX_Size() int { - return m.Size() -} -func (m *AclState) XXX_DiscardUnknown() { - xxx_messageInfo_AclState.DiscardUnknown(m) -} - -var xxx_messageInfo_AclState proto.InternalMessageInfo - -func (m *AclState) GetReadKeyIds() []string { - if m != nil { - return m.ReadKeyIds - } - return nil -} - -func (m *AclState) GetUserStates() []*AclUserState { - if m != nil { - return m.UserStates - } - return nil -} - -func (m *AclState) GetInvites() map[string]*AclUserInvite { - if m != nil { - return m.Invites - } - return nil -} - -type AclUserState struct { - Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - Permissions AclUserPermissions `protobuf:"varint,2,opt,name=permissions,proto3,enum=aclrecord.AclUserPermissions" json:"permissions,omitempty"` -} - -func (m *AclUserState) Reset() { *m = AclUserState{} } -func (m *AclUserState) String() string { return proto.CompactTextString(m) } -func (*AclUserState) ProtoMessage() {} -func (*AclUserState) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{7} -} -func (m *AclUserState) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclUserState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclUserState.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 *AclUserState) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclUserState.Merge(m, src) -} -func (m *AclUserState) XXX_Size() int { - return m.Size() -} -func (m *AclUserState) XXX_DiscardUnknown() { - xxx_messageInfo_AclUserState.DiscardUnknown(m) -} - -var xxx_messageInfo_AclUserState proto.InternalMessageInfo - -func (m *AclUserState) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *AclUserState) GetPermissions() AclUserPermissions { - if m != nil { - return m.Permissions - } - return AclUserPermissions_Admin -} - -type AclUserAdd struct { - Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - EncryptedReadKeys [][]byte `protobuf:"bytes,2,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` - Permissions AclUserPermissions `protobuf:"varint,3,opt,name=permissions,proto3,enum=aclrecord.AclUserPermissions" json:"permissions,omitempty"` -} - -func (m *AclUserAdd) Reset() { *m = AclUserAdd{} } -func (m *AclUserAdd) String() string { return proto.CompactTextString(m) } -func (*AclUserAdd) ProtoMessage() {} -func (*AclUserAdd) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{8} -} -func (m *AclUserAdd) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclUserAdd) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclUserAdd.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 *AclUserAdd) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclUserAdd.Merge(m, src) -} -func (m *AclUserAdd) XXX_Size() int { - return m.Size() -} -func (m *AclUserAdd) XXX_DiscardUnknown() { - xxx_messageInfo_AclUserAdd.DiscardUnknown(m) -} - -var xxx_messageInfo_AclUserAdd proto.InternalMessageInfo - -func (m *AclUserAdd) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *AclUserAdd) GetEncryptedReadKeys() [][]byte { - if m != nil { - return m.EncryptedReadKeys - } - return nil -} - -func (m *AclUserAdd) GetPermissions() AclUserPermissions { - if m != nil { - return m.Permissions - } - return AclUserPermissions_Admin -} - -type AclUserInvite struct { - AcceptPublicKey []byte `protobuf:"bytes,1,opt,name=acceptPublicKey,proto3" json:"acceptPublicKey,omitempty"` - EncryptedReadKeys [][]byte `protobuf:"bytes,2,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` - Permissions AclUserPermissions `protobuf:"varint,3,opt,name=permissions,proto3,enum=aclrecord.AclUserPermissions" json:"permissions,omitempty"` -} - -func (m *AclUserInvite) Reset() { *m = AclUserInvite{} } -func (m *AclUserInvite) String() string { return proto.CompactTextString(m) } -func (*AclUserInvite) ProtoMessage() {} -func (*AclUserInvite) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{9} -} -func (m *AclUserInvite) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclUserInvite) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclUserInvite.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 *AclUserInvite) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclUserInvite.Merge(m, src) -} -func (m *AclUserInvite) XXX_Size() int { - return m.Size() -} -func (m *AclUserInvite) XXX_DiscardUnknown() { - xxx_messageInfo_AclUserInvite.DiscardUnknown(m) -} - -var xxx_messageInfo_AclUserInvite proto.InternalMessageInfo - -func (m *AclUserInvite) GetAcceptPublicKey() []byte { - if m != nil { - return m.AcceptPublicKey - } - return nil -} - -func (m *AclUserInvite) GetEncryptedReadKeys() [][]byte { - if m != nil { - return m.EncryptedReadKeys - } - return nil -} - -func (m *AclUserInvite) GetPermissions() AclUserPermissions { - if m != nil { - return m.Permissions - } - return AclUserPermissions_Admin -} - -type AclUserJoin struct { - Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - AcceptSignature []byte `protobuf:"bytes,2,opt,name=acceptSignature,proto3" json:"acceptSignature,omitempty"` - AcceptPubKey []byte `protobuf:"bytes,3,opt,name=acceptPubKey,proto3" json:"acceptPubKey,omitempty"` - EncryptedReadKeys [][]byte `protobuf:"bytes,4,rep,name=encryptedReadKeys,proto3" json:"encryptedReadKeys,omitempty"` -} - -func (m *AclUserJoin) Reset() { *m = AclUserJoin{} } -func (m *AclUserJoin) String() string { return proto.CompactTextString(m) } -func (*AclUserJoin) ProtoMessage() {} -func (*AclUserJoin) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{10} -} -func (m *AclUserJoin) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclUserJoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclUserJoin.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 *AclUserJoin) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclUserJoin.Merge(m, src) -} -func (m *AclUserJoin) XXX_Size() int { - return m.Size() -} -func (m *AclUserJoin) XXX_DiscardUnknown() { - xxx_messageInfo_AclUserJoin.DiscardUnknown(m) -} - -var xxx_messageInfo_AclUserJoin proto.InternalMessageInfo - -func (m *AclUserJoin) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *AclUserJoin) GetAcceptSignature() []byte { - if m != nil { - return m.AcceptSignature - } - return nil -} - -func (m *AclUserJoin) GetAcceptPubKey() []byte { - if m != nil { - return m.AcceptPubKey - } - return nil -} - -func (m *AclUserJoin) GetEncryptedReadKeys() [][]byte { - if m != nil { - return m.EncryptedReadKeys - } - return nil -} - -type AclUserRemove struct { - Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - ReadKeyReplaces []*AclReadKeyReplace `protobuf:"bytes,2,rep,name=readKeyReplaces,proto3" json:"readKeyReplaces,omitempty"` -} - -func (m *AclUserRemove) Reset() { *m = AclUserRemove{} } -func (m *AclUserRemove) String() string { return proto.CompactTextString(m) } -func (*AclUserRemove) ProtoMessage() {} -func (*AclUserRemove) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{11} -} -func (m *AclUserRemove) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclUserRemove) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclUserRemove.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 *AclUserRemove) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclUserRemove.Merge(m, src) -} -func (m *AclUserRemove) XXX_Size() int { - return m.Size() -} -func (m *AclUserRemove) XXX_DiscardUnknown() { - xxx_messageInfo_AclUserRemove.DiscardUnknown(m) -} - -var xxx_messageInfo_AclUserRemove proto.InternalMessageInfo - -func (m *AclUserRemove) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *AclUserRemove) GetReadKeyReplaces() []*AclReadKeyReplace { - if m != nil { - return m.ReadKeyReplaces - } - return nil -} - -type AclReadKeyReplace struct { - Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - EncryptedReadKey []byte `protobuf:"bytes,2,opt,name=encryptedReadKey,proto3" json:"encryptedReadKey,omitempty"` -} - -func (m *AclReadKeyReplace) Reset() { *m = AclReadKeyReplace{} } -func (m *AclReadKeyReplace) String() string { return proto.CompactTextString(m) } -func (*AclReadKeyReplace) ProtoMessage() {} -func (*AclReadKeyReplace) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{12} -} -func (m *AclReadKeyReplace) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclReadKeyReplace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclReadKeyReplace.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 *AclReadKeyReplace) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclReadKeyReplace.Merge(m, src) -} -func (m *AclReadKeyReplace) XXX_Size() int { - return m.Size() -} -func (m *AclReadKeyReplace) XXX_DiscardUnknown() { - xxx_messageInfo_AclReadKeyReplace.DiscardUnknown(m) -} - -var xxx_messageInfo_AclReadKeyReplace proto.InternalMessageInfo - -func (m *AclReadKeyReplace) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *AclReadKeyReplace) GetEncryptedReadKey() []byte { - if m != nil { - return m.EncryptedReadKey - } - return nil -} - -type AclUserPermissionChange struct { - Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - Permissions AclUserPermissions `protobuf:"varint,2,opt,name=permissions,proto3,enum=aclrecord.AclUserPermissions" json:"permissions,omitempty"` -} - -func (m *AclUserPermissionChange) Reset() { *m = AclUserPermissionChange{} } -func (m *AclUserPermissionChange) String() string { return proto.CompactTextString(m) } -func (*AclUserPermissionChange) ProtoMessage() {} -func (*AclUserPermissionChange) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{13} -} -func (m *AclUserPermissionChange) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclUserPermissionChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclUserPermissionChange.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 *AclUserPermissionChange) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclUserPermissionChange.Merge(m, src) -} -func (m *AclUserPermissionChange) XXX_Size() int { - return m.Size() -} -func (m *AclUserPermissionChange) XXX_DiscardUnknown() { - xxx_messageInfo_AclUserPermissionChange.DiscardUnknown(m) -} - -var xxx_messageInfo_AclUserPermissionChange proto.InternalMessageInfo - -func (m *AclUserPermissionChange) GetIdentity() []byte { - if m != nil { - return m.Identity - } - return nil -} - -func (m *AclUserPermissionChange) GetPermissions() AclUserPermissions { - if m != nil { - return m.Permissions - } - return AclUserPermissions_Admin -} - -type AclSyncMessage struct { - Content *AclSyncContentValue `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` -} - -func (m *AclSyncMessage) Reset() { *m = AclSyncMessage{} } -func (m *AclSyncMessage) String() string { return proto.CompactTextString(m) } -func (*AclSyncMessage) ProtoMessage() {} -func (*AclSyncMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{14} -} -func (m *AclSyncMessage) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclSyncMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclSyncMessage.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 *AclSyncMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclSyncMessage.Merge(m, src) -} -func (m *AclSyncMessage) XXX_Size() int { - return m.Size() -} -func (m *AclSyncMessage) XXX_DiscardUnknown() { - xxx_messageInfo_AclSyncMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_AclSyncMessage proto.InternalMessageInfo - -func (m *AclSyncMessage) GetContent() *AclSyncContentValue { - if m != nil { - return m.Content - } - return nil -} - -// AclSyncContentValue provides different types for acl sync -type AclSyncContentValue struct { - // Types that are valid to be assigned to Value: - // - // *AclSyncContentValue_AddRecords - Value isAclSyncContentValue_Value `protobuf_oneof:"value"` -} - -func (m *AclSyncContentValue) Reset() { *m = AclSyncContentValue{} } -func (m *AclSyncContentValue) String() string { return proto.CompactTextString(m) } -func (*AclSyncContentValue) ProtoMessage() {} -func (*AclSyncContentValue) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{15} -} -func (m *AclSyncContentValue) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclSyncContentValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclSyncContentValue.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 *AclSyncContentValue) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclSyncContentValue.Merge(m, src) -} -func (m *AclSyncContentValue) XXX_Size() int { - return m.Size() -} -func (m *AclSyncContentValue) XXX_DiscardUnknown() { - xxx_messageInfo_AclSyncContentValue.DiscardUnknown(m) -} - -var xxx_messageInfo_AclSyncContentValue proto.InternalMessageInfo - -type isAclSyncContentValue_Value interface { - isAclSyncContentValue_Value() - MarshalTo([]byte) (int, error) - Size() int -} - -type AclSyncContentValue_AddRecords struct { - AddRecords *AclAddRecords `protobuf:"bytes,1,opt,name=addRecords,proto3,oneof" json:"addRecords,omitempty"` -} - -func (*AclSyncContentValue_AddRecords) isAclSyncContentValue_Value() {} - -func (m *AclSyncContentValue) GetValue() isAclSyncContentValue_Value { - if m != nil { - return m.Value - } - return nil -} - -func (m *AclSyncContentValue) GetAddRecords() *AclAddRecords { - if x, ok := m.GetValue().(*AclSyncContentValue_AddRecords); ok { - return x.AddRecords - } - return nil -} - -// XXX_OneofWrappers is for the internal use of the proto package. -func (*AclSyncContentValue) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*AclSyncContentValue_AddRecords)(nil), - } -} - -type AclAddRecords struct { - Records []*RawAclRecordWithId `protobuf:"bytes,1,rep,name=records,proto3" json:"records,omitempty"` -} - -func (m *AclAddRecords) Reset() { *m = AclAddRecords{} } -func (m *AclAddRecords) String() string { return proto.CompactTextString(m) } -func (*AclAddRecords) ProtoMessage() {} -func (*AclAddRecords) Descriptor() ([]byte, []int) { - return fileDescriptor_c8e9f754f34e929b, []int{16} -} -func (m *AclAddRecords) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AclAddRecords) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AclAddRecords.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 *AclAddRecords) XXX_Merge(src proto.Message) { - xxx_messageInfo_AclAddRecords.Merge(m, src) -} -func (m *AclAddRecords) XXX_Size() int { - return m.Size() -} -func (m *AclAddRecords) XXX_DiscardUnknown() { - xxx_messageInfo_AclAddRecords.DiscardUnknown(m) -} - -var xxx_messageInfo_AclAddRecords proto.InternalMessageInfo - -func (m *AclAddRecords) GetRecords() []*RawAclRecordWithId { - if m != nil { - return m.Records - } - return nil -} - func init() { proto.RegisterEnum("aclrecord.AclUserPermissions", AclUserPermissions_name, AclUserPermissions_value) - proto.RegisterType((*RawAclRecord)(nil), "aclrecord.RawAclRecord") - proto.RegisterType((*RawAclRecordWithId)(nil), "aclrecord.RawAclRecordWithId") - proto.RegisterType((*AclRecord)(nil), "aclrecord.AclRecord") proto.RegisterType((*AclRoot)(nil), "aclrecord.AclRoot") + proto.RegisterType((*AclAccountInvite)(nil), "aclrecord.AclAccountInvite") + proto.RegisterType((*AclAccountRequestJoin)(nil), "aclrecord.AclAccountRequestJoin") + proto.RegisterType((*AclAccountRequestAccept)(nil), "aclrecord.AclAccountRequestAccept") + proto.RegisterType((*AclAccountRequestDecline)(nil), "aclrecord.AclAccountRequestDecline") + proto.RegisterType((*AclAccountInviteRevoke)(nil), "aclrecord.AclAccountInviteRevoke") + proto.RegisterType((*AclReadKeyWithRecord)(nil), "aclrecord.AclReadKeyWithRecord") + proto.RegisterType((*AclEncryptedReadKey)(nil), "aclrecord.AclEncryptedReadKey") + proto.RegisterType((*AclAccountPermissionChange)(nil), "aclrecord.AclAccountPermissionChange") + proto.RegisterType((*AclReadKeyChange)(nil), "aclrecord.AclReadKeyChange") + proto.RegisterType((*AclAccountRemove)(nil), "aclrecord.AclAccountRemove") + proto.RegisterType((*AclAccountRequestRemove)(nil), "aclrecord.AclAccountRequestRemove") proto.RegisterType((*AclContentValue)(nil), "aclrecord.AclContentValue") proto.RegisterType((*AclData)(nil), "aclrecord.AclData") - proto.RegisterType((*AclState)(nil), "aclrecord.AclState") - proto.RegisterMapType((map[string]*AclUserInvite)(nil), "aclrecord.AclState.InvitesEntry") - proto.RegisterType((*AclUserState)(nil), "aclrecord.AclUserState") - proto.RegisterType((*AclUserAdd)(nil), "aclrecord.AclUserAdd") - proto.RegisterType((*AclUserInvite)(nil), "aclrecord.AclUserInvite") - proto.RegisterType((*AclUserJoin)(nil), "aclrecord.AclUserJoin") - proto.RegisterType((*AclUserRemove)(nil), "aclrecord.AclUserRemove") - proto.RegisterType((*AclReadKeyReplace)(nil), "aclrecord.AclReadKeyReplace") - proto.RegisterType((*AclUserPermissionChange)(nil), "aclrecord.AclUserPermissionChange") - proto.RegisterType((*AclSyncMessage)(nil), "aclrecord.AclSyncMessage") - proto.RegisterType((*AclSyncContentValue)(nil), "aclrecord.AclSyncContentValue") - proto.RegisterType((*AclAddRecords)(nil), "aclrecord.AclAddRecords") } func init() { @@ -1144,209 +955,60 @@ func init() { } var fileDescriptor_c8e9f754f34e929b = []byte{ - // 914 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xf7, 0xd8, 0x49, 0x9c, 0x7d, 0x36, 0x89, 0x33, 0x40, 0xbb, 0x8a, 0x82, 0x15, 0xad, 0x84, - 0x14, 0x55, 0x55, 0x22, 0x0c, 0x52, 0xaa, 0x08, 0x51, 0xb9, 0xa5, 0xc8, 0x6e, 0x84, 0x54, 0x4d, - 0x80, 0xa2, 0x72, 0x9a, 0xcc, 0x8e, 0xd2, 0xa5, 0xeb, 0xdd, 0xd5, 0xcc, 0xd8, 0xc8, 0x9f, 0x02, - 0x6e, 0x5c, 0xb9, 0x20, 0xf1, 0x51, 0x38, 0xf6, 0x12, 0x89, 0x23, 0x4a, 0x3e, 0x03, 0x77, 0x34, - 0x33, 0xde, 0xff, 0xce, 0x0a, 0x0e, 0x70, 0x48, 0xbc, 0xf3, 0xde, 0x6f, 0x66, 0x7e, 0xef, 0xf7, - 0xfe, 0xec, 0xc2, 0xa7, 0x2c, 0x9e, 0xcd, 0xe2, 0x48, 0x26, 0x94, 0xf1, 0x93, 0xf8, 0xf2, 0x7b, - 0xce, 0xd4, 0x09, 0x65, 0xa1, 0xfe, 0x13, 0x9c, 0xc5, 0xc2, 0x4f, 0x44, 0xac, 0xe2, 0x13, 0xf3, - 0x5f, 0xe6, 0xd6, 0x63, 0x63, 0xc0, 0x4e, 0x66, 0xf0, 0x7e, 0x41, 0xd0, 0x27, 0xf4, 0x87, 0x31, - 0x0b, 0x89, 0x31, 0x60, 0x17, 0xba, 0x09, 0x5d, 0x86, 0x31, 0xf5, 0x5d, 0x74, 0x88, 0x8e, 0xfa, - 0x24, 0x5d, 0xe2, 0x03, 0x70, 0x64, 0x70, 0x15, 0x51, 0x35, 0x17, 0xdc, 0x6d, 0x1b, 0x5f, 0x6e, - 0xc0, 0x0f, 0x60, 0x40, 0x19, 0xe3, 0x89, 0x8a, 0xc5, 0xd4, 0xe7, 0x91, 0x0a, 0xd4, 0xd2, 0xed, - 0x18, 0x50, 0xcd, 0x8e, 0x1f, 0xc2, 0x5e, 0x6a, 0xbb, 0xc8, 0x4e, 0xdc, 0x30, 0xe0, 0xba, 0xc3, - 0xfb, 0x0c, 0x70, 0x91, 0xe1, 0xcb, 0x40, 0xbd, 0x9e, 0x36, 0xf1, 0xdc, 0x81, 0x76, 0xe0, 0x1b, - 0x82, 0x0e, 0x69, 0x07, 0xbe, 0xf7, 0x23, 0x02, 0x27, 0x8f, 0xef, 0x1e, 0x6c, 0x25, 0x82, 0x2f, - 0xa6, 0x76, 0x9b, 0x43, 0x56, 0x2b, 0xbc, 0x0f, 0xdb, 0x41, 0xca, 0xdb, 0x06, 0x97, 0xad, 0x31, - 0x86, 0x0d, 0x9f, 0x2a, 0xba, 0x8a, 0xc7, 0x3c, 0x6b, 0x35, 0x04, 0xa7, 0xfe, 0x39, 0x5f, 0x4e, - 0x7d, 0xc3, 0xdd, 0x21, 0xb9, 0x41, 0x7b, 0x55, 0x30, 0xe3, 0x52, 0xd1, 0x59, 0xe2, 0x6e, 0x1e, - 0xa2, 0xa3, 0x0e, 0xc9, 0x0d, 0xde, 0x35, 0x82, 0xae, 0x66, 0x14, 0xc7, 0xaa, 0x74, 0x2f, 0xaa, - 0xdc, 0x7b, 0x00, 0xce, 0x8c, 0x4a, 0xc5, 0xc5, 0x39, 0x4f, 0x49, 0xe5, 0x06, 0xad, 0x80, 0xc9, - 0xff, 0xd4, 0x37, 0xc4, 0x1c, 0x92, 0x2e, 0x75, 0x2e, 0x78, 0xc4, 0xc4, 0x32, 0x51, 0xdc, 0x27, - 0x96, 0xd3, 0x4a, 0xde, 0x9a, 0xbd, 0x99, 0xa9, 0xce, 0x54, 0xca, 0x26, 0xcf, 0xd4, 0x96, 0xcd, - 0x54, 0xcd, 0xe1, 0x5d, 0xb7, 0x61, 0x77, 0xcc, 0xc2, 0xa7, 0x71, 0xa4, 0x78, 0xa4, 0xbe, 0xa1, - 0xe1, 0x9c, 0xe3, 0x8f, 0xa0, 0x3b, 0x97, 0x5c, 0x8c, 0x7d, 0x2b, 0x78, 0x6f, 0xf4, 0xfe, 0x71, - 0x5e, 0x8e, 0x63, 0x16, 0x7e, 0x6d, 0x9d, 0x93, 0x16, 0x49, 0x71, 0xf8, 0x0c, 0x40, 0x3f, 0x12, - 0x3e, 0x8b, 0x17, 0xb6, 0xd2, 0x7a, 0x23, 0xb7, 0xbe, 0xcb, 0xfa, 0x27, 0x2d, 0x52, 0x40, 0xe3, - 0x6f, 0xe1, 0x3d, 0xbd, 0x7a, 0xc1, 0xc5, 0x2c, 0x90, 0x32, 0x88, 0xa3, 0xa7, 0xaf, 0x69, 0x74, - 0xc5, 0x8d, 0x42, 0xbd, 0x91, 0x57, 0x3f, 0xa5, 0x8a, 0x9c, 0xb4, 0xc8, 0xda, 0x13, 0x52, 0x56, - 0xd3, 0x68, 0x11, 0x28, 0x5b, 0xad, 0x6b, 0x59, 0x59, 0x7f, 0xca, 0xca, 0xae, 0xf0, 0x27, 0xb0, - 0xad, 0x57, 0xcf, 0xe3, 0x20, 0x32, 0x1a, 0xf7, 0x46, 0xf7, 0xea, 0x3b, 0xb5, 0x77, 0xd2, 0x22, - 0x19, 0xf2, 0x49, 0x17, 0x36, 0x17, 0x5a, 0x43, 0xef, 0x99, 0x29, 0x97, 0xcf, 0x75, 0xd9, 0x9d, - 0x01, 0xd0, 0x4c, 0x61, 0x17, 0x1d, 0x76, 0x8e, 0x7a, 0xa3, 0xfd, 0xf2, 0x59, 0x45, 0xf9, 0x49, - 0x01, 0xed, 0xfd, 0x85, 0x60, 0x7b, 0xcc, 0xc2, 0x0b, 0x45, 0x15, 0xc7, 0x43, 0x80, 0xac, 0x5c, - 0xa5, 0x39, 0xc8, 0x21, 0x05, 0x0b, 0x3e, 0xb5, 0xe1, 0x1a, 0xb0, 0x74, 0xdb, 0xe6, 0xa2, 0xfb, - 0x75, 0xd2, 0xc6, 0x4f, 0x0a, 0x50, 0x7c, 0x06, 0xdd, 0xc0, 0x44, 0x2d, 0xdd, 0x8e, 0xd9, 0x75, - 0x58, 0xde, 0x65, 0x60, 0xc7, 0x56, 0x18, 0xf9, 0x2c, 0x52, 0x62, 0x49, 0xd2, 0x0d, 0xfb, 0x5f, - 0x41, 0xbf, 0xe8, 0xc0, 0x03, 0xe8, 0xbc, 0xe1, 0xcb, 0x55, 0xa7, 0xea, 0x47, 0x7c, 0xbc, 0xd2, - 0xe4, 0xee, 0xb2, 0xb0, 0x07, 0x10, 0x0b, 0x3b, 0x6b, 0x3f, 0x42, 0xde, 0x1b, 0xe8, 0x17, 0xd9, - 0x36, 0xb6, 0xdc, 0x63, 0xe8, 0x25, 0x59, 0xe6, 0xa5, 0xb9, 0x65, 0x67, 0xf4, 0x41, 0x53, 0xd9, - 0x48, 0x52, 0xdc, 0xe1, 0xfd, 0x8c, 0x00, 0xf2, 0xb2, 0x6e, 0xbc, 0xeb, 0x21, 0xec, 0x55, 0xdb, - 0xd1, 0x2a, 0xdd, 0x27, 0x75, 0x47, 0x95, 0x59, 0xe7, 0x5f, 0x33, 0xfb, 0x0d, 0xc1, 0x3b, 0x25, - 0x8d, 0xf0, 0x11, 0xec, 0xda, 0x71, 0xfb, 0x62, 0x7e, 0x19, 0x06, 0xec, 0x9c, 0xa7, 0x1c, 0xab, - 0xe6, 0xff, 0x9b, 0xea, 0xaf, 0x08, 0x7a, 0x85, 0xae, 0x68, 0x54, 0x31, 0x0b, 0xe2, 0xa2, 0xf2, - 0x72, 0xaa, 0x9a, 0xb1, 0x07, 0xfd, 0x2c, 0x2e, 0x1d, 0xab, 0x1d, 0xe7, 0x25, 0xdb, 0xfa, 0x40, - 0x37, 0xee, 0x08, 0xd4, 0x93, 0x99, 0xa2, 0xab, 0xf1, 0xd3, 0x44, 0xf4, 0x0b, 0xd8, 0x5d, 0xf5, - 0x17, 0xe1, 0x49, 0x48, 0x59, 0xd6, 0x56, 0x07, 0x65, 0x65, 0x48, 0x09, 0x44, 0xaa, 0x9b, 0xbc, - 0xef, 0x60, 0xaf, 0x86, 0x6a, 0xbc, 0x78, 0xdd, 0xeb, 0xa0, 0xbd, 0xfe, 0x75, 0xe0, 0x2d, 0xe0, - 0xfe, 0x1d, 0x83, 0xf1, 0xbf, 0x6d, 0x9b, 0xe7, 0xb0, 0xa3, 0x67, 0xc3, 0x32, 0x62, 0x5f, 0x72, - 0x29, 0xe9, 0x15, 0xc7, 0x8f, 0xa0, 0xcb, 0xb2, 0x31, 0xa7, 0x7b, 0x7d, 0x58, 0x99, 0x23, 0xcb, - 0x88, 0x95, 0x46, 0x5d, 0x0a, 0xf7, 0x5e, 0xc1, 0xbb, 0x6b, 0xfc, 0x66, 0x74, 0xfa, 0xbe, 0xfd, - 0x0c, 0x90, 0xab, 0x33, 0x2b, 0xf3, 0x63, 0x9c, 0xf9, 0xf5, 0x00, 0xcf, 0xd1, 0xf9, 0x28, 0x9e, - 0x98, 0x8c, 0xe7, 0x38, 0x7c, 0x0a, 0x5d, 0x91, 0x1d, 0xa9, 0xb3, 0x59, 0x8c, 0xba, 0xfe, 0xdd, - 0x42, 0x52, 0xf4, 0x83, 0x53, 0xc0, 0x75, 0x51, 0xb0, 0x03, 0x9b, 0x63, 0x7f, 0x16, 0x44, 0x83, - 0x16, 0x06, 0xd8, 0x7a, 0x29, 0x02, 0xc5, 0xc5, 0x00, 0xe9, 0x67, 0x9d, 0x21, 0x2e, 0x06, 0xed, - 0x27, 0x8f, 0x7f, 0xbf, 0x19, 0xa2, 0xb7, 0x37, 0x43, 0xf4, 0xe7, 0xcd, 0x10, 0xfd, 0x74, 0x3b, - 0x6c, 0xbd, 0xbd, 0x1d, 0xb6, 0xfe, 0xb8, 0x1d, 0xb6, 0x5e, 0x7d, 0xf8, 0x8f, 0xbe, 0x0a, 0x2f, - 0xb7, 0xcc, 0xcf, 0xc7, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x3b, 0x3f, 0x0a, 0x45, 0x0a, - 0x00, 0x00, -} - -func (m *RawAclRecord) 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 *RawAclRecord) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *RawAclRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.AcceptorSignature) > 0 { - i -= len(m.AcceptorSignature) - copy(dAtA[i:], m.AcceptorSignature) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.AcceptorSignature))) - i-- - dAtA[i] = 0x22 - } - if len(m.AcceptorIdentity) > 0 { - i -= len(m.AcceptorIdentity) - copy(dAtA[i:], m.AcceptorIdentity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.AcceptorIdentity))) - i-- - dAtA[i] = 0x1a - } - if len(m.Signature) > 0 { - i -= len(m.Signature) - copy(dAtA[i:], m.Signature) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Signature))) - i-- - dAtA[i] = 0x12 - } - if len(m.Payload) > 0 { - i -= len(m.Payload) - copy(dAtA[i:], m.Payload) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Payload))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *RawAclRecordWithId) 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 *RawAclRecordWithId) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *RawAclRecordWithId) 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 = encodeVarintAclrecord(dAtA, i, uint64(len(m.Id))) - i-- - dAtA[i] = 0x12 - } - if len(m.Payload) > 0 { - i -= len(m.Payload) - copy(dAtA[i:], m.Payload) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Payload))) - i-- - dAtA[i] = 0xa - } - 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 m.Timestamp != 0 { - i = encodeVarintAclrecord(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x28 - } - if len(m.ReadKeyId) > 0 { - i -= len(m.ReadKeyId) - copy(dAtA[i:], m.ReadKeyId) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.ReadKeyId))) - i-- - dAtA[i] = 0x22 - } - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Data))) - i-- - dAtA[i] = 0x1a - } - if len(m.Identity) > 0 { - i -= len(m.Identity) - copy(dAtA[i:], m.Identity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) - i-- - dAtA[i] = 0x12 - } - if len(m.PrevId) > 0 { - i -= len(m.PrevId) - copy(dAtA[i:], m.PrevId) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.PrevId))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil + // 835 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0x5f, 0x4f, 0xdb, 0x56, + 0x14, 0xb7, 0x13, 0x92, 0x90, 0x63, 0xfe, 0x98, 0x3b, 0x36, 0x3c, 0xb6, 0x79, 0x99, 0x27, 0xa6, + 0x08, 0x4d, 0x30, 0x65, 0x9a, 0x34, 0x4d, 0x93, 0x20, 0x10, 0xb4, 0x84, 0x8d, 0x6d, 0xba, 0x6c, + 0x63, 0xaa, 0xd4, 0x4a, 0x17, 0xfb, 0x08, 0xdc, 0xc6, 0x76, 0x7a, 0x7d, 0x93, 0x2a, 0xdf, 0xa2, + 0x1f, 0xa6, 0xaf, 0x7d, 0xef, 0x23, 0x2f, 0x95, 0xfa, 0x58, 0xc1, 0x07, 0xe8, 0x57, 0xa8, 0x7c, + 0x1d, 0xe2, 0x3f, 0x31, 0x69, 0x79, 0x80, 0xd8, 0xe7, 0x9e, 0xdf, 0xef, 0xfc, 0xfb, 0x9d, 0x9b, + 0xc0, 0xaf, 0x76, 0xe0, 0x79, 0x81, 0x1f, 0x0e, 0x98, 0x8d, 0xbb, 0xc1, 0xf9, 0x63, 0xb4, 0xc5, + 0x2e, 0xb3, 0xfb, 0xd1, 0x1f, 0x47, 0x3b, 0xe0, 0xce, 0x80, 0x07, 0x22, 0xd8, 0x95, 0xff, 0xc3, + 0xc4, 0xba, 0x23, 0x0d, 0xa4, 0x3e, 0x35, 0x58, 0xaf, 0x55, 0xa8, 0xb5, 0xed, 0x3e, 0x0d, 0x02, + 0x41, 0x36, 0x61, 0xd1, 0x75, 0xd0, 0x17, 0xae, 0x18, 0x1b, 0x6a, 0x43, 0x6d, 0x2e, 0xd1, 0xe9, + 0x3b, 0xf9, 0x12, 0xea, 0x1e, 0x0b, 0x05, 0xf2, 0xdf, 0x71, 0x6c, 0x94, 0xe4, 0x61, 0x62, 0x20, + 0x06, 0xd4, 0x64, 0x2a, 0x3d, 0xc7, 0x28, 0x37, 0xd4, 0x66, 0x9d, 0xde, 0xbe, 0x92, 0x6d, 0xd0, + 0xd1, 0xb7, 0xf9, 0x78, 0x20, 0xd0, 0xa1, 0xc8, 0x9c, 0x08, 0xbe, 0x20, 0xe1, 0x33, 0xf6, 0x28, + 0x86, 0x70, 0x3d, 0x0c, 0x05, 0xf3, 0x06, 0x46, 0xa5, 0xa1, 0x36, 0xcb, 0x34, 0x31, 0x90, 0xef, + 0x61, 0xed, 0x36, 0x9b, 0x53, 0xf7, 0xc2, 0x67, 0x62, 0xc8, 0xd1, 0xa8, 0x4a, 0xaa, 0xd9, 0x03, + 0xeb, 0x07, 0xd0, 0xdb, 0x76, 0xbf, 0x6d, 0xdb, 0xc1, 0xd0, 0x17, 0x3d, 0x7f, 0xe4, 0x0a, 0x8c, + 0xf8, 0x5d, 0xf9, 0x14, 0x25, 0x11, 0x17, 0x98, 0x18, 0xac, 0x97, 0x2a, 0x7c, 0x9a, 0x40, 0x28, + 0x3e, 0x1d, 0x62, 0x28, 0x8e, 0x03, 0xd7, 0x27, 0xdf, 0xc1, 0x4a, 0xec, 0xd6, 0xcb, 0x76, 0x27, + 0x67, 0x4d, 0xfc, 0xa8, 0xec, 0x6d, 0xcf, 0x91, 0x8d, 0xaa, 0xd3, 0x9c, 0x95, 0xfc, 0x0c, 0x1b, + 0x59, 0x64, 0x52, 0x4f, 0x59, 0x12, 0xdf, 0x75, 0x1c, 0x4d, 0xc8, 0x43, 0xc1, 0x1c, 0x26, 0xd8, + 0xa4, 0x8b, 0xd3, 0x77, 0xeb, 0x9d, 0x0a, 0x1b, 0x33, 0xf9, 0xb7, 0x6d, 0x1b, 0x07, 0xf3, 0x27, + 0xdb, 0x84, 0x55, 0x1e, 0x3b, 0xe7, 0xd2, 0xce, 0x9b, 0xc9, 0x09, 0xac, 0xe5, 0x67, 0x16, 0x1a, + 0xe5, 0x46, 0xb9, 0xa9, 0xb5, 0xbe, 0xde, 0x49, 0x34, 0x16, 0xc9, 0x29, 0x3e, 0x3d, 0x73, 0xc5, + 0x65, 0x8c, 0xa6, 0xb3, 0x48, 0xb2, 0x07, 0xda, 0x00, 0xb9, 0xe7, 0x86, 0xa1, 0x1b, 0xf8, 0xa1, + 0xac, 0x67, 0xa5, 0xf5, 0x55, 0x96, 0xe8, 0xdf, 0x10, 0xf9, 0xdf, 0x89, 0x13, 0x4d, 0x23, 0xac, + 0x0e, 0x18, 0x33, 0x05, 0x77, 0xd0, 0xee, 0xbb, 0x3e, 0x16, 0x55, 0xa5, 0x16, 0x56, 0x65, 0xed, + 0xc3, 0x67, 0x79, 0xa5, 0x50, 0x1c, 0x05, 0x4f, 0xb0, 0x60, 0x9e, 0x6a, 0xd1, 0x3c, 0xad, 0x47, + 0xb0, 0x5e, 0x54, 0x73, 0xd4, 0x75, 0x9e, 0x45, 0x4e, 0xdf, 0x0b, 0xf7, 0xa2, 0x54, 0xbc, 0x17, + 0xd6, 0x43, 0xf8, 0xa4, 0x6d, 0xf7, 0x8f, 0xf2, 0xeb, 0x32, 0x6f, 0xa8, 0xf7, 0xa1, 0x1f, 0xc3, + 0x66, 0xd2, 0x80, 0xa4, 0xd9, 0x87, 0x97, 0xcc, 0xbf, 0xc0, 0xb9, 0x51, 0x72, 0x13, 0x2c, 0xdd, + 0x7b, 0x82, 0xff, 0xc8, 0x2d, 0x9d, 0x24, 0x32, 0x09, 0xb8, 0x0f, 0x1a, 0x8b, 0x73, 0x91, 0xfa, + 0x52, 0xa5, 0xbe, 0xcc, 0x2c, 0x69, 0xbe, 0x17, 0x34, 0x0d, 0xb1, 0x44, 0x7a, 0xf7, 0x29, 0x7a, + 0xc1, 0x08, 0x89, 0x09, 0x30, 0x49, 0xdb, 0xc5, 0x98, 0x74, 0x89, 0xa6, 0x2c, 0xf9, 0xa8, 0xa5, + 0xfb, 0x47, 0xfd, 0xbc, 0x60, 0xfd, 0xe2, 0xe0, 0xd6, 0x8b, 0x0a, 0xac, 0xb6, 0xed, 0xfe, 0x61, + 0xe0, 0x0b, 0xf4, 0xc5, 0x7f, 0xac, 0x3f, 0x44, 0xf2, 0x13, 0x54, 0x63, 0x19, 0xc9, 0xae, 0x6a, + 0xad, 0x2f, 0xb2, 0xb1, 0x32, 0x7a, 0xec, 0x2a, 0x74, 0xe2, 0x4c, 0x7e, 0x83, 0x25, 0x37, 0xa5, + 0x51, 0xd9, 0x73, 0xad, 0xf5, 0xcd, 0x1c, 0x70, 0xec, 0xd8, 0x55, 0x68, 0x06, 0x48, 0x3a, 0xa0, + 0xf1, 0xe4, 0x8e, 0x93, 0x17, 0x8f, 0xd6, 0x6a, 0x14, 0xf2, 0xa4, 0xee, 0xc2, 0xae, 0x42, 0xd3, + 0x30, 0x72, 0x0c, 0xcb, 0x3c, 0x7d, 0xd3, 0xc8, 0x2d, 0xd6, 0x5a, 0xd6, 0x3c, 0x9e, 0xd8, 0xb3, + 0xab, 0xd0, 0x2c, 0x94, 0x9c, 0x82, 0x3e, 0xc8, 0xa9, 0x4f, 0x7e, 0x0b, 0x68, 0xad, 0xad, 0x42, + 0xba, 0xbc, 0x54, 0xbb, 0x0a, 0x9d, 0x21, 0x20, 0x87, 0xb0, 0xcc, 0xd2, 0x42, 0x90, 0xdf, 0x18, + 0x77, 0x75, 0x3b, 0x76, 0x89, 0x32, 0xcb, 0x60, 0x22, 0x12, 0x9e, 0xd6, 0xa8, 0x51, 0x2b, 0x22, + 0xc9, 0xc8, 0x38, 0x2e, 0x2f, 0xad, 0xeb, 0x13, 0x58, 0xe1, 0x99, 0x3b, 0xca, 0x58, 0x94, 0x2c, + 0xdf, 0xce, 0xeb, 0xd5, 0xc4, 0xb5, 0xab, 0xd0, 0x1c, 0x98, 0xfc, 0x0f, 0xeb, 0xac, 0x40, 0x6b, + 0x46, 0xfd, 0xc3, 0x03, 0x98, 0x96, 0x59, 0xc8, 0x70, 0x50, 0x83, 0xca, 0x28, 0x92, 0xa8, 0x75, + 0x24, 0x7f, 0x1a, 0x74, 0x98, 0x60, 0xe4, 0x17, 0x00, 0x36, 0x15, 0xf0, 0x64, 0x27, 0x37, 0xb3, + 0x31, 0xd2, 0xea, 0xa6, 0x29, 0xef, 0xed, 0x3f, 0x80, 0xcc, 0xde, 0x03, 0x64, 0x11, 0x16, 0xfe, + 0x0c, 0x7c, 0xd4, 0x15, 0x52, 0x87, 0xca, 0x5f, 0xcf, 0x7c, 0xe4, 0xba, 0x1a, 0x3d, 0xb6, 0x1d, + 0xcf, 0xf5, 0xf5, 0x12, 0x01, 0xa8, 0x9e, 0x71, 0x57, 0x20, 0xd7, 0xcb, 0xd1, 0x73, 0xd4, 0x5c, + 0xe4, 0xfa, 0xc2, 0xc1, 0xde, 0xab, 0x6b, 0x53, 0xbd, 0xba, 0x36, 0xd5, 0xb7, 0xd7, 0xa6, 0xfa, + 0xfc, 0xc6, 0x54, 0xae, 0x6e, 0x4c, 0xe5, 0xcd, 0x8d, 0xa9, 0x3c, 0xd8, 0xfa, 0xa8, 0xdf, 0x44, + 0xe7, 0x55, 0xf9, 0xf1, 0xe3, 0xfb, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd0, 0xe7, 0x8e, 0xa9, 0x43, + 0x09, 0x00, 0x00, } func (m *AclRoot) Marshal() (dAtA []byte, err error) { @@ -1412,6 +1074,418 @@ func (m *AclRoot) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *AclAccountInvite) 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 *AclAccountInvite) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountInvite) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.InviteKey) > 0 { + i -= len(m.InviteKey) + copy(dAtA[i:], m.InviteKey) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.InviteKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclAccountRequestJoin) 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 *AclAccountRequestJoin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountRequestJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Metadata) > 0 { + i -= len(m.Metadata) + copy(dAtA[i:], m.Metadata) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Metadata))) + i-- + dAtA[i] = 0x22 + } + if len(m.InviteIdentitySignature) > 0 { + i -= len(m.InviteIdentitySignature) + copy(dAtA[i:], m.InviteIdentitySignature) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.InviteIdentitySignature))) + i-- + dAtA[i] = 0x1a + } + if len(m.InviteRecordId) > 0 { + i -= len(m.InviteRecordId) + copy(dAtA[i:], m.InviteRecordId) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.InviteRecordId))) + i-- + dAtA[i] = 0x12 + } + if len(m.InviteIdentity) > 0 { + i -= len(m.InviteIdentity) + copy(dAtA[i:], m.InviteIdentity) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.InviteIdentity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclAccountRequestAccept) 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 *AclAccountRequestAccept) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountRequestAccept) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Permissions != 0 { + i = encodeVarintAclrecord(dAtA, i, uint64(m.Permissions)) + i-- + dAtA[i] = 0x20 + } + if len(m.EncryptedReadKeys) > 0 { + for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.EncryptedReadKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclrecord(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.RequestRecordId) > 0 { + i -= len(m.RequestRecordId) + copy(dAtA[i:], m.RequestRecordId) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.RequestRecordId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclAccountRequestDecline) 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 *AclAccountRequestDecline) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountRequestDecline) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RequestRecordId) > 0 { + i -= len(m.RequestRecordId) + copy(dAtA[i:], m.RequestRecordId) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.RequestRecordId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclAccountInviteRevoke) 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 *AclAccountInviteRevoke) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountInviteRevoke) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.InviteRecordId) > 0 { + i -= len(m.InviteRecordId) + copy(dAtA[i:], m.InviteRecordId) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.InviteRecordId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclReadKeyWithRecord) 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 *AclReadKeyWithRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclReadKeyWithRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.EncryptedReadKey) > 0 { + i -= len(m.EncryptedReadKey) + copy(dAtA[i:], m.EncryptedReadKey) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.EncryptedReadKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.RecordId) > 0 { + i -= len(m.RecordId) + copy(dAtA[i:], m.RecordId) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.RecordId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclEncryptedReadKey) 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 *AclEncryptedReadKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclEncryptedReadKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.EncryptedReadKey) > 0 { + i -= len(m.EncryptedReadKey) + copy(dAtA[i:], m.EncryptedReadKey) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.EncryptedReadKey))) + i-- + dAtA[i] = 0x12 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclAccountPermissionChange) 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 *AclAccountPermissionChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountPermissionChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Permissions != 0 { + i = encodeVarintAclrecord(dAtA, i, uint64(m.Permissions)) + i-- + dAtA[i] = 0x10 + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AclReadKeyChange) 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 *AclReadKeyChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclReadKeyChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AccountKeys) > 0 { + for iNdEx := len(m.AccountKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AccountKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclrecord(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *AclAccountRemove) 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 *AclAccountRemove) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AccountKeys) > 0 { + for iNdEx := len(m.AccountKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AccountKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclrecord(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Identities) > 0 { + for iNdEx := len(m.Identities) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Identities[iNdEx]) + copy(dAtA[i:], m.Identities[iNdEx]) + i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identities[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *AclAccountRequestRemove) 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 *AclAccountRequestRemove) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclAccountRequestRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *AclContentValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1444,16 +1518,16 @@ func (m *AclContentValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *AclContentValue_UserAdd) MarshalTo(dAtA []byte) (int, error) { +func (m *AclContentValue_Invite) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *AclContentValue_UserAdd) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *AclContentValue_Invite) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) - if m.UserAdd != nil { + if m.Invite != nil { { - size, err := m.UserAdd.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Invite.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1465,16 +1539,16 @@ func (m *AclContentValue_UserAdd) MarshalToSizedBuffer(dAtA []byte) (int, error) } return len(dAtA) - i, nil } -func (m *AclContentValue_UserRemove) MarshalTo(dAtA []byte) (int, error) { +func (m *AclContentValue_InviteRevoke) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *AclContentValue_UserRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *AclContentValue_InviteRevoke) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) - if m.UserRemove != nil { + if m.InviteRevoke != nil { { - size, err := m.UserRemove.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.InviteRevoke.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1486,16 +1560,16 @@ func (m *AclContentValue_UserRemove) MarshalToSizedBuffer(dAtA []byte) (int, err } return len(dAtA) - i, nil } -func (m *AclContentValue_UserPermissionChange) MarshalTo(dAtA []byte) (int, error) { +func (m *AclContentValue_RequestJoin) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *AclContentValue_UserPermissionChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *AclContentValue_RequestJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) - if m.UserPermissionChange != nil { + if m.RequestJoin != nil { { - size, err := m.UserPermissionChange.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.RequestJoin.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1507,16 +1581,16 @@ func (m *AclContentValue_UserPermissionChange) MarshalToSizedBuffer(dAtA []byte) } return len(dAtA) - i, nil } -func (m *AclContentValue_UserInvite) MarshalTo(dAtA []byte) (int, error) { +func (m *AclContentValue_RequestAccept) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *AclContentValue_UserInvite) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *AclContentValue_RequestAccept) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) - if m.UserInvite != nil { + if m.RequestAccept != nil { { - size, err := m.UserInvite.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.RequestAccept.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1528,16 +1602,16 @@ func (m *AclContentValue_UserInvite) MarshalToSizedBuffer(dAtA []byte) (int, err } return len(dAtA) - i, nil } -func (m *AclContentValue_UserJoin) MarshalTo(dAtA []byte) (int, error) { +func (m *AclContentValue_PermissionChange) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *AclContentValue_UserJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *AclContentValue_PermissionChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) - if m.UserJoin != nil { + if m.PermissionChange != nil { { - size, err := m.UserJoin.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.PermissionChange.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1549,6 +1623,90 @@ func (m *AclContentValue_UserJoin) MarshalToSizedBuffer(dAtA []byte) (int, error } return len(dAtA) - i, nil } +func (m *AclContentValue_AccountRemove) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclContentValue_AccountRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.AccountRemove != nil { + { + size, err := m.AccountRemove.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclrecord(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + return len(dAtA) - i, nil +} +func (m *AclContentValue_ReadKeyChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclContentValue_ReadKeyChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ReadKeyChange != nil { + { + size, err := m.ReadKeyChange.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclrecord(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *AclContentValue_RequestDecline) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclContentValue_RequestDecline) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.RequestDecline != nil { + { + size, err := m.RequestDecline.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclrecord(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + return len(dAtA) - i, nil +} +func (m *AclContentValue_AccountRequestRemove) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AclContentValue_AccountRequestRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.AccountRequestRemove != nil { + { + size, err := m.AccountRequestRemove.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAclrecord(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + return len(dAtA) - i, nil +} func (m *AclData) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1586,495 +1744,6 @@ func (m *AclData) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *AclState) 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 *AclState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Invites) > 0 { - for k := range m.Invites { - v := m.Invites[k] - baseI := i - if v != nil { - { - size, err := v.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintAclrecord(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintAclrecord(dAtA, i, uint64(len(k))) - i-- - dAtA[i] = 0xa - i = encodeVarintAclrecord(dAtA, i, uint64(baseI-i)) - i-- - dAtA[i] = 0x1a - } - } - if len(m.UserStates) > 0 { - for iNdEx := len(m.UserStates) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.UserStates[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintAclrecord(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - } - if len(m.ReadKeyIds) > 0 { - for iNdEx := len(m.ReadKeyIds) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.ReadKeyIds[iNdEx]) - copy(dAtA[i:], m.ReadKeyIds[iNdEx]) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.ReadKeyIds[iNdEx]))) - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func (m *AclUserState) 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 *AclUserState) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclUserState) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Permissions != 0 { - i = encodeVarintAclrecord(dAtA, i, uint64(m.Permissions)) - i-- - dAtA[i] = 0x10 - } - if len(m.Identity) > 0 { - i -= len(m.Identity) - copy(dAtA[i:], m.Identity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclUserAdd) 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 *AclUserAdd) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclUserAdd) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Permissions != 0 { - i = encodeVarintAclrecord(dAtA, i, uint64(m.Permissions)) - i-- - dAtA[i] = 0x18 - } - if len(m.EncryptedReadKeys) > 0 { - for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.EncryptedReadKeys[iNdEx]) - copy(dAtA[i:], m.EncryptedReadKeys[iNdEx]) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.EncryptedReadKeys[iNdEx]))) - i-- - dAtA[i] = 0x12 - } - } - if len(m.Identity) > 0 { - i -= len(m.Identity) - copy(dAtA[i:], m.Identity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclUserInvite) 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 *AclUserInvite) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclUserInvite) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Permissions != 0 { - i = encodeVarintAclrecord(dAtA, i, uint64(m.Permissions)) - i-- - dAtA[i] = 0x18 - } - if len(m.EncryptedReadKeys) > 0 { - for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.EncryptedReadKeys[iNdEx]) - copy(dAtA[i:], m.EncryptedReadKeys[iNdEx]) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.EncryptedReadKeys[iNdEx]))) - i-- - dAtA[i] = 0x12 - } - } - if len(m.AcceptPublicKey) > 0 { - i -= len(m.AcceptPublicKey) - copy(dAtA[i:], m.AcceptPublicKey) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.AcceptPublicKey))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclUserJoin) 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 *AclUserJoin) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclUserJoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.EncryptedReadKeys) > 0 { - for iNdEx := len(m.EncryptedReadKeys) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.EncryptedReadKeys[iNdEx]) - copy(dAtA[i:], m.EncryptedReadKeys[iNdEx]) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.EncryptedReadKeys[iNdEx]))) - i-- - dAtA[i] = 0x22 - } - } - if len(m.AcceptPubKey) > 0 { - i -= len(m.AcceptPubKey) - copy(dAtA[i:], m.AcceptPubKey) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.AcceptPubKey))) - i-- - dAtA[i] = 0x1a - } - if len(m.AcceptSignature) > 0 { - i -= len(m.AcceptSignature) - copy(dAtA[i:], m.AcceptSignature) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.AcceptSignature))) - i-- - dAtA[i] = 0x12 - } - if len(m.Identity) > 0 { - i -= len(m.Identity) - copy(dAtA[i:], m.Identity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclUserRemove) 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 *AclUserRemove) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclUserRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.ReadKeyReplaces) > 0 { - for iNdEx := len(m.ReadKeyReplaces) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.ReadKeyReplaces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintAclrecord(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - } - if len(m.Identity) > 0 { - i -= len(m.Identity) - copy(dAtA[i:], m.Identity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclReadKeyReplace) 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 *AclReadKeyReplace) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclReadKeyReplace) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.EncryptedReadKey) > 0 { - i -= len(m.EncryptedReadKey) - copy(dAtA[i:], m.EncryptedReadKey) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.EncryptedReadKey))) - i-- - dAtA[i] = 0x12 - } - if len(m.Identity) > 0 { - i -= len(m.Identity) - copy(dAtA[i:], m.Identity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclUserPermissionChange) 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 *AclUserPermissionChange) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclUserPermissionChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Permissions != 0 { - i = encodeVarintAclrecord(dAtA, i, uint64(m.Permissions)) - i-- - dAtA[i] = 0x10 - } - if len(m.Identity) > 0 { - i -= len(m.Identity) - copy(dAtA[i:], m.Identity) - i = encodeVarintAclrecord(dAtA, i, uint64(len(m.Identity))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclSyncMessage) 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 *AclSyncMessage) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclSyncMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Content != nil { - { - size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintAclrecord(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *AclSyncContentValue) 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 *AclSyncContentValue) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclSyncContentValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Value != nil { - { - size := m.Value.Size() - i -= size - if _, err := m.Value.MarshalTo(dAtA[i:]); err != nil { - return 0, err - } - } - } - return len(dAtA) - i, nil -} - -func (m *AclSyncContentValue_AddRecords) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclSyncContentValue_AddRecords) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - if m.AddRecords != nil { - { - size, err := m.AddRecords.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintAclrecord(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} -func (m *AclAddRecords) 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 *AclAddRecords) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *AclAddRecords) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Records) > 0 { - for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintAclrecord(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - func encodeVarintAclrecord(dAtA []byte, offset int, v uint64) int { offset -= sovAclrecord(v) base := offset @@ -2086,76 +1755,6 @@ func encodeVarintAclrecord(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *RawAclRecord) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Payload) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.Signature) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.AcceptorIdentity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.AcceptorSignature) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - return n -} - -func (m *RawAclRecordWithId) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Payload) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.Id) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - return n -} - -func (m *AclRecord) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.PrevId) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.Identity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.Data) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.ReadKeyId) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - if m.Timestamp != 0 { - n += 1 + sovAclrecord(uint64(m.Timestamp)) - } - return n -} - func (m *AclRoot) Size() (n int) { if m == nil { return 0 @@ -2188,6 +1787,191 @@ func (m *AclRoot) Size() (n int) { return n } +func (m *AclAccountInvite) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.InviteKey) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} + +func (m *AclAccountRequestJoin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.InviteIdentity) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + l = len(m.InviteRecordId) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + l = len(m.InviteIdentitySignature) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + l = len(m.Metadata) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} + +func (m *AclAccountRequestAccept) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + l = len(m.RequestRecordId) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + if len(m.EncryptedReadKeys) > 0 { + for _, e := range m.EncryptedReadKeys { + l = e.Size() + n += 1 + l + sovAclrecord(uint64(l)) + } + } + if m.Permissions != 0 { + n += 1 + sovAclrecord(uint64(m.Permissions)) + } + return n +} + +func (m *AclAccountRequestDecline) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RequestRecordId) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} + +func (m *AclAccountInviteRevoke) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.InviteRecordId) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} + +func (m *AclReadKeyWithRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RecordId) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + l = len(m.EncryptedReadKey) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} + +func (m *AclEncryptedReadKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + l = len(m.EncryptedReadKey) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} + +func (m *AclAccountPermissionChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovAclrecord(uint64(l)) + } + if m.Permissions != 0 { + n += 1 + sovAclrecord(uint64(m.Permissions)) + } + return n +} + +func (m *AclReadKeyChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AccountKeys) > 0 { + for _, e := range m.AccountKeys { + l = e.Size() + n += 1 + l + sovAclrecord(uint64(l)) + } + } + return n +} + +func (m *AclAccountRemove) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Identities) > 0 { + for _, b := range m.Identities { + l = len(b) + n += 1 + l + sovAclrecord(uint64(l)) + } + } + if len(m.AccountKeys) > 0 { + for _, e := range m.AccountKeys { + l = e.Size() + n += 1 + l + sovAclrecord(uint64(l)) + } + } + return n +} + +func (m *AclAccountRequestRemove) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *AclContentValue) Size() (n int) { if m == nil { return 0 @@ -2200,62 +1984,110 @@ func (m *AclContentValue) Size() (n int) { return n } -func (m *AclContentValue_UserAdd) Size() (n int) { +func (m *AclContentValue_Invite) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.UserAdd != nil { - l = m.UserAdd.Size() + if m.Invite != nil { + l = m.Invite.Size() n += 1 + l + sovAclrecord(uint64(l)) } return n } -func (m *AclContentValue_UserRemove) Size() (n int) { +func (m *AclContentValue_InviteRevoke) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.UserRemove != nil { - l = m.UserRemove.Size() + if m.InviteRevoke != nil { + l = m.InviteRevoke.Size() n += 1 + l + sovAclrecord(uint64(l)) } return n } -func (m *AclContentValue_UserPermissionChange) Size() (n int) { +func (m *AclContentValue_RequestJoin) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.UserPermissionChange != nil { - l = m.UserPermissionChange.Size() + if m.RequestJoin != nil { + l = m.RequestJoin.Size() n += 1 + l + sovAclrecord(uint64(l)) } return n } -func (m *AclContentValue_UserInvite) Size() (n int) { +func (m *AclContentValue_RequestAccept) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.UserInvite != nil { - l = m.UserInvite.Size() + if m.RequestAccept != nil { + l = m.RequestAccept.Size() n += 1 + l + sovAclrecord(uint64(l)) } return n } -func (m *AclContentValue_UserJoin) Size() (n int) { +func (m *AclContentValue_PermissionChange) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.UserJoin != nil { - l = m.UserJoin.Size() + if m.PermissionChange != nil { + l = m.PermissionChange.Size() + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} +func (m *AclContentValue_AccountRemove) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AccountRemove != nil { + l = m.AccountRemove.Size() + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} +func (m *AclContentValue_ReadKeyChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ReadKeyChange != nil { + l = m.ReadKeyChange.Size() + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} +func (m *AclContentValue_RequestDecline) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.RequestDecline != nil { + l = m.RequestDecline.Size() + n += 1 + l + sovAclrecord(uint64(l)) + } + return n +} +func (m *AclContentValue_AccountRequestRemove) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AccountRequestRemove != nil { + l = m.AccountRequestRemove.Size() n += 1 + l + sovAclrecord(uint64(l)) } return n @@ -2275,740 +2107,12 @@ func (m *AclData) Size() (n int) { return n } -func (m *AclState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.ReadKeyIds) > 0 { - for _, s := range m.ReadKeyIds { - l = len(s) - n += 1 + l + sovAclrecord(uint64(l)) - } - } - if len(m.UserStates) > 0 { - for _, e := range m.UserStates { - l = e.Size() - n += 1 + l + sovAclrecord(uint64(l)) - } - } - if len(m.Invites) > 0 { - for k, v := range m.Invites { - _ = k - _ = v - l = 0 - if v != nil { - l = v.Size() - l += 1 + sovAclrecord(uint64(l)) - } - mapEntrySize := 1 + len(k) + sovAclrecord(uint64(len(k))) + l - n += mapEntrySize + 1 + sovAclrecord(uint64(mapEntrySize)) - } - } - return n -} - -func (m *AclUserState) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Identity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - if m.Permissions != 0 { - n += 1 + sovAclrecord(uint64(m.Permissions)) - } - return n -} - -func (m *AclUserAdd) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Identity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - if len(m.EncryptedReadKeys) > 0 { - for _, b := range m.EncryptedReadKeys { - l = len(b) - n += 1 + l + sovAclrecord(uint64(l)) - } - } - if m.Permissions != 0 { - n += 1 + sovAclrecord(uint64(m.Permissions)) - } - return n -} - -func (m *AclUserInvite) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.AcceptPublicKey) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - if len(m.EncryptedReadKeys) > 0 { - for _, b := range m.EncryptedReadKeys { - l = len(b) - n += 1 + l + sovAclrecord(uint64(l)) - } - } - if m.Permissions != 0 { - n += 1 + sovAclrecord(uint64(m.Permissions)) - } - return n -} - -func (m *AclUserJoin) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Identity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.AcceptSignature) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.AcceptPubKey) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - if len(m.EncryptedReadKeys) > 0 { - for _, b := range m.EncryptedReadKeys { - l = len(b) - n += 1 + l + sovAclrecord(uint64(l)) - } - } - return n -} - -func (m *AclUserRemove) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Identity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - if len(m.ReadKeyReplaces) > 0 { - for _, e := range m.ReadKeyReplaces { - l = e.Size() - n += 1 + l + sovAclrecord(uint64(l)) - } - } - return n -} - -func (m *AclReadKeyReplace) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Identity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - l = len(m.EncryptedReadKey) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - return n -} - -func (m *AclUserPermissionChange) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Identity) - if l > 0 { - n += 1 + l + sovAclrecord(uint64(l)) - } - if m.Permissions != 0 { - n += 1 + sovAclrecord(uint64(m.Permissions)) - } - return n -} - -func (m *AclSyncMessage) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Content != nil { - l = m.Content.Size() - n += 1 + l + sovAclrecord(uint64(l)) - } - return n -} - -func (m *AclSyncContentValue) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Value != nil { - n += m.Value.Size() - } - return n -} - -func (m *AclSyncContentValue_AddRecords) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.AddRecords != nil { - l = m.AddRecords.Size() - n += 1 + l + sovAclrecord(uint64(l)) - } - return n -} -func (m *AclAddRecords) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Records) > 0 { - for _, e := range m.Records { - l = e.Size() - n += 1 + l + sovAclrecord(uint64(l)) - } - } - return n -} - func sovAclrecord(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozAclrecord(x uint64) (n int) { return sovAclrecord(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *RawAclRecord) 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 ErrIntOverflowAclrecord - } - 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: RawAclRecord: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RawAclRecord: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Payload = append(m.Payload[:0], dAtA[iNdEx:postIndex]...) - if m.Payload == nil { - m.Payload = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AcceptorIdentity", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AcceptorIdentity = append(m.AcceptorIdentity[:0], dAtA[iNdEx:postIndex]...) - if m.AcceptorIdentity == nil { - m.AcceptorIdentity = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AcceptorSignature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AcceptorSignature = append(m.AcceptorSignature[:0], dAtA[iNdEx:postIndex]...) - if m.AcceptorSignature == nil { - m.AcceptorSignature = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RawAclRecordWithId) 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 ErrIntOverflowAclrecord - } - 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: RawAclRecordWithId: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RawAclRecordWithId: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Payload = append(m.Payload[:0], dAtA[iNdEx:postIndex]...) - if m.Payload == nil { - m.Payload = []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 ErrIntOverflowAclrecord - } - 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 ErrInvalidLengthAclrecord - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - 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 ErrIntOverflowAclrecord - } - 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 PrevId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - 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 ErrInvalidLengthAclrecord - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.PrevId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Identity = append(m.Identity[:0], dAtA[iNdEx:postIndex]...) - if m.Identity == nil { - m.Identity = []byte{} - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ReadKeyId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - 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 ErrInvalidLengthAclrecord - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ReadKeyId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - m.Timestamp = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Timestamp |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *AclRoot) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3246,7 +2350,7 @@ func (m *AclRoot) Unmarshal(dAtA []byte) error { } return nil } -func (m *AclContentValue) Unmarshal(dAtA []byte) error { +func (m *AclAccountInvite) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3269,17 +2373,17 @@ func (m *AclContentValue) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: AclContentValue: wiretype end group for non-group") + return fmt.Errorf("proto: AclAccountInvite: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: AclContentValue: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: AclAccountInvite: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserAdd", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InviteKey", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowAclrecord @@ -3289,304 +2393,113 @@ func (m *AclContentValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthAclrecord } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthAclrecord } if postIndex > l { return io.ErrUnexpectedEOF } - v := &AclUserAdd{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.InviteKey = append(m.InviteKey[:0], dAtA[iNdEx:postIndex]...) + if m.InviteKey == nil { + m.InviteKey = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclrecord(dAtA[iNdEx:]) + if err != nil { return err } - m.Value = &AclContentValue_UserAdd{v} + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclrecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AclAccountRequestJoin) 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 ErrIntOverflowAclrecord + } + 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: AclAccountRequestJoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AclAccountRequestJoin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InviteIdentity", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InviteIdentity = append(m.InviteIdentity[:0], dAtA[iNdEx:postIndex]...) + if m.InviteIdentity == nil { + m.InviteIdentity = []byte{} + } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserRemove", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - v := &AclUserRemove{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - m.Value = &AclContentValue_UserRemove{v} - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserPermissionChange", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - v := &AclUserPermissionChange{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - m.Value = &AclContentValue_UserPermissionChange{v} - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserInvite", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - v := &AclUserInvite{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - m.Value = &AclContentValue_UserInvite{v} - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserJoin", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - v := &AclUserJoin{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - m.Value = &AclContentValue_UserJoin{v} - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AclData) 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 ErrIntOverflowAclrecord - } - 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: AclData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AclData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AclContent", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AclContent = append(m.AclContent, &AclContentValue{}) - if err := m.AclContent[len(m.AclContent)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AclState) 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 ErrIntOverflowAclrecord - } - 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: AclState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AclState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ReadKeyIds", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InviteRecordId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -3614,224 +2527,11 @@ func (m *AclState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ReadKeyIds = append(m.ReadKeyIds, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field UserStates", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.UserStates = append(m.UserStates, &AclUserState{}) - if err := m.UserStates[len(m.UserStates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.InviteRecordId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Invites", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Invites == nil { - m.Invites = make(map[string]*AclUserInvite) - } - var mapkey string - var mapvalue *AclUserInvite - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthAclrecord - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthAclrecord - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var mapmsglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - mapmsglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if mapmsglen < 0 { - return ErrInvalidLengthAclrecord - } - postmsgIndex := iNdEx + mapmsglen - if postmsgIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postmsgIndex > l { - return io.ErrUnexpectedEOF - } - mapvalue = &AclUserInvite{} - if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { - return err - } - iNdEx = postmsgIndex - } else { - iNdEx = entryPreIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Invites[mapkey] = mapvalue - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AclUserState) 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 ErrIntOverflowAclrecord - } - 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: AclUserState: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AclUserState: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InviteIdentitySignature", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3858,455 +2558,14 @@ func (m *AclUserState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Identity = append(m.Identity[:0], dAtA[iNdEx:postIndex]...) - if m.Identity == nil { - m.Identity = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) - } - m.Permissions = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Permissions |= AclUserPermissions(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AclUserAdd) 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 ErrIntOverflowAclrecord - } - 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: AclUserAdd: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AclUserAdd: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Identity = append(m.Identity[:0], dAtA[iNdEx:postIndex]...) - if m.Identity == nil { - m.Identity = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.EncryptedReadKeys = append(m.EncryptedReadKeys, make([]byte, postIndex-iNdEx)) - copy(m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1], dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) - } - m.Permissions = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Permissions |= AclUserPermissions(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AclUserInvite) 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 ErrIntOverflowAclrecord - } - 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: AclUserInvite: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AclUserInvite: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AcceptPublicKey", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AcceptPublicKey = append(m.AcceptPublicKey[:0], dAtA[iNdEx:postIndex]...) - if m.AcceptPublicKey == nil { - m.AcceptPublicKey = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.EncryptedReadKeys = append(m.EncryptedReadKeys, make([]byte, postIndex-iNdEx)) - copy(m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1], dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) - } - m.Permissions = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Permissions |= AclUserPermissions(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AclUserJoin) 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 ErrIntOverflowAclrecord - } - 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: AclUserJoin: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AclUserJoin: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Identity = append(m.Identity[:0], dAtA[iNdEx:postIndex]...) - if m.Identity == nil { - m.Identity = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AcceptSignature", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AcceptSignature = append(m.AcceptSignature[:0], dAtA[iNdEx:postIndex]...) - if m.AcceptSignature == nil { - m.AcceptSignature = []byte{} - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AcceptPubKey", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowAclrecord - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthAclrecord - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthAclrecord - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AcceptPubKey = append(m.AcceptPubKey[:0], dAtA[iNdEx:postIndex]...) - if m.AcceptPubKey == nil { - m.AcceptPubKey = []byte{} + m.InviteIdentitySignature = append(m.InviteIdentitySignature[:0], dAtA[iNdEx:postIndex]...) + if m.InviteIdentitySignature == nil { + m.InviteIdentitySignature = []byte{} } iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -4333,8 +2592,10 @@ func (m *AclUserJoin) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.EncryptedReadKeys = append(m.EncryptedReadKeys, make([]byte, postIndex-iNdEx)) - copy(m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1], dAtA[iNdEx:postIndex]) + m.Metadata = append(m.Metadata[:0], dAtA[iNdEx:postIndex]...) + if m.Metadata == nil { + m.Metadata = []byte{} + } iNdEx = postIndex default: iNdEx = preIndex @@ -4357,7 +2618,7 @@ func (m *AclUserJoin) Unmarshal(dAtA []byte) error { } return nil } -func (m *AclUserRemove) Unmarshal(dAtA []byte) error { +func (m *AclAccountRequestAccept) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4380,10 +2641,10 @@ func (m *AclUserRemove) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: AclUserRemove: wiretype end group for non-group") + return fmt.Errorf("proto: AclAccountRequestAccept: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: AclUserRemove: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: AclAccountRequestAccept: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -4422,7 +2683,39 @@ func (m *AclUserRemove) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ReadKeyReplaces", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field RequestRecordId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + 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 ErrInvalidLengthAclrecord + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestRecordId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKeys", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4449,11 +2742,310 @@ func (m *AclUserRemove) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ReadKeyReplaces = append(m.ReadKeyReplaces, &AclReadKeyReplace{}) - if err := m.ReadKeyReplaces[len(m.ReadKeyReplaces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.EncryptedReadKeys = append(m.EncryptedReadKeys, &AclReadKeyWithRecord{}) + if err := m.EncryptedReadKeys[len(m.EncryptedReadKeys)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) + } + m.Permissions = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Permissions |= AclUserPermissions(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAclrecord(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclrecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AclAccountRequestDecline) 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 ErrIntOverflowAclrecord + } + 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: AclAccountRequestDecline: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AclAccountRequestDecline: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestRecordId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + 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 ErrInvalidLengthAclrecord + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestRecordId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclrecord(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclrecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AclAccountInviteRevoke) 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 ErrIntOverflowAclrecord + } + 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: AclAccountInviteRevoke: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AclAccountInviteRevoke: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InviteRecordId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + 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 ErrInvalidLengthAclrecord + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InviteRecordId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclrecord(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclrecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AclReadKeyWithRecord) 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 ErrIntOverflowAclrecord + } + 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: AclReadKeyWithRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AclReadKeyWithRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RecordId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + 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 ErrInvalidLengthAclrecord + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RecordId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EncryptedReadKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EncryptedReadKey = append(m.EncryptedReadKey[:0], dAtA[iNdEx:postIndex]...) + if m.EncryptedReadKey == nil { + m.EncryptedReadKey = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipAclrecord(dAtA[iNdEx:]) @@ -4475,7 +3067,7 @@ func (m *AclUserRemove) Unmarshal(dAtA []byte) error { } return nil } -func (m *AclReadKeyReplace) Unmarshal(dAtA []byte) error { +func (m *AclEncryptedReadKey) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4498,10 +3090,10 @@ func (m *AclReadKeyReplace) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: AclReadKeyReplace: wiretype end group for non-group") + return fmt.Errorf("proto: AclEncryptedReadKey: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: AclReadKeyReplace: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: AclEncryptedReadKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -4593,7 +3185,7 @@ func (m *AclReadKeyReplace) Unmarshal(dAtA []byte) error { } return nil } -func (m *AclUserPermissionChange) Unmarshal(dAtA []byte) error { +func (m *AclAccountPermissionChange) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4616,10 +3208,10 @@ func (m *AclUserPermissionChange) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: AclUserPermissionChange: wiretype end group for non-group") + return fmt.Errorf("proto: AclAccountPermissionChange: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: AclUserPermissionChange: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: AclAccountPermissionChange: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -4696,7 +3288,7 @@ func (m *AclUserPermissionChange) Unmarshal(dAtA []byte) error { } return nil } -func (m *AclSyncMessage) Unmarshal(dAtA []byte) error { +func (m *AclReadKeyChange) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4719,15 +3311,15 @@ func (m *AclSyncMessage) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: AclSyncMessage: wiretype end group for non-group") + return fmt.Errorf("proto: AclReadKeyChange: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: AclSyncMessage: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: AclReadKeyChange: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AccountKeys", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4754,10 +3346,8 @@ func (m *AclSyncMessage) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Content == nil { - m.Content = &AclSyncContentValue{} - } - if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.AccountKeys = append(m.AccountKeys, &AclEncryptedReadKey{}) + if err := m.AccountKeys[len(m.AccountKeys)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -4782,7 +3372,7 @@ func (m *AclSyncMessage) Unmarshal(dAtA []byte) error { } return nil } -func (m *AclSyncContentValue) Unmarshal(dAtA []byte) error { +func (m *AclAccountRemove) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -4805,15 +3395,47 @@ func (m *AclSyncContentValue) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: AclSyncContentValue: wiretype end group for non-group") + return fmt.Errorf("proto: AclAccountRemove: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: AclSyncContentValue: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: AclAccountRemove: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AddRecords", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Identities", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identities = append(m.Identities, make([]byte, postIndex-iNdEx)) + copy(m.Identities[len(m.Identities)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountKeys", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4840,65 +3462,149 @@ func (m *AclSyncContentValue) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &AclAddRecords{} + m.AccountKeys = append(m.AccountKeys, &AclEncryptedReadKey{}) + if err := m.AccountKeys[len(m.AccountKeys)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclrecord(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclrecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AclAccountRequestRemove) 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 ErrIntOverflowAclrecord + } + 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: AclAccountRequestRemove: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AclAccountRequestRemove: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipAclrecord(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclrecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AclContentValue) 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 ErrIntOverflowAclrecord + } + 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: AclContentValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AclContentValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Invite", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclAccountInvite{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Value = &AclSyncContentValue_AddRecords{v} + m.Value = &AclContentValue_Invite{v} iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipAclrecord(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthAclrecord - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *AclAddRecords) 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 ErrIntOverflowAclrecord - } - 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: AclAddRecords: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AclAddRecords: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InviteRevoke", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4925,8 +3631,338 @@ func (m *AclAddRecords) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Records = append(m.Records, &RawAclRecordWithId{}) - if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + v := &AclAccountInviteRevoke{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_InviteRevoke{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestJoin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclAccountRequestJoin{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_RequestJoin{v} + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestAccept", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclAccountRequestAccept{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_RequestAccept{v} + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PermissionChange", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclAccountPermissionChange{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_PermissionChange{v} + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountRemove", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclAccountRemove{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_AccountRemove{v} + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReadKeyChange", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclReadKeyChange{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_ReadKeyChange{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestDecline", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclAccountRequestDecline{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_RequestDecline{v} + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountRequestRemove", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &AclAccountRequestRemove{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &AclContentValue_AccountRequestRemove{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAclrecord(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclrecord + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AclData) 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 ErrIntOverflowAclrecord + } + 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: AclData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AclData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclContent", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclrecord + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAclrecord + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAclrecord + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AclContent = append(m.AclContent, &AclContentValue{}) + if err := m.AclContent[len(m.AclContent)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/commonspace/object/acl/aclrecordproto/protos/aclrecord.proto b/commonspace/object/acl/aclrecordproto/protos/aclrecord.proto index c90cf116..4b6f2b5e 100644 --- a/commonspace/object/acl/aclrecordproto/protos/aclrecord.proto +++ b/commonspace/object/acl/aclrecordproto/protos/aclrecord.proto @@ -2,26 +2,7 @@ syntax = "proto3"; package aclrecord; option go_package = "commonspace/object/acl/aclrecordproto"; -message RawAclRecord { - bytes payload = 1; - bytes signature = 2; - bytes acceptorIdentity = 3; - bytes acceptorSignature = 4; -} - -message RawAclRecordWithId { - bytes payload = 1; - string id = 2; -} - -message AclRecord { - string prevId = 1; - bytes identity = 2; - bytes data = 3; - string readKeyId = 4; - int64 timestamp = 5; -} - +// AclRoot is a root of access control list message AclRoot { bytes identity = 1; bytes masterKey = 2; @@ -31,82 +12,95 @@ message AclRoot { bytes identitySignature = 6; } -message AclContentValue { - oneof value { - AclUserAdd userAdd = 1; - AclUserRemove userRemove = 2; - AclUserPermissionChange userPermissionChange = 3; - AclUserInvite userInvite = 4; - AclUserJoin userJoin = 5; - } +// AclAccountInvite contains the public invite key, the private part of which is sent to the user directly +message AclAccountInvite { + bytes inviteKey = 1; } -message AclData { - repeated AclContentValue aclContent = 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 +message AclAccountRequestJoin { + bytes inviteIdentity = 1; + string inviteRecordId = 2; + bytes inviteIdentitySignature = 3; + bytes metadata = 4; } -message AclState { - repeated string readKeyIds = 1; - repeated AclUserState userStates = 2; - map invites = 3; -} - -message AclUserState { +// AclAccountRequestAccept contains the reference to join record and all read keys, encrypted with the identity of the requestor +message AclAccountRequestAccept { bytes identity = 1; - AclUserPermissions permissions = 2; + string requestRecordId = 2; + repeated AclReadKeyWithRecord encryptedReadKeys = 3; + AclUserPermissions permissions = 4; } -message AclUserAdd { - bytes identity = 1; - repeated bytes encryptedReadKeys = 2; - AclUserPermissions permissions = 3; +// AclAccountRequestDecline contains the reference to join record +message AclAccountRequestDecline { + string requestRecordId = 1; } -message AclUserInvite { - bytes acceptPublicKey = 1; - repeated bytes encryptedReadKeys = 2; - AclUserPermissions permissions = 3; +// AclAccountInviteRevoke revokes the invite record +message AclAccountInviteRevoke { + string inviteRecordId = 1; } -message AclUserJoin { - bytes identity = 1; - bytes acceptSignature = 2; - bytes acceptPubKey = 3; - repeated bytes encryptedReadKeys = 4; +// AclReadKeys are a read key with record id +message AclReadKeyWithRecord { + string recordId = 1; + bytes encryptedReadKey = 2; } -message AclUserRemove { - bytes identity = 1; - repeated AclReadKeyReplace readKeyReplaces = 2; -} - -message AclReadKeyReplace { +// AclEncryptedReadKeys are new key for specific identity +message AclEncryptedReadKey { bytes identity = 1; bytes encryptedReadKey = 2; } -message AclUserPermissionChange { +// AclAccountPermissionChange changes permissions of specific account +message AclAccountPermissionChange { bytes identity = 1; AclUserPermissions permissions = 2; } -enum AclUserPermissions { - Admin = 0; - Writer = 1; - Reader = 2; +// AclReadKeyChange changes the key for a space +message AclReadKeyChange { + repeated AclEncryptedReadKey accountKeys = 1; } -message AclSyncMessage { - AclSyncContentValue content = 1; +// AclAccountRemove removes an account and changes read key for space +message AclAccountRemove { + repeated bytes identities = 1; + repeated AclEncryptedReadKey accountKeys = 2; } -// AclSyncContentValue provides different types for acl sync -message AclSyncContentValue { +// AclAccountRequestRemove adds a request to remove an account +message AclAccountRequestRemove { +} + +// AclContentValue contains possible values for Acl +message AclContentValue { oneof value { - AclAddRecords addRecords = 1; + AclAccountInvite invite = 1; + AclAccountInviteRevoke inviteRevoke = 2; + AclAccountRequestJoin requestJoin = 3; + AclAccountRequestAccept requestAccept = 4; + AclAccountPermissionChange permissionChange = 5; + AclAccountRemove accountRemove = 6; + AclReadKeyChange readKeyChange = 7; + AclAccountRequestDecline requestDecline = 8; + AclAccountRequestRemove accountRequestRemove = 9; } } -message AclAddRecords { - repeated RawAclRecordWithId records = 1; -} \ No newline at end of file +// AclData contains different acl content +message AclData { + repeated AclContentValue aclContent = 1; +} + +// AclUserPermissions contains different possible user roles +enum AclUserPermissions { + None = 0; + Owner = 1; + Admin = 2; + Writer = 3; + Reader = 4; +} diff --git a/commonspace/object/acl/list/aclrecordbuilder.go b/commonspace/object/acl/list/aclrecordbuilder.go index a1e89560..28c3c1f6 100644 --- a/commonspace/object/acl/list/aclrecordbuilder.go +++ b/commonspace/object/acl/list/aclrecordbuilder.go @@ -1,11 +1,14 @@ package list import ( + "time" + + "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "github.com/anyproto/any-sync/consensus/consensusproto" "github.com/anyproto/any-sync/util/cidutil" "github.com/anyproto/any-sync/util/crypto" "github.com/gogo/protobuf/proto" - "time" ) type RootContent struct { @@ -15,26 +18,387 @@ type RootContent struct { EncryptedReadKey []byte } +type RequestJoinPayload struct { + InviteRecordId string + InviteKey crypto.PrivKey + Metadata []byte +} + +type RequestAcceptPayload struct { + RequestRecordId string + Permissions AclPermissions +} + +type PermissionChangePayload struct { + Identity crypto.PubKey + Permissions AclPermissions +} + +type AccountRemovePayload struct { + Identities []crypto.PubKey + ReadKey crypto.SymKey +} + +type InviteResult struct { + InviteRec *consensusproto.RawRecord + InviteKey crypto.PrivKey +} + type AclRecordBuilder interface { - Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error) - BuildRoot(content RootContent) (rec *aclrecordproto.RawAclRecordWithId, err error) + UnmarshallWithId(rawIdRecord *consensusproto.RawRecordWithId) (rec *AclRecord, err error) + Unmarshall(rawRecord *consensusproto.RawRecord) (rec *AclRecord, err error) + + BuildRoot(content RootContent) (rec *consensusproto.RawRecordWithId, err error) + BuildInvite() (res InviteResult, err error) + BuildInviteRevoke(inviteRecordId string) (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) + BuildRequestRemove() (rawRecord *consensusproto.RawRecord, err error) + BuildPermissionChange(payload PermissionChangePayload) (rawRecord *consensusproto.RawRecord, err error) + BuildReadKeyChange(newKey crypto.SymKey) (rawRecord *consensusproto.RawRecord, err error) + BuildAccountRemove(payload AccountRemovePayload) (rawRecord *consensusproto.RawRecord, err error) } type aclRecordBuilder struct { - id string - keyStorage crypto.KeyStorage + id string + keyStorage crypto.KeyStorage + accountKeys *accountdata.AccountKeys + verifier AcceptorVerifier + state *AclState } -func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage) AclRecordBuilder { +func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage, keys *accountdata.AccountKeys, verifier AcceptorVerifier) AclRecordBuilder { return &aclRecordBuilder{ - id: id, - keyStorage: keyStorage, + id: id, + keyStorage: keyStorage, + accountKeys: keys, + verifier: verifier, } } -func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error) { +func (a *aclRecordBuilder) buildRecord(aclContent *aclrecordproto.AclContentValue) (rawRec *consensusproto.RawRecord, err error) { + aclData := &aclrecordproto.AclData{AclContent: []*aclrecordproto.AclContentValue{ + aclContent, + }} + marshalledData, err := aclData.Marshal() + if err != nil { + return + } + protoKey, err := a.accountKeys.SignKey.GetPublic().Marshall() + if err != nil { + return + } + rec := &consensusproto.Record{ + PrevId: a.state.lastRecordId, + Identity: protoKey, + Data: marshalledData, + Timestamp: time.Now().Unix(), + } + marshalledRec, err := rec.Marshal() + if err != nil { + return + } + signature, err := a.accountKeys.SignKey.Sign(marshalledRec) + if err != nil { + return + } + rawRec = &consensusproto.RawRecord{ + Payload: marshalledRec, + Signature: signature, + } + return +} + +func (a *aclRecordBuilder) BuildInvite() (res InviteResult, 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 + } + inviteRec := &aclrecordproto.AclAccountInvite{InviteKey: invitePubKey} + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}} + rawRec, err := a.buildRecord(content) + if err != nil { + return + } + res.InviteKey = privKey + res.InviteRec = rawRec + return +} + +func (a *aclRecordBuilder) BuildInviteRevoke(inviteRecordId string) (rawRecord *consensusproto.RawRecord, err error) { + if !a.state.Permissions(a.state.pubKey).CanManageAccounts() { + err = ErrInsufficientPermissions + return + } + _, exists := a.state.inviteKeys[inviteRecordId] + if !exists { + err = ErrNoSuchInvite + return + } + revokeRec := &aclrecordproto.AclAccountInviteRevoke{InviteRecordId: inviteRecordId} + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_InviteRevoke{InviteRevoke: revokeRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) BuildRequestJoin(payload RequestJoinPayload) (rawRecord *consensusproto.RawRecord, err error) { + key, exists := a.state.inviteKeys[payload.InviteRecordId] + if !exists { + err = ErrNoSuchInvite + return + } + if !payload.InviteKey.GetPublic().Equals(key) { + err = ErrIncorrectInviteKey + } + rawIdentity, err := a.accountKeys.SignKey.GetPublic().Raw() + if err != nil { + return + } + signature, err := payload.InviteKey.Sign(rawIdentity) + if err != nil { + return + } + protoIdentity, err := a.accountKeys.SignKey.GetPublic().Marshall() + if err != nil { + return + } + joinRec := &aclrecordproto.AclAccountRequestJoin{ + InviteIdentity: protoIdentity, + InviteRecordId: payload.InviteRecordId, + InviteIdentitySignature: signature, + Metadata: payload.Metadata, + } + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_RequestJoin{RequestJoin: joinRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) BuildRequestAccept(payload RequestAcceptPayload) (rawRecord *consensusproto.RawRecord, err error) { + if !a.state.Permissions(a.state.pubKey).CanManageAccounts() { + err = ErrInsufficientPermissions + return + } + request, exists := a.state.requestRecords[payload.RequestRecordId] + if !exists { + err = ErrNoSuchRequest + return + } + var encryptedReadKeys []*aclrecordproto.AclReadKeyWithRecord + for keyId, key := range a.state.userReadKeys { + rawKey, err := key.Raw() + if err != nil { + return nil, err + } + enc, err := request.RequestIdentity.Encrypt(rawKey) + if err != nil { + return nil, err + } + encryptedReadKeys = append(encryptedReadKeys, &aclrecordproto.AclReadKeyWithRecord{ + RecordId: keyId, + EncryptedReadKey: enc, + }) + } + if err != nil { + return + } + requestIdentityProto, err := request.RequestIdentity.Marshall() + if err != nil { + return + } + acceptRec := &aclrecordproto.AclAccountRequestAccept{ + Identity: requestIdentityProto, + RequestRecordId: payload.RequestRecordId, + EncryptedReadKeys: encryptedReadKeys, + Permissions: aclrecordproto.AclUserPermissions(payload.Permissions), + } + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_RequestAccept{RequestAccept: acceptRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) BuildRequestDecline(requestRecordId string) (rawRecord *consensusproto.RawRecord, err error) { + if !a.state.Permissions(a.state.pubKey).CanManageAccounts() { + err = ErrInsufficientPermissions + return + } + _, exists := a.state.requestRecords[requestRecordId] + if !exists { + err = ErrNoSuchRequest + return + } + declineRec := &aclrecordproto.AclAccountRequestDecline{RequestRecordId: requestRecordId} + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_RequestDecline{RequestDecline: declineRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) BuildPermissionChange(payload PermissionChangePayload) (rawRecord *consensusproto.RawRecord, err error) { + permissions := a.state.Permissions(a.state.pubKey) + if !permissions.CanManageAccounts() || payload.Identity.Equals(a.state.pubKey) { + err = ErrInsufficientPermissions + return + } + if payload.Permissions.IsOwner() { + err = ErrIsOwner + return + } + protoIdentity, err := payload.Identity.Marshall() + if err != nil { + return + } + permissionRec := &aclrecordproto.AclAccountPermissionChange{ + Identity: protoIdentity, + Permissions: aclrecordproto.AclUserPermissions(payload.Permissions), + } + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_PermissionChange{PermissionChange: permissionRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) BuildReadKeyChange(newKey crypto.SymKey) (rawRecord *consensusproto.RawRecord, err error) { + if !a.state.Permissions(a.state.pubKey).CanManageAccounts() { + err = ErrInsufficientPermissions + return + } + rawKey, err := newKey.Raw() + if err != nil { + return + } + if len(rawKey) != crypto.KeyBytes { + err = ErrIncorrectReadKey + return + } + var aclReadKeys []*aclrecordproto.AclEncryptedReadKey + for _, st := range a.state.userStates { + protoIdentity, err := st.PubKey.Marshall() + if err != nil { + return nil, err + } + enc, err := st.PubKey.Encrypt(rawKey) + if err != nil { + return nil, err + } + aclReadKeys = append(aclReadKeys, &aclrecordproto.AclEncryptedReadKey{ + Identity: protoIdentity, + EncryptedReadKey: enc, + }) + } + readRec := &aclrecordproto.AclReadKeyChange{AccountKeys: aclReadKeys} + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_ReadKeyChange{ReadKeyChange: readRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) BuildAccountRemove(payload AccountRemovePayload) (rawRecord *consensusproto.RawRecord, err error) { + deletedMap := map[string]struct{}{} + for _, key := range payload.Identities { + permissions := a.state.Permissions(key) + if permissions.IsOwner() { + return nil, ErrInsufficientPermissions + } + if permissions.NoPermissions() { + return nil, ErrNoSuchAccount + } + deletedMap[mapKeyFromPubKey(key)] = struct{}{} + } + if !a.state.Permissions(a.state.pubKey).CanManageAccounts() { + err = ErrInsufficientPermissions + return + } + rawKey, err := payload.ReadKey.Raw() + if err != nil { + return + } + if len(rawKey) != crypto.KeyBytes { + err = ErrIncorrectReadKey + return + } + var aclReadKeys []*aclrecordproto.AclEncryptedReadKey + for _, st := range a.state.userStates { + if _, exists := deletedMap[mapKeyFromPubKey(st.PubKey)]; exists { + continue + } + protoIdentity, err := st.PubKey.Marshall() + if err != nil { + return nil, err + } + enc, err := st.PubKey.Encrypt(rawKey) + if err != nil { + return nil, err + } + aclReadKeys = append(aclReadKeys, &aclrecordproto.AclEncryptedReadKey{ + Identity: protoIdentity, + EncryptedReadKey: enc, + }) + } + var marshalledIdentities [][]byte + for _, key := range payload.Identities { + protoIdentity, err := key.Marshall() + if err != nil { + return nil, err + } + marshalledIdentities = append(marshalledIdentities, protoIdentity) + } + removeRec := &aclrecordproto.AclAccountRemove{AccountKeys: aclReadKeys, Identities: marshalledIdentities} + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_AccountRemove{AccountRemove: removeRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) BuildRequestRemove() (rawRecord *consensusproto.RawRecord, err error) { + permissions := a.state.Permissions(a.state.pubKey) + if permissions.NoPermissions() { + err = ErrNoSuchAccount + return + } + if permissions.IsOwner() { + err = ErrIsOwner + return + } + removeRec := &aclrecordproto.AclAccountRequestRemove{} + content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_AccountRequestRemove{AccountRequestRemove: removeRec}} + return a.buildRecord(content) +} + +func (a *aclRecordBuilder) Unmarshall(rawRecord *consensusproto.RawRecord) (rec *AclRecord, err error) { + aclRecord := &consensusproto.Record{} + err = proto.Unmarshal(rawRecord.Payload, aclRecord) + if err != nil { + return + } + pubKey, err := a.keyStorage.PubKeyFromProto(aclRecord.Identity) + if err != nil { + return + } + aclData := &aclrecordproto.AclData{} + err = proto.Unmarshal(aclRecord.Data, aclData) + if err != nil { + return + } + rec = &AclRecord{ + PrevId: aclRecord.PrevId, + Timestamp: aclRecord.Timestamp, + Data: aclRecord.Data, + Signature: rawRecord.Signature, + Identity: pubKey, + Model: aclData, + } + res, err := pubKey.Verify(rawRecord.Payload, rawRecord.Signature) + if err != nil { + return + } + if !res { + err = ErrInvalidSignature + return + } + return +} + +func (a *aclRecordBuilder) UnmarshallWithId(rawIdRecord *consensusproto.RawRecordWithId) (rec *AclRecord, err error) { var ( - rawRec = &aclrecordproto.RawAclRecord{} + rawRec = &consensusproto.RawRecord{} pubKey crypto.PubKey ) err = proto.Unmarshal(rawIdRecord.Payload, rawRec) @@ -53,14 +417,17 @@ func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWi } rec = &AclRecord{ Id: rawIdRecord.Id, - ReadKeyId: rawIdRecord.Id, Timestamp: aclRoot.Timestamp, Signature: rawRec.Signature, Identity: pubKey, Model: aclRoot, } } else { - aclRecord := &aclrecordproto.AclRecord{} + err = a.verifier.VerifyAcceptor(rawRec) + if err != nil { + return + } + aclRecord := &consensusproto.Record{} err = proto.Unmarshal(rawRec.Payload, aclRecord) if err != nil { return @@ -69,14 +436,19 @@ func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWi if err != nil { return } + aclData := &aclrecordproto.AclData{} + err = proto.Unmarshal(aclRecord.Data, aclData) + if err != nil { + return + } rec = &AclRecord{ Id: rawIdRecord.Id, PrevId: aclRecord.PrevId, - ReadKeyId: aclRecord.ReadKeyId, Timestamp: aclRecord.Timestamp, Data: aclRecord.Data, Signature: rawRec.Signature, Identity: pubKey, + Model: aclData, } } @@ -84,7 +456,7 @@ func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWi return } -func (a *aclRecordBuilder) BuildRoot(content RootContent) (rec *aclrecordproto.RawAclRecordWithId, err error) { +func (a *aclRecordBuilder) BuildRoot(content RootContent) (rec *consensusproto.RawRecordWithId, err error) { rawIdentity, err := content.PrivKey.GetPublic().Raw() if err != nil { return @@ -118,8 +490,8 @@ func (a *aclRecordBuilder) BuildRoot(content RootContent) (rec *aclrecordproto.R func verifyRaw( pubKey crypto.PubKey, - rawRec *aclrecordproto.RawAclRecord, - recWithId *aclrecordproto.RawAclRecordWithId) (err error) { + rawRec *consensusproto.RawRecord, + recWithId *consensusproto.RawRecordWithId) (err error) { // verifying signature res, err := pubKey.Verify(rawRec.Payload, rawRec.Signature) if err != nil { @@ -137,7 +509,7 @@ func verifyRaw( return } -func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWithId *aclrecordproto.RawAclRecordWithId, err error) { +func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWithId *consensusproto.RawRecordWithId, err error) { marshalledRoot, err := aclRoot.Marshal() if err != nil { return @@ -146,7 +518,7 @@ func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWit if err != nil { return } - raw := &aclrecordproto.RawAclRecord{ + raw := &consensusproto.RawRecord{ Payload: marshalledRoot, Signature: signature, } @@ -158,7 +530,7 @@ func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWit if err != nil { return } - rawWithId = &aclrecordproto.RawAclRecordWithId{ + rawWithId = &consensusproto.RawRecordWithId{ Payload: marshalledRaw, Id: aclHeadId, } diff --git a/commonspace/object/acl/list/aclrecordbuilder_test.go b/commonspace/object/acl/list/aclrecordbuilder_test.go deleted file mode 100644 index 27a75a28..00000000 --- a/commonspace/object/acl/list/aclrecordbuilder_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package list - -import ( - "testing" -) - -func TestAclRecordBuilder_BuildUserJoin(t *testing.T) { - return -} diff --git a/commonspace/object/acl/list/aclstate.go b/commonspace/object/acl/list/aclstate.go index a780c315..f11a99bd 100644 --- a/commonspace/object/acl/list/aclstate.go +++ b/commonspace/object/acl/list/aclstate.go @@ -2,7 +2,7 @@ package list import ( "errors" - "fmt" + "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" "github.com/anyproto/any-sync/util/crypto" @@ -13,19 +13,24 @@ import ( var log = logger.NewNamedSugared("common.commonspace.acllist") var ( - ErrNoSuchUser = errors.New("no such user") - ErrFailedToDecrypt = errors.New("failed to decrypt key") - ErrUserRemoved = errors.New("user was removed from the document") - ErrDocumentForbidden = errors.New("your user was forbidden access to the document") - ErrUserAlreadyExists = errors.New("user already exists") - ErrNoSuchRecord = errors.New("no such record") - ErrNoSuchInvite = errors.New("no such invite") - ErrOldInvite = errors.New("invite is too old") - ErrInsufficientPermissions = errors.New("insufficient permissions") - ErrNoReadKey = errors.New("acl state doesn't have a read key") - ErrInvalidSignature = errors.New("signature is invalid") - ErrIncorrectRoot = errors.New("incorrect root") - ErrIncorrectRecordSequence = errors.New("incorrect prev id of a record") + ErrNoSuchAccount = errors.New("no such account") + ErrPendingRequest = errors.New("already exists pending request") + ErrUnexpectedContentType = errors.New("unexpected content type") + ErrIncorrectIdentity = errors.New("incorrect identity") + ErrIncorrectInviteKey = errors.New("incorrect invite key") + ErrFailedToDecrypt = errors.New("failed to decrypt key") + ErrNoSuchRecord = errors.New("no such record") + ErrNoSuchRequest = errors.New("no such request") + ErrNoSuchInvite = errors.New("no such invite") + ErrInsufficientPermissions = errors.New("insufficient permissions") + ErrIsOwner = errors.New("can't be made by owner") + ErrIncorrectNumberOfAccounts = errors.New("incorrect number of accounts") + ErrDuplicateAccounts = errors.New("duplicate accounts") + ErrNoReadKey = errors.New("acl state doesn't have a read key") + ErrIncorrectReadKey = errors.New("incorrect read key") + ErrInvalidSignature = errors.New("signature is invalid") + ErrIncorrectRoot = errors.New("incorrect root") + ErrIncorrectRecordSequence = errors.New("incorrect prev id of a record") ) type UserPermissionPair struct { @@ -36,37 +41,71 @@ type UserPermissionPair struct { type AclState struct { id string currentReadKeyId string - userReadKeys map[string]crypto.SymKey - userStates map[string]AclUserState - statesAtRecord map[string][]AclUserState - key crypto.PrivKey - pubKey crypto.PubKey - keyStore crypto.KeyStorage - totalReadKeys int + // userReadKeys is a map recordId -> read key which tells us about every read key + userReadKeys map[string]crypto.SymKey + // userStates is a map pubKey -> state which defines current user state + userStates map[string]AclUserState + // statesAtRecord is a map recordId -> state which define user state at particular record + // probably this can grow rather large at some point, so we can maybe optimise later to have: + // - map pubKey -> []recordIds (where recordIds is an array where such identity permissions were changed) + statesAtRecord map[string][]AclUserState + // inviteKeys is a map recordId -> invite + inviteKeys map[string]crypto.PubKey + // requestRecords is a map recordId -> RequestRecord + requestRecords map[string]RequestRecord + // pendingRequests is a map pubKey -> recordId + pendingRequests map[string]string + key crypto.PrivKey + pubKey crypto.PubKey + keyStore crypto.KeyStorage + totalReadKeys int - lastRecordId string + lastRecordId string + contentValidator ContentValidator } func newAclStateWithKeys( id string, key crypto.PrivKey) (*AclState, error) { - return &AclState{ - id: id, - key: key, - pubKey: key.GetPublic(), - userReadKeys: make(map[string]crypto.SymKey), - userStates: make(map[string]AclUserState), - statesAtRecord: make(map[string][]AclUserState), - }, nil + st := &AclState{ + id: id, + key: key, + pubKey: key.GetPublic(), + userReadKeys: make(map[string]crypto.SymKey), + userStates: make(map[string]AclUserState), + statesAtRecord: make(map[string][]AclUserState), + inviteKeys: make(map[string]crypto.PubKey), + requestRecords: make(map[string]RequestRecord), + pendingRequests: make(map[string]string), + keyStore: crypto.NewKeyStorage(), + } + st.contentValidator = &contentValidator{ + keyStore: st.keyStore, + aclState: st, + } + return st, nil } func newAclState(id string) *AclState { - return &AclState{ - id: id, - userReadKeys: make(map[string]crypto.SymKey), - userStates: make(map[string]AclUserState), - statesAtRecord: make(map[string][]AclUserState), + st := &AclState{ + id: id, + userReadKeys: make(map[string]crypto.SymKey), + userStates: make(map[string]AclUserState), + statesAtRecord: make(map[string][]AclUserState), + inviteKeys: make(map[string]crypto.PubKey), + requestRecords: make(map[string]RequestRecord), + pendingRequests: make(map[string]string), + keyStore: crypto.NewKeyStorage(), } + st.contentValidator = &contentValidator{ + keyStore: st.keyStore, + aclState: st, + } + return st +} + +func (st *AclState) Validator() ContentValidator { + return st.contentValidator } func (st *AclState) CurrentReadKeyId() string { @@ -74,7 +113,7 @@ func (st *AclState) CurrentReadKeyId() string { } func (st *AclState) CurrentReadKey() (crypto.SymKey, error) { - key, exists := st.userReadKeys[st.currentReadKeyId] + key, exists := st.userReadKeys[st.CurrentReadKeyId()] if !exists { return nil, ErrNoReadKey } @@ -97,7 +136,7 @@ func (st *AclState) StateAtRecord(id string, pubKey crypto.PubKey) (AclUserState return perm, nil } } - return AclUserState{}, ErrNoSuchUser + return AclUserState{}, ErrNoSuchAccount } func (st *AclState) applyRecord(record *AclRecord) (err error) { @@ -110,17 +149,18 @@ func (st *AclState) applyRecord(record *AclRecord) (err error) { err = ErrIncorrectRecordSequence return } + // if the record is root record if record.Id == st.id { err = st.applyRoot(record) if err != nil { return } st.statesAtRecord[record.Id] = []AclUserState{ - {PubKey: record.Identity, Permissions: aclrecordproto.AclUserPermissions_Admin}, + st.userStates[mapKeyFromPubKey(record.Identity)], } return } - + // if the model is not cached if record.Model == nil { aclData := &aclrecordproto.AclData{} err = proto.Unmarshal(record.Data, aclData) @@ -129,18 +169,16 @@ func (st *AclState) applyRecord(record *AclRecord) (err error) { } record.Model = aclData } - + // applying records contents err = st.applyChangeData(record) if err != nil { return } - - // getting all states for users at record + // getting all states for users at record and saving them var states []AclUserState for _, state := range st.userStates { states = append(states, state) } - st.statesAtRecord[record.Id] = states return } @@ -156,9 +194,9 @@ func (st *AclState) applyRoot(record *AclRecord) (err error) { // adding user to the list userState := AclUserState{ PubKey: record.Identity, - Permissions: aclrecordproto.AclUserPermissions_Admin, + Permissions: AclPermissions(aclrecordproto.AclUserPermissions_Owner), } - st.currentReadKeyId = record.ReadKeyId + st.currentReadKeyId = record.Id st.userStates[mapKeyFromPubKey(record.Identity)] = userState st.totalReadKeys++ return @@ -181,92 +219,191 @@ func (st *AclState) saveReadKeyFromRoot(record *AclRecord) (err error) { return } } - st.userReadKeys[record.Id] = readKey return } func (st *AclState) applyChangeData(record *AclRecord) (err error) { - defer func() { - if err != nil { - return - } - if record.ReadKeyId != st.currentReadKeyId { - st.totalReadKeys++ - st.currentReadKeyId = record.ReadKeyId - } - }() model := record.Model.(*aclrecordproto.AclData) - if !st.isUserJoin(model) { - // we check signature when we add this to the List, so no need to do it here - if _, exists := st.userStates[mapKeyFromPubKey(record.Identity)]; !exists { - err = ErrNoSuchUser - return - } - - // only Admins can do non-user join changes - if !st.HasPermission(record.Identity, aclrecordproto.AclUserPermissions_Admin) { - // TODO: add string encoding - err = fmt.Errorf("user %s must have admin permissions", record.Identity.Account()) - return - } - } - for _, ch := range model.GetAclContent() { - if err = st.applyChangeContent(ch, record.Id); err != nil { + if err = st.applyChangeContent(ch, record.Id, record.Identity); err != nil { log.Info("error while applying changes: %v; ignore", zap.Error(err)) return err } } - return nil } -func (st *AclState) applyChangeContent(ch *aclrecordproto.AclContentValue, recordId string) error { +func (st *AclState) applyChangeContent(ch *aclrecordproto.AclContentValue, recordId string, authorIdentity crypto.PubKey) error { switch { - case ch.GetUserPermissionChange() != nil: - return st.applyUserPermissionChange(ch.GetUserPermissionChange(), recordId) - case ch.GetUserAdd() != nil: - return st.applyUserAdd(ch.GetUserAdd(), recordId) - case ch.GetUserRemove() != nil: - return st.applyUserRemove(ch.GetUserRemove(), recordId) - case ch.GetUserInvite() != nil: - return st.applyUserInvite(ch.GetUserInvite(), recordId) - case ch.GetUserJoin() != nil: - return st.applyUserJoin(ch.GetUserJoin(), recordId) + case ch.GetPermissionChange() != nil: + return st.applyPermissionChange(ch.GetPermissionChange(), recordId, authorIdentity) + case ch.GetInvite() != nil: + return st.applyInvite(ch.GetInvite(), recordId, authorIdentity) + case ch.GetInviteRevoke() != nil: + return st.applyInviteRevoke(ch.GetInviteRevoke(), recordId, authorIdentity) + case ch.GetRequestJoin() != nil: + return st.applyRequestJoin(ch.GetRequestJoin(), recordId, authorIdentity) + case ch.GetRequestAccept() != nil: + return st.applyRequestAccept(ch.GetRequestAccept(), recordId, authorIdentity) + case ch.GetRequestDecline() != nil: + return st.applyRequestDecline(ch.GetRequestDecline(), recordId, authorIdentity) + case ch.GetAccountRemove() != nil: + return st.applyAccountRemove(ch.GetAccountRemove(), recordId, authorIdentity) + case ch.GetReadKeyChange() != nil: + return st.applyReadKeyChange(ch.GetReadKeyChange(), recordId, authorIdentity) + case ch.GetAccountRequestRemove() != nil: + return st.applyRequestRemove(ch.GetAccountRequestRemove(), recordId, authorIdentity) default: - return fmt.Errorf("unexpected change type: %v", ch) + return ErrUnexpectedContentType } } -func (st *AclState) applyUserPermissionChange(ch *aclrecordproto.AclUserPermissionChange, recordId string) error { +func (st *AclState) applyPermissionChange(ch *aclrecordproto.AclAccountPermissionChange, recordId string, authorIdentity crypto.PubKey) error { chIdentity, err := st.keyStore.PubKeyFromProto(ch.Identity) if err != nil { return err } - state, exists := st.userStates[mapKeyFromPubKey(chIdentity)] - if !exists { - return ErrNoSuchUser + err = st.contentValidator.ValidatePermissionChange(ch, authorIdentity) + if err != nil { + return err } - - state.Permissions = ch.Permissions + stringKey := mapKeyFromPubKey(chIdentity) + state, _ := st.userStates[stringKey] + state.Permissions = AclPermissions(ch.Permissions) + st.userStates[stringKey] = state return nil } -func (st *AclState) applyUserInvite(ch *aclrecordproto.AclUserInvite, recordId string) error { - // TODO: check old code and bring it back :-) +func (st *AclState) applyInvite(ch *aclrecordproto.AclAccountInvite, recordId string, authorIdentity crypto.PubKey) error { + inviteKey, err := st.keyStore.PubKeyFromProto(ch.InviteKey) + if err != nil { + return err + } + err = st.contentValidator.ValidateInvite(ch, authorIdentity) + if err != nil { + return err + } + st.inviteKeys[recordId] = inviteKey return nil } -func (st *AclState) applyUserJoin(ch *aclrecordproto.AclUserJoin, recordId string) error { +func (st *AclState) applyInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, recordId string, authorIdentity crypto.PubKey) error { + err := st.contentValidator.ValidateInviteRevoke(ch, authorIdentity) + if err != nil { + return err + } + delete(st.inviteKeys, ch.InviteRecordId) return nil } -func (st *AclState) applyUserAdd(ch *aclrecordproto.AclUserAdd, recordId string) error { +func (st *AclState) applyRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, recordId string, authorIdentity crypto.PubKey) error { + err := st.contentValidator.ValidateRequestJoin(ch, authorIdentity) + if err != nil { + return err + } + st.pendingRequests[mapKeyFromPubKey(authorIdentity)] = recordId + st.requestRecords[recordId] = RequestRecord{ + RequestIdentity: authorIdentity, + RequestMetadata: ch.Metadata, + Type: RequestTypeJoin, + } return nil } -func (st *AclState) applyUserRemove(ch *aclrecordproto.AclUserRemove, recordId string) error { +func (st *AclState) applyRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, recordId string, authorIdentity crypto.PubKey) error { + err := st.contentValidator.ValidateRequestAccept(ch, authorIdentity) + if err != nil { + return err + } + acceptIdentity, err := st.keyStore.PubKeyFromProto(ch.Identity) + if err != nil { + return err + } + record, _ := st.requestRecords[ch.RequestRecordId] + st.userStates[mapKeyFromPubKey(acceptIdentity)] = AclUserState{ + PubKey: acceptIdentity, + Permissions: AclPermissions(ch.Permissions), + RequestMetadata: record.RequestMetadata, + } + delete(st.pendingRequests, mapKeyFromPubKey(st.requestRecords[ch.RequestRecordId].RequestIdentity)) + if !st.pubKey.Equals(acceptIdentity) { + return nil + } + for _, key := range ch.EncryptedReadKeys { + decrypted, err := st.key.Decrypt(key.EncryptedReadKey) + if err != nil { + return err + } + sym, err := crypto.UnmarshallAESKey(decrypted) + if err != nil { + return err + } + st.userReadKeys[key.RecordId] = sym + } + return nil +} + +func (st *AclState) applyRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, recordId string, authorIdentity crypto.PubKey) error { + err := st.contentValidator.ValidateRequestDecline(ch, authorIdentity) + if err != nil { + return err + } + delete(st.pendingRequests, mapKeyFromPubKey(st.requestRecords[ch.RequestRecordId].RequestIdentity)) + delete(st.requestRecords, ch.RequestRecordId) + return nil +} + +func (st *AclState) applyRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, recordId string, authorIdentity crypto.PubKey) error { + err := st.contentValidator.ValidateRequestRemove(ch, authorIdentity) + if err != nil { + return err + } + st.requestRecords[recordId] = RequestRecord{ + RequestIdentity: authorIdentity, + Type: RequestTypeRemove, + } + st.pendingRequests[mapKeyFromPubKey(authorIdentity)] = recordId + return nil +} + +func (st *AclState) applyAccountRemove(ch *aclrecordproto.AclAccountRemove, recordId string, authorIdentity crypto.PubKey) error { + err := st.contentValidator.ValidateAccountRemove(ch, authorIdentity) + if err != nil { + return err + } + for _, rawIdentity := range ch.Identities { + identity, err := st.keyStore.PubKeyFromProto(rawIdentity) + if err != nil { + return err + } + idKey := mapKeyFromPubKey(identity) + delete(st.userStates, idKey) + delete(st.pendingRequests, idKey) + } + return st.updateReadKey(ch.AccountKeys, recordId) +} + +func (st *AclState) applyReadKeyChange(ch *aclrecordproto.AclReadKeyChange, recordId string, authorIdentity crypto.PubKey) error { + err := st.contentValidator.ValidateReadKeyChange(ch, authorIdentity) + if err != nil { + return err + } + return st.updateReadKey(ch.AccountKeys, recordId) +} + +func (st *AclState) updateReadKey(keys []*aclrecordproto.AclEncryptedReadKey, recordId string) error { + for _, accKey := range keys { + identity, _ := st.keyStore.PubKeyFromProto(accKey.Identity) + if st.pubKey.Equals(identity) { + res, err := st.decryptReadKey(accKey.EncryptedReadKey) + if err != nil { + return err + } + st.userReadKeys[recordId] = res + } + } + st.currentReadKeyId = recordId return nil } @@ -275,7 +412,6 @@ func (st *AclState) decryptReadKey(msg []byte) (crypto.SymKey, error) { if err != nil { return nil, ErrFailedToDecrypt } - key, err := crypto.UnmarshallAESKey(decrypted) if err != nil { return nil, ErrFailedToDecrypt @@ -283,29 +419,31 @@ func (st *AclState) decryptReadKey(msg []byte) (crypto.SymKey, error) { return key, nil } -func (st *AclState) HasPermission(identity crypto.PubKey, permission aclrecordproto.AclUserPermissions) bool { +func (st *AclState) Permissions(identity crypto.PubKey) AclPermissions { state, exists := st.userStates[mapKeyFromPubKey(identity)] if !exists { - return false + return AclPermissions(aclrecordproto.AclUserPermissions_None) } - - return state.Permissions == permission + return state.Permissions } -func (st *AclState) isUserJoin(data *aclrecordproto.AclData) bool { - // if we have a UserJoin, then it should always be the first one applied - return data.GetAclContent() != nil && data.GetAclContent()[0].GetUserJoin() != nil +func (st *AclState) JoinRecords() (records []RequestRecord) { + for _, recId := range st.pendingRequests { + rec := st.requestRecords[recId] + if rec.Type == RequestTypeJoin { + records = append(records, rec) + } + } + return } -func (st *AclState) isUserAdd(data *aclrecordproto.AclData, identity []byte) bool { - return false -} - -func (st *AclState) UserStates() map[string]AclUserState { - return st.userStates -} - -func (st *AclState) Invite(acceptPubKey []byte) (invite *aclrecordproto.AclUserInvite, err error) { +func (st *AclState) RemoveRecords() (records []RequestRecord) { + for _, recId := range st.pendingRequests { + rec := st.requestRecords[recId] + if rec.Type == RequestTypeRemove { + records = append(records, rec) + } + } return } diff --git a/commonspace/object/acl/list/list.go b/commonspace/object/acl/list/list.go index 6c31c9de..a2177ea4 100644 --- a/commonspace/object/acl/list/list.go +++ b/commonspace/object/acl/list/list.go @@ -5,16 +5,21 @@ import ( "context" "errors" "fmt" - "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/liststorage" - "github.com/anyproto/any-sync/util/crypto" "sync" + + "github.com/anyproto/any-sync/commonspace/object/accountdata" + "github.com/anyproto/any-sync/commonspace/object/acl/liststorage" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/anyproto/any-sync/util/cidutil" + "github.com/anyproto/any-sync/util/crypto" ) type IterFunc = func(record *AclRecord) (IsContinue bool) -var ErrIncorrectCID = errors.New("incorrect CID") +var ( + ErrIncorrectCID = errors.New("incorrect CID") + ErrRecordAlreadyExists = errors.New("record already exists") +) type RWLocker interface { sync.Locker @@ -22,26 +27,45 @@ 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 - Root() *aclrecordproto.RawAclRecordWithId + Root() *consensusproto.RawRecordWithId Records() []*AclRecord AclState() *AclState IsAfter(first string, second string) (bool, error) + HasHead(head string) bool Head() *AclRecord + + RecordsAfter(ctx context.Context, id string) (records []*consensusproto.RawRecordWithId, err error) Get(id string) (*AclRecord, error) + GetIndex(idx int) (*AclRecord, error) Iterate(iterFunc IterFunc) IterateFrom(startId string, iterFunc IterFunc) + KeyStorage() crypto.KeyStorage + RecordBuilder() AclRecordBuilder - AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added bool, err error) + ValidateRawRecord(record *consensusproto.RawRecord) (err error) + AddRawRecord(rawRec *consensusproto.RawRecordWithId) (err error) + AddRawRecords(rawRecords []*consensusproto.RawRecordWithId) (err error) - Close() (err error) + Close(ctx context.Context) (err error) } type aclList struct { - root *aclrecordproto.RawAclRecordWithId + root *consensusproto.RawRecordWithId records []*AclRecord indexes map[string]int id string @@ -55,18 +79,45 @@ type aclList struct { sync.RWMutex } -func BuildAclListWithIdentity(acc *accountdata.AccountKeys, storage liststorage.ListStorage) (AclList, error) { - builder := newAclStateBuilderWithIdentity(acc) - keyStorage := crypto.NewKeyStorage() - return build(storage.Id(), keyStorage, builder, NewAclRecordBuilder(storage.Id(), keyStorage), storage) +type internalDeps struct { + storage liststorage.ListStorage + keyStorage crypto.KeyStorage + stateBuilder *aclStateBuilder + recordBuilder AclRecordBuilder + acceptorVerifier AcceptorVerifier } -func BuildAclList(storage liststorage.ListStorage) (AclList, error) { +func BuildAclListWithIdentity(acc *accountdata.AccountKeys, storage liststorage.ListStorage, verifier AcceptorVerifier) (AclList, error) { keyStorage := crypto.NewKeyStorage() - return build(storage.Id(), keyStorage, newAclStateBuilder(), NewAclRecordBuilder(storage.Id(), crypto.NewKeyStorage()), storage) + deps := internalDeps{ + storage: storage, + keyStorage: keyStorage, + stateBuilder: newAclStateBuilderWithIdentity(acc), + recordBuilder: NewAclRecordBuilder(storage.Id(), keyStorage, acc, verifier), + acceptorVerifier: verifier, + } + return build(deps) } -func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilder, recBuilder AclRecordBuilder, storage liststorage.ListStorage) (list AclList, err error) { +func BuildAclList(storage liststorage.ListStorage, verifier AcceptorVerifier) (AclList, error) { + keyStorage := crypto.NewKeyStorage() + deps := internalDeps{ + storage: storage, + keyStorage: keyStorage, + stateBuilder: newAclStateBuilder(), + recordBuilder: NewAclRecordBuilder(storage.Id(), keyStorage, nil, verifier), + acceptorVerifier: verifier, + } + return build(deps) +} + +func build(deps internalDeps) (list AclList, err error) { + var ( + storage = deps.storage + id = deps.storage.Id() + recBuilder = deps.recordBuilder + stateBuilder = deps.stateBuilder + ) head, err := storage.Head() if err != nil { return @@ -77,7 +128,7 @@ func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilde return } - record, err := recBuilder.Unmarshall(rawRecordWithId) + record, err := recBuilder.UnmarshallWithId(rawRecordWithId) if err != nil { return } @@ -89,7 +140,7 @@ func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilde return } - record, err = recBuilder.Unmarshall(rawRecordWithId) + record, err = recBuilder.UnmarshallWithId(rawRecordWithId) if err != nil { return } @@ -119,6 +170,7 @@ func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilde return } + recBuilder.(*aclRecordBuilder).state = state list = &aclList{ root: rootWithId, records: records, @@ -132,15 +184,37 @@ func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilde return } +func (a *aclList) RecordBuilder() AclRecordBuilder { + return a.recordBuilder +} + func (a *aclList) Records() []*AclRecord { return a.records } -func (a *aclList) AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added bool, err error) { - if _, ok := a.indexes[rawRec.Id]; ok { +func (a *aclList) ValidateRawRecord(rawRec *consensusproto.RawRecord) (err error) { + record, err := a.recordBuilder.Unmarshall(rawRec) + if err != nil { return } - record, err := a.recordBuilder.Unmarshall(rawRec) + return a.aclState.Validator().ValidateAclRecordContents(record) +} + +func (a *aclList) AddRawRecords(rawRecords []*consensusproto.RawRecordWithId) (err error) { + for _, rec := range rawRecords { + err = a.AddRawRecord(rec) + if err != nil && err != ErrRecordAlreadyExists { + return + } + } + return +} + +func (a *aclList) AddRawRecord(rawRec *consensusproto.RawRecordWithId) (err error) { + if _, ok := a.indexes[rawRec.Id]; ok { + return ErrRecordAlreadyExists + } + record, err := a.recordBuilder.UnmarshallWithId(rawRec) if err != nil { return } @@ -155,15 +229,6 @@ func (a *aclList) AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added if err = a.storage.SetHead(rawRec.Id); err != nil { return } - return true, nil -} - -func (a *aclList) IsValidNext(rawRec *aclrecordproto.RawAclRecordWithId) (err error) { - _, err = a.recordBuilder.Unmarshall(rawRec) - if err != nil { - return - } - // TODO: change state and add "check" method for records return } @@ -171,7 +236,7 @@ func (a *aclList) Id() string { return a.id } -func (a *aclList) Root() *aclrecordproto.RawAclRecordWithId { +func (a *aclList) Root() *consensusproto.RawRecordWithId { return a.root } @@ -196,14 +261,27 @@ func (a *aclList) Head() *AclRecord { return a.records[len(a.records)-1] } +func (a *aclList) HasHead(head string) bool { + _, exists := a.indexes[head] + return exists +} + func (a *aclList) Get(id string) (*AclRecord, error) { recIdx, ok := a.indexes[id] if !ok { - return nil, fmt.Errorf("no such record") + return nil, ErrNoSuchRecord } return a.records[recIdx], nil } +func (a *aclList) GetIndex(idx int) (*AclRecord, error) { + // TODO: when we add snapshots we will have to monitor record num in snapshots + if idx < 0 || idx >= len(a.records) { + return nil, ErrNoSuchRecord + } + return a.records[idx], nil +} + func (a *aclList) Iterate(iterFunc IterFunc) { for _, rec := range a.records { if !iterFunc(rec) { @@ -212,6 +290,21 @@ func (a *aclList) Iterate(iterFunc IterFunc) { } } +func (a *aclList) RecordsAfter(ctx context.Context, id string) (records []*consensusproto.RawRecordWithId, err error) { + recIdx, ok := a.indexes[id] + if !ok { + return nil, ErrNoSuchRecord + } + for i := recIdx + 1; i < len(a.records); i++ { + rawRec, err := a.storage.GetRawRecord(ctx, a.records[i].Id) + if err != nil { + return nil, err + } + records = append(records, rawRec) + } + return +} + func (a *aclList) IterateFrom(startId string, iterFunc IterFunc) { recIdx, ok := a.indexes[startId] if !ok { @@ -224,6 +317,21 @@ func (a *aclList) IterateFrom(startId string, iterFunc IterFunc) { } } -func (a *aclList) Close() (err error) { +func (a *aclList) Close(ctx context.Context) (err error) { return nil } + +func WrapAclRecord(rawRec *consensusproto.RawRecord) *consensusproto.RawRecordWithId { + payload, err := rawRec.Marshal() + if err != nil { + panic(err) + } + id, err := cidutil.NewCidFromBytes(payload) + if err != nil { + panic(err) + } + return &consensusproto.RawRecordWithId{ + Payload: payload, + Id: id, + } +} diff --git a/commonspace/object/acl/list/list_test.go b/commonspace/object/acl/list/list_test.go index 19229ace..6ce4ea86 100644 --- a/commonspace/object/acl/list/list_test.go +++ b/commonspace/object/acl/list/list_test.go @@ -2,11 +2,98 @@ package list import ( "fmt" - "github.com/anyproto/any-sync/commonspace/object/accountdata" - "github.com/stretchr/testify/require" "testing" + + "github.com/anyproto/any-sync/commonspace/object/accountdata" + "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/anyproto/any-sync/util/crypto" + "github.com/stretchr/testify/require" ) +type aclFixture struct { + ownerKeys *accountdata.AccountKeys + accountKeys *accountdata.AccountKeys + ownerAcl *aclList + accountAcl *aclList + spaceId string +} + +func newFixture(t *testing.T) *aclFixture { + ownerKeys, err := accountdata.NewRandom() + require.NoError(t, err) + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + spaceId := "spaceId" + ownerAcl, err := NewTestDerivedAcl(spaceId, ownerKeys) + require.NoError(t, err) + accountAcl, err := NewTestAclWithRoot(accountKeys, ownerAcl.Root()) + require.NoError(t, err) + return &aclFixture{ + ownerKeys: ownerKeys, + accountKeys: accountKeys, + ownerAcl: ownerAcl.(*aclList), + accountAcl: accountAcl.(*aclList), + spaceId: spaceId, + } +} + +func (fx *aclFixture) addRec(t *testing.T, rec *consensusproto.RawRecordWithId) { + err := fx.ownerAcl.AddRawRecord(rec) + require.NoError(t, err) + err = fx.accountAcl.AddRawRecord(rec) + require.NoError(t, err) +} + +func (fx *aclFixture) inviteAccount(t *testing.T, perms AclPermissions) { + var ( + ownerAcl = fx.ownerAcl + ownerState = fx.ownerAcl.aclState + accountAcl = fx.accountAcl + accountState = fx.accountAcl.aclState + ) + // building invite + inv, err := ownerAcl.RecordBuilder().BuildInvite() + require.NoError(t, err) + inviteRec := WrapAclRecord(inv.InviteRec) + fx.addRec(t, inviteRec) + + // building request join + requestJoin, err := accountAcl.RecordBuilder().BuildRequestJoin(RequestJoinPayload{ + InviteRecordId: inviteRec.Id, + InviteKey: inv.InviteKey, + }) + require.NoError(t, err) + requestJoinRec := WrapAclRecord(requestJoin) + fx.addRec(t, requestJoinRec) + + // building request accept + requestAccept, err := ownerAcl.RecordBuilder().BuildRequestAccept(RequestAcceptPayload{ + RequestRecordId: requestJoinRec.Id, + Permissions: perms, + }) + require.NoError(t, err) + // validate + err = ownerAcl.ValidateRawRecord(requestAccept) + require.NoError(t, err) + requestAcceptRec := WrapAclRecord(requestAccept) + fx.addRec(t, requestAcceptRec) + + // checking acl state + require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, ownerState.Permissions(accountState.pubKey).CanWrite()) + require.Equal(t, 0, len(ownerState.pendingRequests)) + require.Equal(t, 0, len(accountState.pendingRequests)) + require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, accountState.Permissions(accountState.pubKey).CanWrite()) + + _, err = ownerState.StateAtRecord(requestJoinRec.Id, accountState.pubKey) + require.Equal(t, ErrNoSuchAccount, err) + stateAtRec, err := ownerState.StateAtRecord(requestAcceptRec.Id, accountState.pubKey) + require.NoError(t, err) + require.True(t, stateAtRec.Permissions == perms) +} + func TestAclList_BuildRoot(t *testing.T) { randomKeys, err := accountdata.NewRandom() require.NoError(t, err) @@ -14,3 +101,193 @@ func TestAclList_BuildRoot(t *testing.T) { require.NoError(t, err) fmt.Println(randomAcl.Id()) } + +func TestAclList_InvitePipeline(t *testing.T) { + fx := newFixture(t) + fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Writer)) +} + +func TestAclList_InviteRevoke(t *testing.T) { + fx := newFixture(t) + var ( + ownerState = fx.ownerAcl.aclState + accountState = fx.accountAcl.aclState + ) + // building invite + inv, err := fx.ownerAcl.RecordBuilder().BuildInvite() + require.NoError(t, err) + inviteRec := WrapAclRecord(inv.InviteRec) + fx.addRec(t, inviteRec) + + // building invite revoke + inviteRevoke, err := fx.ownerAcl.RecordBuilder().BuildInviteRevoke(ownerState.lastRecordId) + require.NoError(t, err) + inviteRevokeRec := WrapAclRecord(inviteRevoke) + fx.addRec(t, inviteRevokeRec) + + // 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) +} + +func TestAclList_RequestDecline(t *testing.T) { + fx := newFixture(t) + var ( + ownerAcl = fx.ownerAcl + ownerState = fx.ownerAcl.aclState + accountAcl = fx.accountAcl + accountState = fx.accountAcl.aclState + ) + // building invite + inv, err := ownerAcl.RecordBuilder().BuildInvite() + require.NoError(t, err) + inviteRec := WrapAclRecord(inv.InviteRec) + fx.addRec(t, inviteRec) + + // building request join + requestJoin, err := accountAcl.RecordBuilder().BuildRequestJoin(RequestJoinPayload{ + InviteRecordId: inviteRec.Id, + InviteKey: inv.InviteKey, + }) + require.NoError(t, err) + requestJoinRec := WrapAclRecord(requestJoin) + fx.addRec(t, requestJoinRec) + + // building request decline + requestDecline, err := ownerAcl.RecordBuilder().BuildRequestDecline(ownerState.lastRecordId) + require.NoError(t, err) + requestDeclineRec := WrapAclRecord(requestDecline) + fx.addRec(t, requestDeclineRec) + + // checking acl state + require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, ownerState.Permissions(accountState.pubKey).NoPermissions()) + require.Empty(t, ownerState.pendingRequests) + require.Empty(t, accountState.pendingRequests) +} + +func TestAclList_Remove(t *testing.T) { + fx := newFixture(t) + var ( + ownerState = fx.ownerAcl.aclState + accountState = fx.accountAcl.aclState + ) + fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Writer)) + + newReadKey := crypto.NewAES() + remove, err := fx.ownerAcl.RecordBuilder().BuildAccountRemove(AccountRemovePayload{ + Identities: []crypto.PubKey{fx.accountKeys.SignKey.GetPublic()}, + ReadKey: newReadKey, + }) + require.NoError(t, err) + removeRec := WrapAclRecord(remove) + fx.addRec(t, removeRec) + + // checking acl state + require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, ownerState.Permissions(accountState.pubKey).NoPermissions()) + require.True(t, ownerState.userReadKeys[removeRec.Id].Equals(newReadKey)) + require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()]) + require.Equal(t, 0, len(ownerState.pendingRequests)) + require.Equal(t, 0, len(accountState.pendingRequests)) + require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, accountState.Permissions(accountState.pubKey).NoPermissions()) + require.Nil(t, accountState.userReadKeys[removeRec.Id]) + require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()]) +} + +func TestAclList_ReadKeyChange(t *testing.T) { + fx := newFixture(t) + var ( + ownerState = fx.ownerAcl.aclState + accountState = fx.accountAcl.aclState + ) + fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Admin)) + + newReadKey := crypto.NewAES() + readKeyChange, err := fx.ownerAcl.RecordBuilder().BuildReadKeyChange(newReadKey) + require.NoError(t, err) + readKeyRec := WrapAclRecord(readKeyChange) + fx.addRec(t, readKeyRec) + + // checking acl state + require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, ownerState.Permissions(accountState.pubKey).CanManageAccounts()) + require.True(t, ownerState.userReadKeys[readKeyRec.Id].Equals(newReadKey)) + require.True(t, accountState.userReadKeys[readKeyRec.Id].Equals(newReadKey)) + require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()]) + require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()]) + readKey, err := ownerState.CurrentReadKey() + require.NoError(t, err) + require.True(t, newReadKey.Equals(readKey)) + require.Equal(t, 0, len(ownerState.pendingRequests)) + require.Equal(t, 0, len(accountState.pendingRequests)) +} + +func TestAclList_PermissionChange(t *testing.T) { + fx := newFixture(t) + var ( + ownerState = fx.ownerAcl.aclState + accountState = fx.accountAcl.aclState + ) + fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Admin)) + + permissionChange, err := fx.ownerAcl.RecordBuilder().BuildPermissionChange(PermissionChangePayload{ + Identity: fx.accountKeys.SignKey.GetPublic(), + Permissions: AclPermissions(aclrecordproto.AclUserPermissions_Writer), + }) + require.NoError(t, err) + permissionChangeRec := WrapAclRecord(permissionChange) + fx.addRec(t, permissionChangeRec) + + // checking acl state + require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, ownerState.Permissions(accountState.pubKey) == AclPermissions(aclrecordproto.AclUserPermissions_Writer)) + require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, accountState.Permissions(accountState.pubKey) == AclPermissions(aclrecordproto.AclUserPermissions_Writer)) + require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()]) + require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()]) + require.Equal(t, 0, len(ownerState.pendingRequests)) + require.Equal(t, 0, len(accountState.pendingRequests)) +} + +func TestAclList_RequestRemove(t *testing.T) { + fx := newFixture(t) + var ( + ownerState = fx.ownerAcl.aclState + accountState = fx.accountAcl.aclState + ) + fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Writer)) + + removeRequest, err := fx.accountAcl.RecordBuilder().BuildRequestRemove() + require.NoError(t, err) + removeRequestRec := WrapAclRecord(removeRequest) + fx.addRec(t, removeRequestRec) + + recs := fx.accountAcl.AclState().RemoveRecords() + require.Len(t, recs, 1) + require.True(t, accountState.pubKey.Equals(recs[0].RequestIdentity)) + + newReadKey := crypto.NewAES() + remove, err := fx.ownerAcl.RecordBuilder().BuildAccountRemove(AccountRemovePayload{ + Identities: []crypto.PubKey{recs[0].RequestIdentity}, + ReadKey: newReadKey, + }) + require.NoError(t, err) + removeRec := WrapAclRecord(remove) + fx.addRec(t, removeRec) + + // checking acl state + require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, ownerState.Permissions(accountState.pubKey).NoPermissions()) + require.True(t, ownerState.userReadKeys[removeRec.Id].Equals(newReadKey)) + require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()]) + require.Equal(t, 0, len(ownerState.pendingRequests)) + require.Equal(t, 0, len(accountState.pendingRequests)) + require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner()) + require.True(t, accountState.Permissions(accountState.pubKey).NoPermissions()) + require.Nil(t, accountState.userReadKeys[removeRec.Id]) + require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()]) +} diff --git a/commonspace/object/acl/list/listutils.go b/commonspace/object/acl/list/listutils.go index 59e87888..d512f748 100644 --- a/commonspace/object/acl/list/listutils.go +++ b/commonspace/object/acl/list/listutils.go @@ -2,13 +2,13 @@ package list 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/liststorage" + "github.com/anyproto/any-sync/consensus/consensusproto" "github.com/anyproto/any-sync/util/crypto" ) func NewTestDerivedAcl(spaceId string, keys *accountdata.AccountKeys) (AclList, error) { - builder := NewAclRecordBuilder("", crypto.NewKeyStorage()) + builder := NewAclRecordBuilder("", crypto.NewKeyStorage(), keys, NoOpAcceptorVerifier{}) masterKey, _, err := crypto.GenerateRandomEd25519KeyPair() if err != nil { return nil, err @@ -21,11 +21,21 @@ func NewTestDerivedAcl(spaceId string, keys *accountdata.AccountKeys) (AclList, if err != nil { return nil, err } - st, err := liststorage.NewInMemoryAclListStorage(root.Id, []*aclrecordproto.RawAclRecordWithId{ + st, err := liststorage.NewInMemoryAclListStorage(root.Id, []*consensusproto.RawRecordWithId{ root, }) if err != nil { return nil, err } - return BuildAclListWithIdentity(keys, st) + return BuildAclListWithIdentity(keys, st, NoOpAcceptorVerifier{}) +} + +func NewTestAclWithRoot(keys *accountdata.AccountKeys, root *consensusproto.RawRecordWithId) (AclList, error) { + st, err := liststorage.NewInMemoryAclListStorage(root.Id, []*consensusproto.RawRecordWithId{ + root, + }) + if err != nil { + return nil, err + } + return BuildAclListWithIdentity(keys, st, NoOpAcceptorVerifier{}) } diff --git a/commonspace/object/acl/list/mock_list/mock_list.go b/commonspace/object/acl/list/mock_list/mock_list.go index b81f0f80..6cdccac9 100644 --- a/commonspace/object/acl/list/mock_list/mock_list.go +++ b/commonspace/object/acl/list/mock_list/mock_list.go @@ -5,12 +5,13 @@ package mock_list import ( + context "context" reflect "reflect" - aclrecordproto "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" list "github.com/anyproto/any-sync/commonspace/object/acl/list" + consensusproto "github.com/anyproto/any-sync/consensus/consensusproto" crypto "github.com/anyproto/any-sync/util/crypto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockAclList is a mock of AclList interface. @@ -51,12 +52,11 @@ func (mr *MockAclListMockRecorder) AclState() *gomock.Call { } // AddRawRecord mocks base method. -func (m *MockAclList) AddRawRecord(arg0 *aclrecordproto.RawAclRecordWithId) (bool, error) { +func (m *MockAclList) AddRawRecord(arg0 *consensusproto.RawRecordWithId) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddRawRecord", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret0, _ := ret[0].(error) + return ret0 } // AddRawRecord indicates an expected call of AddRawRecord. @@ -65,18 +65,32 @@ func (mr *MockAclListMockRecorder) AddRawRecord(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawRecord", reflect.TypeOf((*MockAclList)(nil).AddRawRecord), arg0) } -// Close mocks base method. -func (m *MockAclList) Close() error { +// AddRawRecords mocks base method. +func (m *MockAclList) AddRawRecords(arg0 []*consensusproto.RawRecordWithId) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") + ret := m.ctrl.Call(m, "AddRawRecords", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddRawRecords indicates an expected call of AddRawRecords. +func (mr *MockAclListMockRecorder) AddRawRecords(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawRecords", reflect.TypeOf((*MockAclList)(nil).AddRawRecords), arg0) +} + +// Close mocks base method. +func (m *MockAclList) Close(arg0 context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close", arg0) ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. -func (mr *MockAclListMockRecorder) Close() *gomock.Call { +func (mr *MockAclListMockRecorder) Close(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockAclList)(nil).Close)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockAclList)(nil).Close), arg0) } // Get mocks base method. @@ -94,6 +108,35 @@ func (mr *MockAclListMockRecorder) Get(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockAclList)(nil).Get), arg0) } +// GetIndex mocks base method. +func (m *MockAclList) GetIndex(arg0 int) (*list.AclRecord, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIndex", arg0) + ret0, _ := ret[0].(*list.AclRecord) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetIndex indicates an expected call of GetIndex. +func (mr *MockAclListMockRecorder) GetIndex(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIndex", reflect.TypeOf((*MockAclList)(nil).GetIndex), arg0) +} + +// HasHead mocks base method. +func (m *MockAclList) HasHead(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasHead", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasHead indicates an expected call of HasHead. +func (mr *MockAclListMockRecorder) HasHead(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasHead", reflect.TypeOf((*MockAclList)(nil).HasHead), arg0) +} + // Head mocks base method. func (m *MockAclList) Head() *list.AclRecord { m.ctrl.T.Helper() @@ -211,6 +254,20 @@ func (mr *MockAclListMockRecorder) RUnlock() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RUnlock", reflect.TypeOf((*MockAclList)(nil).RUnlock)) } +// RecordBuilder mocks base method. +func (m *MockAclList) RecordBuilder() list.AclRecordBuilder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecordBuilder") + ret0, _ := ret[0].(list.AclRecordBuilder) + return ret0 +} + +// RecordBuilder indicates an expected call of RecordBuilder. +func (mr *MockAclListMockRecorder) RecordBuilder() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordBuilder", reflect.TypeOf((*MockAclList)(nil).RecordBuilder)) +} + // Records mocks base method. func (m *MockAclList) Records() []*list.AclRecord { m.ctrl.T.Helper() @@ -225,11 +282,26 @@ func (mr *MockAclListMockRecorder) Records() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Records", reflect.TypeOf((*MockAclList)(nil).Records)) } +// RecordsAfter mocks base method. +func (m *MockAclList) RecordsAfter(arg0 context.Context, arg1 string) ([]*consensusproto.RawRecordWithId, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecordsAfter", arg0, arg1) + ret0, _ := ret[0].([]*consensusproto.RawRecordWithId) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RecordsAfter indicates an expected call of RecordsAfter. +func (mr *MockAclListMockRecorder) RecordsAfter(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordsAfter", reflect.TypeOf((*MockAclList)(nil).RecordsAfter), arg0, arg1) +} + // Root mocks base method. -func (m *MockAclList) Root() *aclrecordproto.RawAclRecordWithId { +func (m *MockAclList) Root() *consensusproto.RawRecordWithId { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Root") - ret0, _ := ret[0].(*aclrecordproto.RawAclRecordWithId) + ret0, _ := ret[0].(*consensusproto.RawRecordWithId) return ret0 } @@ -250,3 +322,17 @@ func (mr *MockAclListMockRecorder) Unlock() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unlock", reflect.TypeOf((*MockAclList)(nil).Unlock)) } + +// ValidateRawRecord mocks base method. +func (m *MockAclList) ValidateRawRecord(arg0 *consensusproto.RawRecord) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateRawRecord", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateRawRecord indicates an expected call of ValidateRawRecord. +func (mr *MockAclListMockRecorder) ValidateRawRecord(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRawRecord", reflect.TypeOf((*MockAclList)(nil).ValidateRawRecord), arg0) +} diff --git a/commonspace/object/acl/list/models.go b/commonspace/object/acl/list/models.go index 7375c7c6..1f958303 100644 --- a/commonspace/object/acl/list/models.go +++ b/commonspace/object/acl/list/models.go @@ -8,7 +8,6 @@ import ( type AclRecord struct { Id string PrevId string - ReadKeyId string Timestamp int64 Data []byte Identity crypto.PubKey @@ -16,7 +15,55 @@ type AclRecord struct { Signature []byte } -type AclUserState struct { - PubKey crypto.PubKey - Permissions aclrecordproto.AclUserPermissions +type RequestRecord struct { + RequestIdentity crypto.PubKey + RequestMetadata []byte + Type RequestType +} + +type AclUserState struct { + PubKey crypto.PubKey + Permissions AclPermissions + RequestMetadata []byte +} + +type RequestType int + +const ( + RequestTypeRemove RequestType = iota + RequestTypeJoin +) + +type AclPermissions aclrecordproto.AclUserPermissions + +func (p AclPermissions) NoPermissions() bool { + return aclrecordproto.AclUserPermissions(p) == aclrecordproto.AclUserPermissions_None +} + +func (p AclPermissions) IsOwner() bool { + return aclrecordproto.AclUserPermissions(p) == aclrecordproto.AclUserPermissions_Owner +} + +func (p AclPermissions) CanWrite() bool { + switch aclrecordproto.AclUserPermissions(p) { + case aclrecordproto.AclUserPermissions_Admin: + return true + case aclrecordproto.AclUserPermissions_Writer: + return true + case aclrecordproto.AclUserPermissions_Owner: + return true + default: + return false + } +} + +func (p AclPermissions) CanManageAccounts() bool { + switch aclrecordproto.AclUserPermissions(p) { + case aclrecordproto.AclUserPermissions_Admin: + return true + case aclrecordproto.AclUserPermissions_Owner: + return true + default: + return false + } } diff --git a/commonspace/object/acl/list/validator.go b/commonspace/object/acl/list/validator.go new file mode 100644 index 00000000..1296789c --- /dev/null +++ b/commonspace/object/acl/list/validator.go @@ -0,0 +1,218 @@ +package list + +import ( + "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "github.com/anyproto/any-sync/util/crypto" +) + +type ContentValidator interface { + ValidateAclRecordContents(ch *AclRecord) (err error) + ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, authorIdentity crypto.PubKey) (err error) + ValidateInvite(ch *aclrecordproto.AclAccountInvite, 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) + ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, authorIdentity crypto.PubKey) (err error) + ValidateAccountRemove(ch *aclrecordproto.AclAccountRemove, authorIdentity crypto.PubKey) (err error) + ValidateRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, authorIdentity crypto.PubKey) (err error) + ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, authorIdentity crypto.PubKey) (err error) +} + +type contentValidator struct { + keyStore crypto.KeyStorage + aclState *AclState +} + +func (c *contentValidator) ValidateAclRecordContents(ch *AclRecord) (err error) { + if ch.PrevId != c.aclState.lastRecordId { + return ErrIncorrectRecordSequence + } + aclData := ch.Model.(*aclrecordproto.AclData) + for _, content := range aclData.AclContent { + err = c.validateAclRecordContent(content, ch.Identity) + if err != nil { + return + } + } + return +} + +func (c *contentValidator) validateAclRecordContent(ch *aclrecordproto.AclContentValue, authorIdentity crypto.PubKey) (err error) { + switch { + case ch.GetPermissionChange() != nil: + return c.ValidatePermissionChange(ch.GetPermissionChange(), authorIdentity) + case ch.GetInvite() != nil: + return c.ValidateInvite(ch.GetInvite(), authorIdentity) + case ch.GetInviteRevoke() != nil: + return c.ValidateInviteRevoke(ch.GetInviteRevoke(), authorIdentity) + case ch.GetRequestJoin() != nil: + return c.ValidateRequestJoin(ch.GetRequestJoin(), authorIdentity) + case ch.GetRequestAccept() != nil: + return c.ValidateRequestAccept(ch.GetRequestAccept(), authorIdentity) + case ch.GetRequestDecline() != nil: + return c.ValidateRequestDecline(ch.GetRequestDecline(), authorIdentity) + case ch.GetAccountRemove() != nil: + return c.ValidateAccountRemove(ch.GetAccountRemove(), authorIdentity) + case ch.GetAccountRequestRemove() != nil: + return c.ValidateRequestRemove(ch.GetAccountRequestRemove(), authorIdentity) + case ch.GetReadKeyChange() != nil: + return c.ValidateReadKeyChange(ch.GetReadKeyChange(), authorIdentity) + default: + return ErrUnexpectedContentType + } +} + +func (c *contentValidator) ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, authorIdentity crypto.PubKey) (err error) { + if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { + return ErrInsufficientPermissions + } + chIdentity, err := c.keyStore.PubKeyFromProto(ch.Identity) + if err != nil { + return err + } + _, exists := c.aclState.userStates[mapKeyFromPubKey(chIdentity)] + if !exists { + return ErrNoSuchAccount + } + return +} + +func (c *contentValidator) ValidateInvite(ch *aclrecordproto.AclAccountInvite, authorIdentity crypto.PubKey) (err error) { + if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { + return ErrInsufficientPermissions + } + _, err = c.keyStore.PubKeyFromProto(ch.InviteKey) + return +} + +func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error) { + if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { + return ErrInsufficientPermissions + } + _, exists := c.aclState.inviteKeys[ch.InviteRecordId] + if !exists { + return ErrNoSuchInvite + } + return +} + +func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, authorIdentity crypto.PubKey) (err error) { + inviteKey, exists := c.aclState.inviteKeys[ch.InviteRecordId] + if !exists { + return ErrNoSuchInvite + } + inviteIdentity, err := c.keyStore.PubKeyFromProto(ch.InviteIdentity) + if err != nil { + return + } + if _, exists := c.aclState.pendingRequests[mapKeyFromPubKey(inviteIdentity)]; exists { + return ErrPendingRequest + } + if !authorIdentity.Equals(inviteIdentity) { + return ErrIncorrectIdentity + } + rawInviteIdentity, err := inviteIdentity.Raw() + if err != nil { + return err + } + ok, err := inviteKey.Verify(rawInviteIdentity, ch.InviteIdentitySignature) + if err != nil { + return ErrInvalidSignature + } + if !ok { + return ErrInvalidSignature + } + return +} + +func (c *contentValidator) ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, authorIdentity crypto.PubKey) (err error) { + if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { + return ErrInsufficientPermissions + } + record, exists := c.aclState.requestRecords[ch.RequestRecordId] + if !exists { + return ErrNoSuchRequest + } + acceptIdentity, err := c.keyStore.PubKeyFromProto(ch.Identity) + if err != nil { + return + } + if !acceptIdentity.Equals(record.RequestIdentity) { + return ErrIncorrectIdentity + } + if ch.Permissions == aclrecordproto.AclUserPermissions_Owner { + return ErrInsufficientPermissions + } + return +} + +func (c *contentValidator) ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, authorIdentity crypto.PubKey) (err error) { + if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { + return ErrInsufficientPermissions + } + _, exists := c.aclState.requestRecords[ch.RequestRecordId] + if !exists { + return ErrNoSuchRequest + } + return +} + +func (c *contentValidator) ValidateAccountRemove(ch *aclrecordproto.AclAccountRemove, authorIdentity crypto.PubKey) (err error) { + if !c.aclState.Permissions(authorIdentity).CanManageAccounts() { + return ErrInsufficientPermissions + } + seenIdentities := map[string]struct{}{} + for _, rawIdentity := range ch.Identities { + identity, err := c.keyStore.PubKeyFromProto(rawIdentity) + if err != nil { + return err + } + if identity.Equals(authorIdentity) { + return ErrInsufficientPermissions + } + permissions := c.aclState.Permissions(identity) + if permissions.NoPermissions() { + return ErrNoSuchAccount + } + if permissions.IsOwner() { + return ErrInsufficientPermissions + } + idKey := mapKeyFromPubKey(identity) + if _, exists := seenIdentities[idKey]; exists { + return ErrDuplicateAccounts + } + seenIdentities[mapKeyFromPubKey(identity)] = struct{}{} + } + return c.validateAccountReadKeys(ch.AccountKeys, len(c.aclState.userStates)-len(ch.Identities)) +} + +func (c *contentValidator) ValidateRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, authorIdentity crypto.PubKey) (err error) { + if c.aclState.Permissions(authorIdentity).NoPermissions() { + return ErrInsufficientPermissions + } + if _, exists := c.aclState.pendingRequests[mapKeyFromPubKey(authorIdentity)]; exists { + return ErrPendingRequest + } + return +} + +func (c *contentValidator) ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, authorIdentity crypto.PubKey) (err error) { + return c.validateAccountReadKeys(ch.AccountKeys, len(c.aclState.userStates)) +} + +func (c *contentValidator) validateAccountReadKeys(accountKeys []*aclrecordproto.AclEncryptedReadKey, usersNum int) (err error) { + if len(accountKeys) != usersNum { + return ErrIncorrectNumberOfAccounts + } + for _, encKeys := range accountKeys { + identity, err := c.keyStore.PubKeyFromProto(encKeys.Identity) + if err != nil { + return err + } + _, exists := c.aclState.userStates[mapKeyFromPubKey(identity)] + if !exists { + return ErrNoSuchAccount + } + } + return +} diff --git a/commonspace/object/acl/liststorage/inmemory.go b/commonspace/object/acl/liststorage/inmemory.go index 615fe948..276d4f78 100644 --- a/commonspace/object/acl/liststorage/inmemory.go +++ b/commonspace/object/acl/liststorage/inmemory.go @@ -3,24 +3,26 @@ package liststorage import ( "context" "fmt" - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + + "github.com/anyproto/any-sync/consensus/consensusproto" + "sync" ) type inMemoryAclListStorage struct { id string - root *aclrecordproto.RawAclRecordWithId + root *consensusproto.RawRecordWithId head string - records map[string]*aclrecordproto.RawAclRecordWithId + records map[string]*consensusproto.RawRecordWithId sync.RWMutex } func NewInMemoryAclListStorage( id string, - records []*aclrecordproto.RawAclRecordWithId) (ListStorage, error) { + records []*consensusproto.RawRecordWithId) (ListStorage, error) { - allRecords := make(map[string]*aclrecordproto.RawAclRecordWithId) + allRecords := make(map[string]*consensusproto.RawRecordWithId) for _, ch := range records { allRecords[ch.Id] = ch } @@ -41,7 +43,7 @@ func (t *inMemoryAclListStorage) Id() string { return t.id } -func (t *inMemoryAclListStorage) Root() (*aclrecordproto.RawAclRecordWithId, error) { +func (t *inMemoryAclListStorage) Root() (*consensusproto.RawRecordWithId, error) { t.RLock() defer t.RUnlock() return t.root, nil @@ -60,7 +62,7 @@ func (t *inMemoryAclListStorage) SetHead(head string) error { return nil } -func (t *inMemoryAclListStorage) AddRawRecord(ctx context.Context, record *aclrecordproto.RawAclRecordWithId) error { +func (t *inMemoryAclListStorage) AddRawRecord(ctx context.Context, record *consensusproto.RawRecordWithId) error { t.Lock() defer t.Unlock() // TODO: better to do deep copy @@ -68,7 +70,7 @@ func (t *inMemoryAclListStorage) AddRawRecord(ctx context.Context, record *aclre return nil } -func (t *inMemoryAclListStorage) GetRawRecord(ctx context.Context, recordId string) (*aclrecordproto.RawAclRecordWithId, error) { +func (t *inMemoryAclListStorage) GetRawRecord(ctx context.Context, recordId string) (*consensusproto.RawRecordWithId, error) { t.RLock() defer t.RUnlock() if res, exists := t.records[recordId]; exists { diff --git a/commonspace/object/acl/liststorage/liststorage.go b/commonspace/object/acl/liststorage/liststorage.go index 416fe100..f17e201f 100644 --- a/commonspace/object/acl/liststorage/liststorage.go +++ b/commonspace/object/acl/liststorage/liststorage.go @@ -4,7 +4,8 @@ package liststorage import ( "context" "errors" - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + + "github.com/anyproto/any-sync/consensus/consensusproto" ) var ( @@ -14,15 +15,15 @@ var ( ) type Exporter interface { - ListStorage(root *aclrecordproto.RawAclRecordWithId) (ListStorage, error) + ListStorage(root *consensusproto.RawRecordWithId) (ListStorage, error) } type ListStorage interface { Id() string - Root() (*aclrecordproto.RawAclRecordWithId, error) + Root() (*consensusproto.RawRecordWithId, error) Head() (string, error) SetHead(headId string) error - GetRawRecord(ctx context.Context, id string) (*aclrecordproto.RawAclRecordWithId, error) - AddRawRecord(ctx context.Context, rec *aclrecordproto.RawAclRecordWithId) error + GetRawRecord(ctx context.Context, id string) (*consensusproto.RawRecordWithId, error) + AddRawRecord(ctx context.Context, rec *consensusproto.RawRecordWithId) error } diff --git a/commonspace/object/acl/liststorage/mock_liststorage/mock_liststorage.go b/commonspace/object/acl/liststorage/mock_liststorage/mock_liststorage.go index ce0a8bbf..52d00f50 100644 --- a/commonspace/object/acl/liststorage/mock_liststorage/mock_liststorage.go +++ b/commonspace/object/acl/liststorage/mock_liststorage/mock_liststorage.go @@ -8,8 +8,8 @@ import ( context "context" reflect "reflect" - aclrecordproto "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" - gomock "github.com/golang/mock/gomock" + consensusproto "github.com/anyproto/any-sync/consensus/consensusproto" + gomock "go.uber.org/mock/gomock" ) // MockListStorage is a mock of ListStorage interface. @@ -36,7 +36,7 @@ func (m *MockListStorage) EXPECT() *MockListStorageMockRecorder { } // AddRawRecord mocks base method. -func (m *MockListStorage) AddRawRecord(arg0 context.Context, arg1 *aclrecordproto.RawAclRecordWithId) error { +func (m *MockListStorage) AddRawRecord(arg0 context.Context, arg1 *consensusproto.RawRecordWithId) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddRawRecord", arg0, arg1) ret0, _ := ret[0].(error) @@ -50,10 +50,10 @@ func (mr *MockListStorageMockRecorder) AddRawRecord(arg0, arg1 interface{}) *gom } // GetRawRecord mocks base method. -func (m *MockListStorage) GetRawRecord(arg0 context.Context, arg1 string) (*aclrecordproto.RawAclRecordWithId, error) { +func (m *MockListStorage) GetRawRecord(arg0 context.Context, arg1 string) (*consensusproto.RawRecordWithId, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRawRecord", arg0, arg1) - ret0, _ := ret[0].(*aclrecordproto.RawAclRecordWithId) + ret0, _ := ret[0].(*consensusproto.RawRecordWithId) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -94,10 +94,10 @@ func (mr *MockListStorageMockRecorder) Id() *gomock.Call { } // Root mocks base method. -func (m *MockListStorage) Root() (*aclrecordproto.RawAclRecordWithId, error) { +func (m *MockListStorage) Root() (*consensusproto.RawRecordWithId, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Root") - ret0, _ := ret[0].(*aclrecordproto.RawAclRecordWithId) + ret0, _ := ret[0].(*consensusproto.RawRecordWithId) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/commonspace/object/acl/syncacl/aclsyncprotocol.go b/commonspace/object/acl/syncacl/aclsyncprotocol.go new file mode 100644 index 00000000..22fd3a0c --- /dev/null +++ b/commonspace/object/acl/syncacl/aclsyncprotocol.go @@ -0,0 +1,120 @@ +//go:generate mockgen -destination mock_syncacl/mock_syncacl.go github.com/anyproto/any-sync/commonspace/object/acl/syncacl SyncAcl,SyncClient,RequestFactory,AclSyncProtocol +package syncacl + +import ( + "context" + + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/object/acl/list" + "github.com/anyproto/any-sync/consensus/consensusproto" + "go.uber.org/zap" +) + +type AclSyncProtocol interface { + HeadUpdate(ctx context.Context, senderId string, update *consensusproto.LogHeadUpdate) (request *consensusproto.LogSyncMessage, err error) + FullSyncRequest(ctx context.Context, senderId string, request *consensusproto.LogFullSyncRequest) (response *consensusproto.LogSyncMessage, err error) + FullSyncResponse(ctx context.Context, senderId string, response *consensusproto.LogFullSyncResponse) (err error) +} + +type aclSyncProtocol struct { + log logger.CtxLogger + spaceId string + aclList list.AclList + reqFactory RequestFactory +} + +func (a *aclSyncProtocol) HeadUpdate(ctx context.Context, senderId string, update *consensusproto.LogHeadUpdate) (request *consensusproto.LogSyncMessage, err error) { + isEmptyUpdate := len(update.Records) == 0 + log := a.log.With( + zap.String("senderId", senderId), + zap.String("update head", update.Head), + zap.Int("len(update records)", len(update.Records))) + log.DebugCtx(ctx, "received acl head update message") + + defer func() { + if err != nil { + log.ErrorCtx(ctx, "acl head update finished with error", zap.Error(err)) + } else if request != nil { + cnt := request.Content.GetFullSyncRequest() + log.DebugCtx(ctx, "returning acl full sync request", zap.String("request head", cnt.Head)) + } else { + if !isEmptyUpdate { + log.DebugCtx(ctx, "acl head update finished correctly") + } + } + }() + if isEmptyUpdate { + headEquals := a.aclList.Head().Id == update.Head + log.DebugCtx(ctx, "is empty acl head update", zap.Bool("headEquals", headEquals)) + if headEquals { + return + } + return a.reqFactory.CreateFullSyncRequest(a.aclList, update.Head) + } + if a.aclList.HasHead(update.Head) { + return + } + err = a.aclList.AddRawRecords(update.Records) + if err == list.ErrIncorrectRecordSequence { + return a.reqFactory.CreateFullSyncRequest(a.aclList, update.Head) + } + return +} + +func (a *aclSyncProtocol) FullSyncRequest(ctx context.Context, senderId string, request *consensusproto.LogFullSyncRequest) (response *consensusproto.LogSyncMessage, err error) { + log := a.log.With( + zap.String("senderId", senderId), + zap.String("request head", request.Head), + zap.Int("len(request records)", len(request.Records))) + log.DebugCtx(ctx, "received acl full sync request message") + + defer func() { + if err != nil { + log.ErrorCtx(ctx, "acl full sync request finished with error", zap.Error(err)) + } else if response != nil { + cnt := response.Content.GetFullSyncResponse() + log.DebugCtx(ctx, "acl full sync response sent", zap.String("response head", cnt.Head), zap.Int("len(response records)", len(cnt.Records))) + } + }() + if !a.aclList.HasHead(request.Head) { + if len(request.Records) > 0 { + // in this case we can try to add some records + err = a.aclList.AddRawRecords(request.Records) + if err != nil { + return + } + } else { + // here it is impossible for us to do anything, we can't return records after head as defined in request, because we don't have it + return nil, list.ErrIncorrectRecordSequence + } + } + return a.reqFactory.CreateFullSyncResponse(a.aclList, request.Head) +} + +func (a *aclSyncProtocol) FullSyncResponse(ctx context.Context, senderId string, response *consensusproto.LogFullSyncResponse) (err error) { + log := a.log.With( + zap.String("senderId", senderId), + zap.String("response head", response.Head), + zap.Int("len(response records)", len(response.Records))) + log.DebugCtx(ctx, "received acl full sync response message") + defer func() { + if err != nil { + log.ErrorCtx(ctx, "acl full sync response failed", zap.Error(err)) + } else { + log.DebugCtx(ctx, "acl full sync response succeeded") + } + }() + if a.aclList.HasHead(response.Head) { + return + } + return a.aclList.AddRawRecords(response.Records) +} + +func newAclSyncProtocol(spaceId string, aclList list.AclList, reqFactory RequestFactory) *aclSyncProtocol { + return &aclSyncProtocol{ + log: log.With(zap.String("spaceId", spaceId), zap.String("aclId", aclList.Id())), + spaceId: spaceId, + aclList: aclList, + reqFactory: reqFactory, + } +} diff --git a/commonspace/object/acl/syncacl/aclsyncprotocol_test.go b/commonspace/object/acl/syncacl/aclsyncprotocol_test.go new file mode 100644 index 00000000..1f861335 --- /dev/null +++ b/commonspace/object/acl/syncacl/aclsyncprotocol_test.go @@ -0,0 +1,213 @@ +package syncacl + +import ( + "context" + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/object/acl/list" + "github.com/anyproto/any-sync/commonspace/object/acl/list/mock_list" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl/mock_syncacl" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "testing" +) + +type aclSyncProtocolFixture struct { + log logger.CtxLogger + spaceId string + senderId string + aclId string + aclMock *mock_list.MockAclList + reqFactory *mock_syncacl.MockRequestFactory + ctrl *gomock.Controller + syncProtocol AclSyncProtocol +} + +func newSyncProtocolFixture(t *testing.T) *aclSyncProtocolFixture { + ctrl := gomock.NewController(t) + aclList := mock_list.NewMockAclList(ctrl) + spaceId := "spaceId" + reqFactory := mock_syncacl.NewMockRequestFactory(ctrl) + aclList.EXPECT().Id().Return("aclId") + syncProtocol := newAclSyncProtocol(spaceId, aclList, reqFactory) + return &aclSyncProtocolFixture{ + log: log, + spaceId: spaceId, + senderId: "senderId", + aclId: "aclId", + aclMock: aclList, + reqFactory: reqFactory, + ctrl: ctrl, + syncProtocol: syncProtocol, + } +} + +func (fx *aclSyncProtocolFixture) stop() { + fx.ctrl.Finish() +} + +func TestHeadUpdate(t *testing.T) { + ctx := context.Background() + fullRequest := &consensusproto.LogSyncMessage{ + Content: &consensusproto.LogSyncContentValue{ + Value: &consensusproto.LogSyncContentValue_FullSyncRequest{ + FullSyncRequest: &consensusproto.LogFullSyncRequest{}, + }, + }, + } + t.Run("head update non empty all heads added", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(false) + fx.aclMock.EXPECT().AddRawRecords(headUpdate.Records).Return(nil) + req, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.Nil(t, req) + require.NoError(t, err) + }) + t.Run("head update results in full request", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(false) + fx.aclMock.EXPECT().AddRawRecords(headUpdate.Records).Return(list.ErrIncorrectRecordSequence) + fx.reqFactory.EXPECT().CreateFullSyncRequest(fx.aclMock, headUpdate.Head).Return(fullRequest, nil) + req, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.Equal(t, fullRequest, req) + require.NoError(t, err) + }) + t.Run("head update old heads", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(true) + req, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.Nil(t, req) + require.NoError(t, err) + }) + t.Run("head update empty equals", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().Head().Return(&list.AclRecord{Id: "h1"}) + req, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.Nil(t, req) + require.NoError(t, err) + }) + t.Run("head update empty results in full request", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().Head().Return(&list.AclRecord{Id: "h2"}) + fx.reqFactory.EXPECT().CreateFullSyncRequest(fx.aclMock, headUpdate.Head).Return(fullRequest, nil) + req, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.Equal(t, fullRequest, req) + require.NoError(t, err) + }) +} + +func TestFullSyncRequest(t *testing.T) { + ctx := context.Background() + fullResponse := &consensusproto.LogSyncMessage{ + Content: &consensusproto.LogSyncContentValue{ + Value: &consensusproto.LogSyncContentValue_FullSyncResponse{ + FullSyncResponse: &consensusproto.LogFullSyncResponse{}, + }, + }, + } + t.Run("full sync request non empty all heads added", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + fullRequest := &consensusproto.LogFullSyncRequest{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(false) + fx.aclMock.EXPECT().AddRawRecords(fullRequest.Records).Return(nil) + fx.reqFactory.EXPECT().CreateFullSyncResponse(fx.aclMock, fullRequest.Head).Return(fullResponse, nil) + resp, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullRequest) + require.Equal(t, fullResponse, resp) + require.NoError(t, err) + }) + t.Run("full sync request non empty head exists", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + fullRequest := &consensusproto.LogFullSyncRequest{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(true) + fx.reqFactory.EXPECT().CreateFullSyncResponse(fx.aclMock, fullRequest.Head).Return(fullResponse, nil) + resp, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullRequest) + require.Equal(t, fullResponse, resp) + require.NoError(t, err) + }) + t.Run("full sync request empty head not exists", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + fullRequest := &consensusproto.LogFullSyncRequest{ + Head: "h1", + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(false) + resp, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullRequest) + require.Nil(t, resp) + require.Error(t, list.ErrIncorrectRecordSequence, err) + }) +} + +func TestFullSyncResponse(t *testing.T) { + ctx := context.Background() + t.Run("full sync response no heads", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + fullResponse := &consensusproto.LogFullSyncResponse{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(false) + fx.aclMock.EXPECT().AddRawRecords(fullResponse.Records).Return(nil) + err := fx.syncProtocol.FullSyncResponse(ctx, fx.senderId, fullResponse) + require.NoError(t, err) + }) + t.Run("full sync response has heads", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + fullResponse := &consensusproto.LogFullSyncResponse{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.aclMock.EXPECT().HasHead("h1").Return(true) + err := fx.syncProtocol.FullSyncResponse(ctx, fx.senderId, fullResponse) + require.NoError(t, err) + }) +} diff --git a/commonspace/object/acl/syncacl/headupdater/headupdater.go b/commonspace/object/acl/syncacl/headupdater/headupdater.go new file mode 100644 index 00000000..64c41f41 --- /dev/null +++ b/commonspace/object/acl/syncacl/headupdater/headupdater.go @@ -0,0 +1,5 @@ +package headupdater + +type HeadUpdater interface { + UpdateHeads(id string, heads []string) +} diff --git a/commonspace/object/acl/syncacl/mock_syncacl/mock_syncacl.go b/commonspace/object/acl/syncacl/mock_syncacl/mock_syncacl.go new file mode 100644 index 00000000..66d16b9d --- /dev/null +++ b/commonspace/object/acl/syncacl/mock_syncacl/mock_syncacl.go @@ -0,0 +1,694 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/anyproto/any-sync/commonspace/object/acl/syncacl (interfaces: SyncAcl,SyncClient,RequestFactory,AclSyncProtocol) + +// Package mock_syncacl is a generated GoMock package. +package mock_syncacl + +import ( + context "context" + reflect "reflect" + + app "github.com/anyproto/any-sync/app" + list "github.com/anyproto/any-sync/commonspace/object/acl/list" + headupdater "github.com/anyproto/any-sync/commonspace/object/acl/syncacl/headupdater" + spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" + consensusproto "github.com/anyproto/any-sync/consensus/consensusproto" + crypto "github.com/anyproto/any-sync/util/crypto" + gomock "go.uber.org/mock/gomock" +) + +// MockSyncAcl is a mock of SyncAcl interface. +type MockSyncAcl struct { + ctrl *gomock.Controller + recorder *MockSyncAclMockRecorder +} + +// MockSyncAclMockRecorder is the mock recorder for MockSyncAcl. +type MockSyncAclMockRecorder struct { + mock *MockSyncAcl +} + +// NewMockSyncAcl creates a new mock instance. +func NewMockSyncAcl(ctrl *gomock.Controller) *MockSyncAcl { + mock := &MockSyncAcl{ctrl: ctrl} + mock.recorder = &MockSyncAclMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSyncAcl) EXPECT() *MockSyncAclMockRecorder { + return m.recorder +} + +// AclState mocks base method. +func (m *MockSyncAcl) AclState() *list.AclState { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AclState") + ret0, _ := ret[0].(*list.AclState) + return ret0 +} + +// AclState indicates an expected call of AclState. +func (mr *MockSyncAclMockRecorder) AclState() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AclState", reflect.TypeOf((*MockSyncAcl)(nil).AclState)) +} + +// AddRawRecord mocks base method. +func (m *MockSyncAcl) AddRawRecord(arg0 *consensusproto.RawRecordWithId) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddRawRecord", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddRawRecord indicates an expected call of AddRawRecord. +func (mr *MockSyncAclMockRecorder) AddRawRecord(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawRecord", reflect.TypeOf((*MockSyncAcl)(nil).AddRawRecord), arg0) +} + +// AddRawRecords mocks base method. +func (m *MockSyncAcl) AddRawRecords(arg0 []*consensusproto.RawRecordWithId) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddRawRecords", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddRawRecords indicates an expected call of AddRawRecords. +func (mr *MockSyncAclMockRecorder) AddRawRecords(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawRecords", reflect.TypeOf((*MockSyncAcl)(nil).AddRawRecords), arg0) +} + +// Close mocks base method. +func (m *MockSyncAcl) Close(arg0 context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockSyncAclMockRecorder) Close(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSyncAcl)(nil).Close), arg0) +} + +// Get mocks base method. +func (m *MockSyncAcl) Get(arg0 string) (*list.AclRecord, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0) + ret0, _ := ret[0].(*list.AclRecord) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockSyncAclMockRecorder) Get(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockSyncAcl)(nil).Get), arg0) +} + +// GetIndex mocks base method. +func (m *MockSyncAcl) GetIndex(arg0 int) (*list.AclRecord, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIndex", arg0) + ret0, _ := ret[0].(*list.AclRecord) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetIndex indicates an expected call of GetIndex. +func (mr *MockSyncAclMockRecorder) GetIndex(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIndex", reflect.TypeOf((*MockSyncAcl)(nil).GetIndex), arg0) +} + +// HandleMessage mocks base method. +func (m *MockSyncAcl) HandleMessage(arg0 context.Context, arg1 string, arg2 *spacesyncproto.ObjectSyncMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleMessage", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// HandleMessage indicates an expected call of HandleMessage. +func (mr *MockSyncAclMockRecorder) HandleMessage(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockSyncAcl)(nil).HandleMessage), arg0, arg1, arg2) +} + +// HandleRequest mocks base method. +func (m *MockSyncAcl) HandleRequest(arg0 context.Context, arg1 string, arg2 *spacesyncproto.ObjectSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HandleRequest indicates an expected call of HandleRequest. +func (mr *MockSyncAclMockRecorder) HandleRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleRequest", reflect.TypeOf((*MockSyncAcl)(nil).HandleRequest), arg0, arg1, arg2) +} + +// HasHead mocks base method. +func (m *MockSyncAcl) HasHead(arg0 string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasHead", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasHead indicates an expected call of HasHead. +func (mr *MockSyncAclMockRecorder) HasHead(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasHead", reflect.TypeOf((*MockSyncAcl)(nil).HasHead), arg0) +} + +// Head mocks base method. +func (m *MockSyncAcl) Head() *list.AclRecord { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Head") + ret0, _ := ret[0].(*list.AclRecord) + return ret0 +} + +// Head indicates an expected call of Head. +func (mr *MockSyncAclMockRecorder) Head() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Head", reflect.TypeOf((*MockSyncAcl)(nil).Head)) +} + +// Id mocks base method. +func (m *MockSyncAcl) Id() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Id") + ret0, _ := ret[0].(string) + return ret0 +} + +// Id indicates an expected call of Id. +func (mr *MockSyncAclMockRecorder) Id() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockSyncAcl)(nil).Id)) +} + +// Init mocks base method. +func (m *MockSyncAcl) Init(arg0 *app.App) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockSyncAclMockRecorder) Init(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockSyncAcl)(nil).Init), arg0) +} + +// IsAfter mocks base method. +func (m *MockSyncAcl) IsAfter(arg0, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsAfter", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsAfter indicates an expected call of IsAfter. +func (mr *MockSyncAclMockRecorder) IsAfter(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsAfter", reflect.TypeOf((*MockSyncAcl)(nil).IsAfter), arg0, arg1) +} + +// Iterate mocks base method. +func (m *MockSyncAcl) Iterate(arg0 func(*list.AclRecord) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Iterate", arg0) +} + +// Iterate indicates an expected call of Iterate. +func (mr *MockSyncAclMockRecorder) Iterate(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterate", reflect.TypeOf((*MockSyncAcl)(nil).Iterate), arg0) +} + +// IterateFrom mocks base method. +func (m *MockSyncAcl) IterateFrom(arg0 string, arg1 func(*list.AclRecord) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateFrom", arg0, arg1) +} + +// IterateFrom indicates an expected call of IterateFrom. +func (mr *MockSyncAclMockRecorder) IterateFrom(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateFrom", reflect.TypeOf((*MockSyncAcl)(nil).IterateFrom), arg0, arg1) +} + +// KeyStorage mocks base method. +func (m *MockSyncAcl) KeyStorage() crypto.KeyStorage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KeyStorage") + ret0, _ := ret[0].(crypto.KeyStorage) + return ret0 +} + +// KeyStorage indicates an expected call of KeyStorage. +func (mr *MockSyncAclMockRecorder) KeyStorage() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeyStorage", reflect.TypeOf((*MockSyncAcl)(nil).KeyStorage)) +} + +// Lock mocks base method. +func (m *MockSyncAcl) Lock() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Lock") +} + +// Lock indicates an expected call of Lock. +func (mr *MockSyncAclMockRecorder) Lock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lock", reflect.TypeOf((*MockSyncAcl)(nil).Lock)) +} + +// Name mocks base method. +func (m *MockSyncAcl) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockSyncAclMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockSyncAcl)(nil).Name)) +} + +// RLock mocks base method. +func (m *MockSyncAcl) RLock() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RLock") +} + +// RLock indicates an expected call of RLock. +func (mr *MockSyncAclMockRecorder) RLock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RLock", reflect.TypeOf((*MockSyncAcl)(nil).RLock)) +} + +// RUnlock mocks base method. +func (m *MockSyncAcl) RUnlock() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RUnlock") +} + +// RUnlock indicates an expected call of RUnlock. +func (mr *MockSyncAclMockRecorder) RUnlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RUnlock", reflect.TypeOf((*MockSyncAcl)(nil).RUnlock)) +} + +// RecordBuilder mocks base method. +func (m *MockSyncAcl) RecordBuilder() list.AclRecordBuilder { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecordBuilder") + ret0, _ := ret[0].(list.AclRecordBuilder) + return ret0 +} + +// RecordBuilder indicates an expected call of RecordBuilder. +func (mr *MockSyncAclMockRecorder) RecordBuilder() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordBuilder", reflect.TypeOf((*MockSyncAcl)(nil).RecordBuilder)) +} + +// Records mocks base method. +func (m *MockSyncAcl) Records() []*list.AclRecord { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Records") + ret0, _ := ret[0].([]*list.AclRecord) + return ret0 +} + +// Records indicates an expected call of Records. +func (mr *MockSyncAclMockRecorder) Records() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Records", reflect.TypeOf((*MockSyncAcl)(nil).Records)) +} + +// RecordsAfter mocks base method. +func (m *MockSyncAcl) RecordsAfter(arg0 context.Context, arg1 string) ([]*consensusproto.RawRecordWithId, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecordsAfter", arg0, arg1) + ret0, _ := ret[0].([]*consensusproto.RawRecordWithId) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RecordsAfter indicates an expected call of RecordsAfter. +func (mr *MockSyncAclMockRecorder) RecordsAfter(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordsAfter", reflect.TypeOf((*MockSyncAcl)(nil).RecordsAfter), arg0, arg1) +} + +// Root mocks base method. +func (m *MockSyncAcl) Root() *consensusproto.RawRecordWithId { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Root") + ret0, _ := ret[0].(*consensusproto.RawRecordWithId) + return ret0 +} + +// Root indicates an expected call of Root. +func (mr *MockSyncAclMockRecorder) Root() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Root", reflect.TypeOf((*MockSyncAcl)(nil).Root)) +} + +// Run mocks base method. +func (m *MockSyncAcl) Run(arg0 context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Run", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Run indicates an expected call of Run. +func (mr *MockSyncAclMockRecorder) Run(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockSyncAcl)(nil).Run), arg0) +} + +// SetHeadUpdater mocks base method. +func (m *MockSyncAcl) SetHeadUpdater(arg0 headupdater.HeadUpdater) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetHeadUpdater", arg0) +} + +// SetHeadUpdater indicates an expected call of SetHeadUpdater. +func (mr *MockSyncAclMockRecorder) SetHeadUpdater(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeadUpdater", reflect.TypeOf((*MockSyncAcl)(nil).SetHeadUpdater), arg0) +} + +// SyncWithPeer mocks base method. +func (m *MockSyncAcl) SyncWithPeer(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SyncWithPeer", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SyncWithPeer indicates an expected call of SyncWithPeer. +func (mr *MockSyncAclMockRecorder) SyncWithPeer(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncWithPeer", reflect.TypeOf((*MockSyncAcl)(nil).SyncWithPeer), arg0, arg1) +} + +// Unlock mocks base method. +func (m *MockSyncAcl) Unlock() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Unlock") +} + +// Unlock indicates an expected call of Unlock. +func (mr *MockSyncAclMockRecorder) Unlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unlock", reflect.TypeOf((*MockSyncAcl)(nil).Unlock)) +} + +// ValidateRawRecord mocks base method. +func (m *MockSyncAcl) ValidateRawRecord(arg0 *consensusproto.RawRecord) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateRawRecord", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateRawRecord indicates an expected call of ValidateRawRecord. +func (mr *MockSyncAclMockRecorder) ValidateRawRecord(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRawRecord", reflect.TypeOf((*MockSyncAcl)(nil).ValidateRawRecord), arg0) +} + +// MockSyncClient is a mock of SyncClient interface. +type MockSyncClient struct { + ctrl *gomock.Controller + recorder *MockSyncClientMockRecorder +} + +// MockSyncClientMockRecorder is the mock recorder for MockSyncClient. +type MockSyncClientMockRecorder struct { + mock *MockSyncClient +} + +// NewMockSyncClient creates a new mock instance. +func NewMockSyncClient(ctrl *gomock.Controller) *MockSyncClient { + mock := &MockSyncClient{ctrl: ctrl} + mock.recorder = &MockSyncClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSyncClient) EXPECT() *MockSyncClientMockRecorder { + return m.recorder +} + +// Broadcast mocks base method. +func (m *MockSyncClient) Broadcast(arg0 *consensusproto.LogSyncMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Broadcast", arg0) +} + +// Broadcast indicates an expected call of Broadcast. +func (mr *MockSyncClientMockRecorder) Broadcast(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockSyncClient)(nil).Broadcast), arg0) +} + +// CreateFullSyncRequest mocks base method. +func (m *MockSyncClient) CreateFullSyncRequest(arg0 list.AclList, arg1 string) (*consensusproto.LogSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest. +func (mr *MockSyncClientMockRecorder) CreateFullSyncRequest(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncRequest), arg0, arg1) +} + +// CreateFullSyncResponse mocks base method. +func (m *MockSyncClient) CreateFullSyncResponse(arg0 list.AclList, arg1 string) (*consensusproto.LogSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse. +func (mr *MockSyncClientMockRecorder) CreateFullSyncResponse(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncResponse), arg0, arg1) +} + +// CreateHeadUpdate mocks base method. +func (m *MockSyncClient) CreateHeadUpdate(arg0 list.AclList, arg1 []*consensusproto.RawRecordWithId) *consensusproto.LogSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + return ret0 +} + +// CreateHeadUpdate indicates an expected call of CreateHeadUpdate. +func (mr *MockSyncClientMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockSyncClient)(nil).CreateHeadUpdate), arg0, arg1) +} + +// QueueRequest mocks base method. +func (m *MockSyncClient) QueueRequest(arg0 string, arg1 *consensusproto.LogSyncMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueueRequest", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// QueueRequest indicates an expected call of QueueRequest. +func (mr *MockSyncClientMockRecorder) QueueRequest(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueRequest", reflect.TypeOf((*MockSyncClient)(nil).QueueRequest), arg0, arg1) +} + +// SendRequest mocks base method. +func (m *MockSyncClient) SendRequest(arg0 context.Context, arg1 string, arg2 *consensusproto.LogSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendRequest indicates an expected call of SendRequest. +func (mr *MockSyncClientMockRecorder) SendRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRequest", reflect.TypeOf((*MockSyncClient)(nil).SendRequest), arg0, arg1, arg2) +} + +// SendUpdate mocks base method. +func (m *MockSyncClient) SendUpdate(arg0 string, arg1 *consensusproto.LogSyncMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendUpdate", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendUpdate indicates an expected call of SendUpdate. +func (mr *MockSyncClientMockRecorder) SendUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendUpdate", reflect.TypeOf((*MockSyncClient)(nil).SendUpdate), arg0, arg1) +} + +// MockRequestFactory is a mock of RequestFactory interface. +type MockRequestFactory struct { + ctrl *gomock.Controller + recorder *MockRequestFactoryMockRecorder +} + +// MockRequestFactoryMockRecorder is the mock recorder for MockRequestFactory. +type MockRequestFactoryMockRecorder struct { + mock *MockRequestFactory +} + +// NewMockRequestFactory creates a new mock instance. +func NewMockRequestFactory(ctrl *gomock.Controller) *MockRequestFactory { + mock := &MockRequestFactory{ctrl: ctrl} + mock.recorder = &MockRequestFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRequestFactory) EXPECT() *MockRequestFactoryMockRecorder { + return m.recorder +} + +// CreateFullSyncRequest mocks base method. +func (m *MockRequestFactory) CreateFullSyncRequest(arg0 list.AclList, arg1 string) (*consensusproto.LogSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest. +func (mr *MockRequestFactoryMockRecorder) CreateFullSyncRequest(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncRequest), arg0, arg1) +} + +// CreateFullSyncResponse mocks base method. +func (m *MockRequestFactory) CreateFullSyncResponse(arg0 list.AclList, arg1 string) (*consensusproto.LogSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse. +func (mr *MockRequestFactoryMockRecorder) CreateFullSyncResponse(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncResponse), arg0, arg1) +} + +// CreateHeadUpdate mocks base method. +func (m *MockRequestFactory) CreateHeadUpdate(arg0 list.AclList, arg1 []*consensusproto.RawRecordWithId) *consensusproto.LogSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + return ret0 +} + +// CreateHeadUpdate indicates an expected call of CreateHeadUpdate. +func (mr *MockRequestFactoryMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockRequestFactory)(nil).CreateHeadUpdate), arg0, arg1) +} + +// MockAclSyncProtocol is a mock of AclSyncProtocol interface. +type MockAclSyncProtocol struct { + ctrl *gomock.Controller + recorder *MockAclSyncProtocolMockRecorder +} + +// MockAclSyncProtocolMockRecorder is the mock recorder for MockAclSyncProtocol. +type MockAclSyncProtocolMockRecorder struct { + mock *MockAclSyncProtocol +} + +// NewMockAclSyncProtocol creates a new mock instance. +func NewMockAclSyncProtocol(ctrl *gomock.Controller) *MockAclSyncProtocol { + mock := &MockAclSyncProtocol{ctrl: ctrl} + mock.recorder = &MockAclSyncProtocolMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAclSyncProtocol) EXPECT() *MockAclSyncProtocolMockRecorder { + return m.recorder +} + +// FullSyncRequest mocks base method. +func (m *MockAclSyncProtocol) FullSyncRequest(arg0 context.Context, arg1 string, arg2 *consensusproto.LogFullSyncRequest) (*consensusproto.LogSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FullSyncRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FullSyncRequest indicates an expected call of FullSyncRequest. +func (mr *MockAclSyncProtocolMockRecorder) FullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullSyncRequest", reflect.TypeOf((*MockAclSyncProtocol)(nil).FullSyncRequest), arg0, arg1, arg2) +} + +// FullSyncResponse mocks base method. +func (m *MockAclSyncProtocol) FullSyncResponse(arg0 context.Context, arg1 string, arg2 *consensusproto.LogFullSyncResponse) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FullSyncResponse", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// FullSyncResponse indicates an expected call of FullSyncResponse. +func (mr *MockAclSyncProtocolMockRecorder) FullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullSyncResponse", reflect.TypeOf((*MockAclSyncProtocol)(nil).FullSyncResponse), arg0, arg1, arg2) +} + +// HeadUpdate mocks base method. +func (m *MockAclSyncProtocol) HeadUpdate(arg0 context.Context, arg1 string, arg2 *consensusproto.LogHeadUpdate) (*consensusproto.LogSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HeadUpdate", arg0, arg1, arg2) + ret0, _ := ret[0].(*consensusproto.LogSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HeadUpdate indicates an expected call of HeadUpdate. +func (mr *MockAclSyncProtocolMockRecorder) HeadUpdate(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeadUpdate", reflect.TypeOf((*MockAclSyncProtocol)(nil).HeadUpdate), arg0, arg1, arg2) +} diff --git a/commonspace/object/acl/syncacl/requestfactory.go b/commonspace/object/acl/syncacl/requestfactory.go new file mode 100644 index 00000000..6adddb4b --- /dev/null +++ b/commonspace/object/acl/syncacl/requestfactory.go @@ -0,0 +1,54 @@ +package syncacl + +import ( + "context" + + "github.com/anyproto/any-sync/commonspace/object/acl/list" + "github.com/anyproto/any-sync/consensus/consensusproto" +) + +type RequestFactory interface { + CreateHeadUpdate(l list.AclList, added []*consensusproto.RawRecordWithId) (msg *consensusproto.LogSyncMessage) + CreateFullSyncRequest(l list.AclList, theirHead string) (req *consensusproto.LogSyncMessage, err error) + CreateFullSyncResponse(l list.AclList, theirHead string) (*consensusproto.LogSyncMessage, error) +} + +type requestFactory struct{} + +func NewRequestFactory() RequestFactory { + return &requestFactory{} +} + +func (r *requestFactory) CreateHeadUpdate(l list.AclList, added []*consensusproto.RawRecordWithId) (msg *consensusproto.LogSyncMessage) { + return consensusproto.WrapHeadUpdate(&consensusproto.LogHeadUpdate{ + Head: l.Head().Id, + Records: added, + }, l.Root()) +} + +func (r *requestFactory) CreateFullSyncRequest(l list.AclList, theirHead string) (req *consensusproto.LogSyncMessage, err error) { + if !l.HasHead(theirHead) { + return consensusproto.WrapFullRequest(&consensusproto.LogFullSyncRequest{ + Head: l.Head().Id, + }, l.Root()), nil + } + records, err := l.RecordsAfter(context.Background(), theirHead) + if err != nil { + return + } + return consensusproto.WrapFullRequest(&consensusproto.LogFullSyncRequest{ + Head: l.Head().Id, + Records: records, + }, l.Root()), nil +} + +func (r *requestFactory) CreateFullSyncResponse(l list.AclList, theirHead string) (resp *consensusproto.LogSyncMessage, err error) { + records, err := l.RecordsAfter(context.Background(), theirHead) + if err != nil { + return + } + return consensusproto.WrapFullResponse(&consensusproto.LogFullSyncResponse{ + Head: l.Head().Id, + Records: records, + }, l.Root()), nil +} diff --git a/commonspace/object/acl/syncacl/syncacl.go b/commonspace/object/acl/syncacl/syncacl.go index dd1e647e..23592490 100644 --- a/commonspace/object/acl/syncacl/syncacl.go +++ b/commonspace/object/acl/syncacl/syncacl.go @@ -1,21 +1,130 @@ package syncacl import ( + "context" + "errors" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl/headupdater" + "github.com/anyproto/any-sync/commonspace/object/syncobjectgetter" + + "github.com/anyproto/any-sync/accountservice" + "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/objectsync" "github.com/anyproto/any-sync/commonspace/objectsync/synchandler" + "github.com/anyproto/any-sync/commonspace/peermanager" + "github.com/anyproto/any-sync/commonspace/requestmanager" + "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/consensus/consensusproto" ) -type SyncAcl struct { +const CName = "common.acl.syncacl" + +var ( + log = logger.NewNamed(CName) + + ErrSyncAclClosed = errors.New("sync acl is closed") +) + +type SyncAcl interface { + app.ComponentRunnable list.AclList - synchandler.SyncHandler - messagePool objectsync.MessagePool + syncobjectgetter.SyncObject + SetHeadUpdater(updater headupdater.HeadUpdater) + SyncWithPeer(ctx context.Context, peerId string) (err error) } -func NewSyncAcl(aclList list.AclList, messagePool objectsync.MessagePool) *SyncAcl { - return &SyncAcl{ - AclList: aclList, - SyncHandler: nil, - messagePool: messagePool, - } +func New() SyncAcl { + return &syncAcl{} +} + +type syncAcl struct { + list.AclList + syncClient SyncClient + syncHandler synchandler.SyncHandler + headUpdater headupdater.HeadUpdater + isClosed bool +} + +func (s *syncAcl) Run(ctx context.Context) (err error) { + return +} + +func (s *syncAcl) HandleRequest(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (response *spacesyncproto.ObjectSyncMessage, err error) { + return s.syncHandler.HandleRequest(ctx, senderId, request) +} + +func (s *syncAcl) SetHeadUpdater(updater headupdater.HeadUpdater) { + s.headUpdater = updater +} + +func (s *syncAcl) HandleMessage(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (err error) { + return s.syncHandler.HandleMessage(ctx, senderId, request) +} + +func (s *syncAcl) Init(a *app.App) (err error) { + storage := a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + aclStorage, err := storage.AclStorage() + if err != nil { + return err + } + acc := a.MustComponent(accountservice.CName).(accountservice.Service) + s.AclList, err = list.BuildAclListWithIdentity(acc.Account(), aclStorage, list.NoOpAcceptorVerifier{}) + if err != nil { + return + } + spaceId := storage.Id() + requestManager := a.MustComponent(requestmanager.CName).(requestmanager.RequestManager) + peerManager := a.MustComponent(peermanager.CName).(peermanager.PeerManager) + syncStatus := a.MustComponent(syncstatus.CName).(syncstatus.StatusService) + s.syncClient = NewSyncClient(spaceId, requestManager, peerManager) + s.syncHandler = newSyncAclHandler(storage.Id(), s, s.syncClient, syncStatus) + return err +} + +func (s *syncAcl) AddRawRecord(rawRec *consensusproto.RawRecordWithId) (err error) { + if s.isClosed { + return ErrSyncAclClosed + } + err = s.AclList.AddRawRecord(rawRec) + if err != nil { + return + } + headUpdate := s.syncClient.CreateHeadUpdate(s, []*consensusproto.RawRecordWithId{rawRec}) + s.headUpdater.UpdateHeads(s.Id(), []string{rawRec.Id}) + s.syncClient.Broadcast(headUpdate) + return +} + +func (s *syncAcl) AddRawRecords(rawRecords []*consensusproto.RawRecordWithId) (err error) { + if s.isClosed { + return ErrSyncAclClosed + } + err = s.AclList.AddRawRecords(rawRecords) + if err != nil { + return + } + headUpdate := s.syncClient.CreateHeadUpdate(s, rawRecords) + s.headUpdater.UpdateHeads(s.Id(), []string{rawRecords[len(rawRecords)-1].Id}) + s.syncClient.Broadcast(headUpdate) + return +} + +func (s *syncAcl) SyncWithPeer(ctx context.Context, peerId string) (err error) { + s.Lock() + defer s.Unlock() + headUpdate := s.syncClient.CreateHeadUpdate(s, nil) + return s.syncClient.SendUpdate(peerId, headUpdate) +} + +func (s *syncAcl) Close(ctx context.Context) (err error) { + s.Lock() + defer s.Unlock() + s.isClosed = true + return +} + +func (s *syncAcl) Name() (name string) { + return CName } diff --git a/commonspace/object/acl/syncacl/syncaclhandler.go b/commonspace/object/acl/syncacl/syncaclhandler.go index 5880f70f..2d76cc46 100644 --- a/commonspace/object/acl/syncacl/syncaclhandler.go +++ b/commonspace/object/acl/syncacl/syncaclhandler.go @@ -2,30 +2,81 @@ package syncacl import ( "context" - "fmt" - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "errors" + "github.com/anyproto/any-sync/commonspace/object/acl/list" + "github.com/anyproto/any-sync/commonspace/objectsync/synchandler" "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/commonspace/syncstatus" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/gogo/protobuf/proto" +) + +var ( + ErrMessageIsRequest = errors.New("message is request") + ErrMessageIsNotRequest = errors.New("message is not request") ) type syncAclHandler struct { - acl list.AclList + aclList list.AclList + syncClient SyncClient + syncProtocol AclSyncProtocol + syncStatus syncstatus.StatusUpdater + spaceId string } -func (s *syncAclHandler) HandleMessage(ctx context.Context, senderId string, req *spacesyncproto.ObjectSyncMessage) (err error) { - aclMsg := &aclrecordproto.AclSyncMessage{} - if err = aclMsg.Unmarshal(req.Payload); err != nil { +func newSyncAclHandler(spaceId string, aclList list.AclList, syncClient SyncClient, syncStatus syncstatus.StatusUpdater) synchandler.SyncHandler { + return &syncAclHandler{ + aclList: aclList, + syncClient: syncClient, + syncProtocol: newAclSyncProtocol(spaceId, aclList, syncClient), + syncStatus: syncStatus, + spaceId: spaceId, + } +} + +func (s *syncAclHandler) HandleMessage(ctx context.Context, senderId string, message *spacesyncproto.ObjectSyncMessage) (err error) { + unmarshalled := &consensusproto.LogSyncMessage{} + err = proto.Unmarshal(message.Payload, unmarshalled) + if err != nil { return } - content := aclMsg.GetContent() + content := unmarshalled.GetContent() + head := consensusproto.GetHead(unmarshalled) + s.syncStatus.HeadsReceive(senderId, s.aclList.Id(), []string{head}) + s.aclList.Lock() + defer s.aclList.Unlock() switch { - case content.GetAddRecords() != nil: - return s.handleAddRecords(ctx, senderId, content.GetAddRecords()) - default: - return fmt.Errorf("unexpected aclSync message: %T", content.Value) + case content.GetHeadUpdate() != nil: + var syncReq *consensusproto.LogSyncMessage + syncReq, err = s.syncProtocol.HeadUpdate(ctx, senderId, content.GetHeadUpdate()) + if err != nil || syncReq == nil { + return + } + return s.syncClient.QueueRequest(senderId, syncReq) + case content.GetFullSyncRequest() != nil: + return ErrMessageIsRequest + case content.GetFullSyncResponse() != nil: + return s.syncProtocol.FullSyncResponse(ctx, senderId, content.GetFullSyncResponse()) } -} - -func (s *syncAclHandler) handleAddRecords(ctx context.Context, senderId string, addRecord *aclrecordproto.AclAddRecords) (err error) { return } + +func (s *syncAclHandler) HandleRequest(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (response *spacesyncproto.ObjectSyncMessage, err error) { + unmarshalled := &consensusproto.LogSyncMessage{} + err = proto.Unmarshal(request.Payload, unmarshalled) + if err != nil { + return + } + fullSyncRequest := unmarshalled.GetContent().GetFullSyncRequest() + if fullSyncRequest == nil { + return nil, ErrMessageIsNotRequest + } + s.aclList.Lock() + defer s.aclList.Unlock() + aclResp, err := s.syncProtocol.FullSyncRequest(ctx, senderId, fullSyncRequest) + if err != nil { + return + } + return spacesyncproto.MarshallSyncMessage(aclResp, s.spaceId, s.aclList.Id()) +} diff --git a/commonspace/object/acl/syncacl/syncaclhandler_test.go b/commonspace/object/acl/syncacl/syncaclhandler_test.go new file mode 100644 index 00000000..bdff2e83 --- /dev/null +++ b/commonspace/object/acl/syncacl/syncaclhandler_test.go @@ -0,0 +1,233 @@ +package syncacl + +import ( + "context" + "fmt" + "github.com/anyproto/any-sync/commonspace/object/acl/list/mock_list" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl/mock_syncacl" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/commonspace/syncstatus" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "sync" + "testing" +) + +type testAclMock struct { + *mock_list.MockAclList + m sync.RWMutex +} + +func newTestAclMock(mockAcl *mock_list.MockAclList) *testAclMock { + return &testAclMock{ + MockAclList: mockAcl, + } +} + +func (t *testAclMock) Lock() { + t.m.Lock() +} + +func (t *testAclMock) RLock() { + t.m.RLock() +} + +func (t *testAclMock) Unlock() { + t.m.Unlock() +} + +func (t *testAclMock) RUnlock() { + t.m.RUnlock() +} + +func (t *testAclMock) TryLock() bool { + return t.m.TryLock() +} + +func (t *testAclMock) TryRLock() bool { + return t.m.TryRLock() +} + +type syncHandlerFixture struct { + ctrl *gomock.Controller + syncClientMock *mock_syncacl.MockSyncClient + aclMock *testAclMock + syncProtocolMock *mock_syncacl.MockAclSyncProtocol + spaceId string + senderId string + aclId string + + syncHandler *syncAclHandler +} + +func newSyncHandlerFixture(t *testing.T) *syncHandlerFixture { + ctrl := gomock.NewController(t) + aclMock := newTestAclMock(mock_list.NewMockAclList(ctrl)) + syncClientMock := mock_syncacl.NewMockSyncClient(ctrl) + syncProtocolMock := mock_syncacl.NewMockAclSyncProtocol(ctrl) + spaceId := "spaceId" + + syncHandler := &syncAclHandler{ + aclList: aclMock, + syncClient: syncClientMock, + syncProtocol: syncProtocolMock, + syncStatus: syncstatus.NewNoOpSyncStatus(), + spaceId: spaceId, + } + return &syncHandlerFixture{ + ctrl: ctrl, + syncClientMock: syncClientMock, + aclMock: aclMock, + syncProtocolMock: syncProtocolMock, + spaceId: spaceId, + senderId: "senderId", + aclId: "aclId", + syncHandler: syncHandler, + } +} + +func (fx *syncHandlerFixture) stop() { + fx.ctrl.Finish() +} + +func TestSyncAclHandler_HandleMessage(t *testing.T) { + ctx := context.Background() + t.Run("handle head update, request returned", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + logMessage := consensusproto.WrapHeadUpdate(headUpdate, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(logMessage, fx.spaceId, fx.aclId) + + syncReq := &consensusproto.LogSyncMessage{} + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.syncProtocolMock.EXPECT().HeadUpdate(ctx, fx.senderId, gomock.Any()).Return(syncReq, nil) + fx.syncClientMock.EXPECT().QueueRequest(fx.senderId, syncReq).Return(nil) + + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) + require.NoError(t, err) + }) + t.Run("handle head update, no request", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + logMessage := consensusproto.WrapHeadUpdate(headUpdate, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(logMessage, fx.spaceId, fx.aclId) + + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.syncProtocolMock.EXPECT().HeadUpdate(ctx, fx.senderId, gomock.Any()).Return(nil, nil) + + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) + require.NoError(t, err) + }) + t.Run("handle head update, returned error", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + logMessage := consensusproto.WrapHeadUpdate(headUpdate, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(logMessage, fx.spaceId, fx.aclId) + + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + expectedErr := fmt.Errorf("some error") + fx.syncProtocolMock.EXPECT().HeadUpdate(ctx, fx.senderId, gomock.Any()).Return(nil, expectedErr) + + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) + require.Error(t, expectedErr, err) + }) + t.Run("handle full sync request is forbidden", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + fullRequest := &consensusproto.LogFullSyncRequest{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + logMessage := consensusproto.WrapFullRequest(fullRequest, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(logMessage, fx.spaceId, fx.aclId) + + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) + require.Error(t, ErrMessageIsRequest, err) + }) + t.Run("handle full sync response, no error", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + fullResponse := &consensusproto.LogFullSyncResponse{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + logMessage := consensusproto.WrapFullResponse(fullResponse, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(logMessage, fx.spaceId, fx.aclId) + + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.syncProtocolMock.EXPECT().FullSyncResponse(ctx, fx.senderId, gomock.Any()).Return(nil) + + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) + require.NoError(t, err) + }) +} + +func TestSyncAclHandler_HandleRequest(t *testing.T) { + ctx := context.Background() + t.Run("handle full sync request, no error", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + fullRequest := &consensusproto.LogFullSyncRequest{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + logMessage := consensusproto.WrapFullRequest(fullRequest, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(logMessage, fx.spaceId, fx.aclId) + fullResp := &consensusproto.LogSyncMessage{ + Content: &consensusproto.LogSyncContentValue{ + Value: &consensusproto.LogSyncContentValue_FullSyncResponse{ + FullSyncResponse: &consensusproto.LogFullSyncResponse{ + Head: "returnedHead", + }, + }, + }, + } + + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + fx.syncProtocolMock.EXPECT().FullSyncRequest(ctx, fx.senderId, gomock.Any()).Return(fullResp, nil) + res, err := fx.syncHandler.HandleRequest(ctx, fx.senderId, objectMsg) + require.NoError(t, err) + unmarshalled := &consensusproto.LogSyncMessage{} + err = proto.Unmarshal(res.Payload, unmarshalled) + if err != nil { + return + } + require.Equal(t, "returnedHead", consensusproto.GetHead(unmarshalled)) + }) + t.Run("handle other message returns error", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + chWithId := &consensusproto.RawRecordWithId{} + headUpdate := &consensusproto.LogHeadUpdate{ + Head: "h1", + Records: []*consensusproto.RawRecordWithId{chWithId}, + } + logMessage := consensusproto.WrapHeadUpdate(headUpdate, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(logMessage, fx.spaceId, fx.aclId) + + fx.aclMock.EXPECT().Id().AnyTimes().Return(fx.aclId) + _, err := fx.syncHandler.HandleRequest(ctx, fx.senderId, objectMsg) + require.Error(t, ErrMessageIsNotRequest, err) + }) +} diff --git a/commonspace/object/acl/syncacl/syncclient.go b/commonspace/object/acl/syncacl/syncclient.go new file mode 100644 index 00000000..28b077f9 --- /dev/null +++ b/commonspace/object/acl/syncacl/syncclient.go @@ -0,0 +1,70 @@ +package syncacl + +import ( + "context" + + "github.com/anyproto/any-sync/commonspace/peermanager" + "github.com/anyproto/any-sync/commonspace/requestmanager" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/consensus/consensusproto" + "go.uber.org/zap" +) + +type SyncClient interface { + RequestFactory + Broadcast(msg *consensusproto.LogSyncMessage) + SendUpdate(peerId string, msg *consensusproto.LogSyncMessage) (err error) + QueueRequest(peerId string, msg *consensusproto.LogSyncMessage) (err error) + SendRequest(ctx context.Context, peerId string, msg *consensusproto.LogSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) +} + +type syncClient struct { + RequestFactory + spaceId string + requestManager requestmanager.RequestManager + peerManager peermanager.PeerManager +} + +func NewSyncClient(spaceId string, requestManager requestmanager.RequestManager, peerManager peermanager.PeerManager) SyncClient { + return &syncClient{ + RequestFactory: &requestFactory{}, + spaceId: spaceId, + requestManager: requestManager, + peerManager: peerManager, + } +} + +func (s *syncClient) Broadcast(msg *consensusproto.LogSyncMessage) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, msg.Id) + if err != nil { + return + } + err = s.peerManager.Broadcast(context.Background(), objMsg) + if err != nil { + log.Debug("broadcast error", zap.Error(err)) + } +} + +func (s *syncClient) SendUpdate(peerId string, msg *consensusproto.LogSyncMessage) (err error) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, msg.Id) + if err != nil { + return + } + return s.peerManager.SendPeer(context.Background(), peerId, objMsg) +} + +func (s *syncClient) SendRequest(ctx context.Context, peerId string, msg *consensusproto.LogSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, msg.Id) + if err != nil { + return + } + return s.requestManager.SendRequest(ctx, peerId, objMsg) +} + +func (s *syncClient) QueueRequest(peerId string, msg *consensusproto.LogSyncMessage) (err error) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, msg.Id) + if err != nil { + return + } + return s.requestManager.QueueRequest(peerId, objMsg) +} diff --git a/commonspace/object/tree/exporter/treeimport.go b/commonspace/object/tree/exporter/treeimport.go index 2e83ab11..25ffa499 100644 --- a/commonspace/object/tree/exporter/treeimport.go +++ b/commonspace/object/tree/exporter/treeimport.go @@ -15,7 +15,7 @@ type TreeImportParams struct { } func ImportHistoryTree(params TreeImportParams) (tree objecttree.ReadableObjectTree, err error) { - aclList, err := list.BuildAclList(params.ListStorage) + aclList, err := list.BuildAclList(params.ListStorage, list.NoOpAcceptorVerifier{}) if err != nil { return } diff --git a/commonspace/object/tree/objecttree/mock_objecttree/mock_objecttree.go b/commonspace/object/tree/objecttree/mock_objecttree/mock_objecttree.go index 1f0897a5..3e349e9d 100644 --- a/commonspace/object/tree/objecttree/mock_objecttree/mock_objecttree.go +++ b/commonspace/object/tree/objecttree/mock_objecttree/mock_objecttree.go @@ -13,7 +13,7 @@ import ( objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" treestorage "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockObjectTree is a mock of ObjectTree interface. diff --git a/commonspace/object/tree/objecttree/objecttree.go b/commonspace/object/tree/objecttree/objecttree.go index 56a72ff9..2733530f 100644 --- a/commonspace/object/tree/objecttree/objecttree.go +++ b/commonspace/object/tree/objecttree/objecttree.go @@ -4,11 +4,11 @@ package objecttree import ( "context" "errors" - "github.com/anyproto/any-sync/util/crypto" "sync" "time" - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "github.com/anyproto/any-sync/util/crypto" + "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" @@ -248,9 +248,7 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt pubKey = content.Key.GetPublic() readKeyId string ) - canWrite := state.HasPermission(pubKey, aclrecordproto.AclUserPermissions_Writer) || - state.HasPermission(pubKey, aclrecordproto.AclUserPermissions_Admin) - if !canWrite { + if !state.Permissions(pubKey).CanWrite() { err = list.ErrInsufficientPermissions return } diff --git a/commonspace/object/tree/objecttree/objecttree_test.go b/commonspace/object/tree/objecttree/objecttree_test.go index 0dd255f8..1ac1a844 100644 --- a/commonspace/object/tree/objecttree/objecttree_test.go +++ b/commonspace/object/tree/objecttree/objecttree_test.go @@ -3,6 +3,9 @@ package objecttree import ( "context" "fmt" + "testing" + "time" + "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" @@ -10,8 +13,6 @@ import ( "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" - "time" ) type testTreeContext struct { @@ -123,6 +124,7 @@ func TestObjectTree(t *testing.T) { require.NoError(t, err) require.GreaterOrEqual(t, start.Unix(), ch.Timestamp) require.LessOrEqual(t, end.Unix(), ch.Timestamp) + require.Equal(t, res.Added[0].Id, oTree.(*objectTree).tree.lastIteratedHeadId) }) t.Run("timestamp is set correctly", func(t *testing.T) { someTs := time.Now().Add(time.Hour).Unix() @@ -139,6 +141,7 @@ func TestObjectTree(t *testing.T) { ch, err := oTree.(*objectTree).changeBuilder.Unmarshall(res.Added[0], true) require.NoError(t, err) require.Equal(t, ch.Timestamp, someTs) + require.Equal(t, res.Added[0].Id, oTree.(*objectTree).tree.lastIteratedHeadId) }) }) diff --git a/commonspace/object/tree/objecttree/objecttreevalidator.go b/commonspace/object/tree/objecttree/objecttreevalidator.go index 59b74d58..7515042b 100644 --- a/commonspace/object/tree/objecttree/objecttreevalidator.go +++ b/commonspace/object/tree/objecttree/objecttreevalidator.go @@ -3,7 +3,7 @@ package objecttree import ( "context" "fmt" - "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/tree/treestorage" "github.com/anyproto/any-sync/util/slice" @@ -52,20 +52,18 @@ func (v *objectTreeValidator) ValidateNewChanges(tree *Tree, aclList list.AclLis func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c *Change) (err error) { var ( - perm list.AclUserState - state = aclList.AclState() + userState list.AclUserState + state = aclList.AclState() ) // checking if the user could write - perm, err = state.StateAtRecord(c.AclHeadId, c.Identity) + userState, err = state.StateAtRecord(c.AclHeadId, c.Identity) if err != nil { return } - - if perm.Permissions != aclrecordproto.AclUserPermissions_Writer && perm.Permissions != aclrecordproto.AclUserPermissions_Admin { + if !userState.Permissions.CanWrite() { err = list.ErrInsufficientPermissions return } - if c.Id == tree.RootId() { return } diff --git a/commonspace/object/tree/objecttree/rawloader.go b/commonspace/object/tree/objecttree/rawloader.go index 894a6494..e3d7df25 100644 --- a/commonspace/object/tree/objecttree/rawloader.go +++ b/commonspace/object/tree/objecttree/rawloader.go @@ -2,10 +2,11 @@ package objecttree import ( "context" + "time" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/util/slice" - "time" ) type rawChangeLoader struct { @@ -22,6 +23,7 @@ type rawCacheEntry struct { change *Change rawChange *treechangeproto.RawTreeChangeWithId position int + removed bool } func newStorageLoader(treeStorage treestorage.TreeStorage, changeBuilder ChangeBuilder) *rawChangeLoader { @@ -126,7 +128,6 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi if err != nil { continue } - entry.position = -1 r.cache[b] = entry existingBreakpoints = append(existingBreakpoints, b) } @@ -135,8 +136,7 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi dfs := func( commonSnapshot string, heads []string, - startCounter int, - shouldVisit func(counter int, mapExists bool) bool, + shouldVisit func(entry rawCacheEntry, mapExists bool) bool, visit func(entry rawCacheEntry) rawCacheEntry) bool { // resetting stack @@ -150,7 +150,7 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi r.idStack = r.idStack[:len(r.idStack)-1] entry, exists := r.cache[id] - if !shouldVisit(entry.position, exists) { + if !shouldVisit(entry, exists) { continue } if id == commonSnapshot { @@ -159,7 +159,6 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi } if !exists { entry, err = r.loadEntry(id) - entry.position = -1 if err != nil { continue } @@ -174,7 +173,7 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi break } prevEntry, exists := r.cache[prev] - if !shouldVisit(prevEntry.position, exists) { + if !shouldVisit(prevEntry, exists) { continue } r.idStack = append(r.idStack, prev) @@ -187,8 +186,8 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi r.idStack = append(r.idStack, heads...) var buffer []*treechangeproto.RawTreeChangeWithId - rootVisited := dfs(commonSnapshot, heads, 0, - func(counter int, mapExists bool) bool { + rootVisited := dfs(commonSnapshot, heads, + func(_ rawCacheEntry, mapExists bool) bool { return !mapExists }, func(entry rawCacheEntry) rawCacheEntry { @@ -213,11 +212,13 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi } // marking all visited as nil - dfs(commonSnapshot, existingBreakpoints, len(buffer), - func(counter int, mapExists bool) bool { - return !mapExists || counter < len(buffer) + dfs(commonSnapshot, existingBreakpoints, + func(entry rawCacheEntry, mapExists bool) bool { + // only going through already loaded changes + return mapExists && !entry.removed }, func(entry rawCacheEntry) rawCacheEntry { + entry.removed = true if entry.position != -1 { buffer[entry.position] = nil } @@ -248,6 +249,7 @@ func (r *rawChangeLoader) loadEntry(id string) (entry rawCacheEntry, err error) entry = rawCacheEntry{ change: change, rawChange: rawChange, + position: -1, } return } diff --git a/commonspace/object/tree/objecttree/tree.go b/commonspace/object/tree/objecttree/tree.go index 51a3b444..1a070e4c 100644 --- a/commonspace/object/tree/objecttree/tree.go +++ b/commonspace/object/tree/objecttree/tree.go @@ -82,6 +82,7 @@ func (t *Tree) AddMergedHead(c *Change) error { } } t.headIds = []string{c.Id} + t.lastIteratedHeadId = c.Id return nil } diff --git a/commonspace/object/tree/objecttree/tree_test.go b/commonspace/object/tree/objecttree/tree_test.go index 4bda3527..3fe0649c 100644 --- a/commonspace/object/tree/objecttree/tree_test.go +++ b/commonspace/object/tree/objecttree/tree_test.go @@ -2,10 +2,12 @@ package objecttree import ( "fmt" - "github.com/stretchr/testify/assert" "math/rand" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func newChange(id string, snapshotId string, prevIds ...string) *Change { @@ -26,6 +28,17 @@ func newSnapshot(id, snapshotId string, prevIds ...string) *Change { } } +func TestTree_AddMergedHead(t *testing.T) { + tr := new(Tree) + _, _ = tr.Add( + newSnapshot("root", ""), + newChange("one", "root", "root"), + ) + require.Equal(t, tr.lastIteratedHeadId, "one") + tr.AddMergedHead(newChange("two", "root", "one")) + require.Equal(t, tr.lastIteratedHeadId, "two") +} + func TestTree_Add(t *testing.T) { t.Run("add first el", func(t *testing.T) { tr := new(Tree) diff --git a/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go b/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go index 0f9d40ba..3a5984ac 100644 --- a/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go +++ b/commonspace/object/tree/synctree/mock_synctree/mock_synctree.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/anyproto/any-sync/commonspace/object/tree/synctree (interfaces: SyncTree,ReceiveQueue,HeadNotifiable) +// Source: github.com/anyproto/any-sync/commonspace/object/tree/synctree (interfaces: SyncTree,ReceiveQueue,HeadNotifiable,SyncClient,RequestFactory,TreeSyncProtocol) // Package mock_synctree is a generated GoMock package. package mock_synctree @@ -15,7 +15,7 @@ import ( treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" treestorage "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockSyncTree is a mock of SyncTree interface. @@ -186,6 +186,21 @@ func (mr *MockSyncTreeMockRecorder) HandleMessage(arg0, arg1, arg2 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockSyncTree)(nil).HandleMessage), arg0, arg1, arg2) } +// HandleRequest mocks base method. +func (m *MockSyncTree) HandleRequest(arg0 context.Context, arg1 string, arg2 *spacesyncproto.ObjectSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HandleRequest indicates an expected call of HandleRequest. +func (mr *MockSyncTreeMockRecorder) HandleRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleRequest", reflect.TypeOf((*MockSyncTree)(nil).HandleRequest), arg0, arg1, arg2) +} + // HasChanges mocks base method. func (m *MockSyncTree) HasChanges(arg0 ...string) bool { m.ctrl.T.Helper() @@ -590,3 +605,287 @@ func (mr *MockHeadNotifiableMockRecorder) UpdateHeads(arg0, arg1 interface{}) *g mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHeads", reflect.TypeOf((*MockHeadNotifiable)(nil).UpdateHeads), arg0, arg1) } + +// MockSyncClient is a mock of SyncClient interface. +type MockSyncClient struct { + ctrl *gomock.Controller + recorder *MockSyncClientMockRecorder +} + +// MockSyncClientMockRecorder is the mock recorder for MockSyncClient. +type MockSyncClientMockRecorder struct { + mock *MockSyncClient +} + +// NewMockSyncClient creates a new mock instance. +func NewMockSyncClient(ctrl *gomock.Controller) *MockSyncClient { + mock := &MockSyncClient{ctrl: ctrl} + mock.recorder = &MockSyncClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSyncClient) EXPECT() *MockSyncClientMockRecorder { + return m.recorder +} + +// Broadcast mocks base method. +func (m *MockSyncClient) Broadcast(arg0 *treechangeproto.TreeSyncMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Broadcast", arg0) +} + +// Broadcast indicates an expected call of Broadcast. +func (mr *MockSyncClientMockRecorder) Broadcast(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockSyncClient)(nil).Broadcast), arg0) +} + +// CreateFullSyncRequest mocks base method. +func (m *MockSyncClient) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest. +func (mr *MockSyncClientMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncRequest), arg0, arg1, arg2) +} + +// CreateFullSyncResponse mocks base method. +func (m *MockSyncClient) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse. +func (mr *MockSyncClientMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncResponse), arg0, arg1, arg2) +} + +// CreateHeadUpdate mocks base method. +func (m *MockSyncClient) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateHeadUpdate indicates an expected call of CreateHeadUpdate. +func (mr *MockSyncClientMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockSyncClient)(nil).CreateHeadUpdate), arg0, arg1) +} + +// CreateNewTreeRequest mocks base method. +func (m *MockSyncClient) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateNewTreeRequest") + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest. +func (mr *MockSyncClientMockRecorder) CreateNewTreeRequest() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateNewTreeRequest)) +} + +// QueueRequest mocks base method. +func (m *MockSyncClient) QueueRequest(arg0, arg1 string, arg2 *treechangeproto.TreeSyncMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueueRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// QueueRequest indicates an expected call of QueueRequest. +func (mr *MockSyncClientMockRecorder) QueueRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueRequest", reflect.TypeOf((*MockSyncClient)(nil).QueueRequest), arg0, arg1, arg2) +} + +// SendRequest mocks base method. +func (m *MockSyncClient) SendRequest(arg0 context.Context, arg1, arg2 string, arg3 *treechangeproto.TreeSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendRequest", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendRequest indicates an expected call of SendRequest. +func (mr *MockSyncClientMockRecorder) SendRequest(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRequest", reflect.TypeOf((*MockSyncClient)(nil).SendRequest), arg0, arg1, arg2, arg3) +} + +// SendUpdate mocks base method. +func (m *MockSyncClient) SendUpdate(arg0, arg1 string, arg2 *treechangeproto.TreeSyncMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendUpdate", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendUpdate indicates an expected call of SendUpdate. +func (mr *MockSyncClientMockRecorder) SendUpdate(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendUpdate", reflect.TypeOf((*MockSyncClient)(nil).SendUpdate), arg0, arg1, arg2) +} + +// MockRequestFactory is a mock of RequestFactory interface. +type MockRequestFactory struct { + ctrl *gomock.Controller + recorder *MockRequestFactoryMockRecorder +} + +// MockRequestFactoryMockRecorder is the mock recorder for MockRequestFactory. +type MockRequestFactoryMockRecorder struct { + mock *MockRequestFactory +} + +// NewMockRequestFactory creates a new mock instance. +func NewMockRequestFactory(ctrl *gomock.Controller) *MockRequestFactory { + mock := &MockRequestFactory{ctrl: ctrl} + mock.recorder = &MockRequestFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRequestFactory) EXPECT() *MockRequestFactoryMockRecorder { + return m.recorder +} + +// CreateFullSyncRequest mocks base method. +func (m *MockRequestFactory) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest. +func (mr *MockRequestFactoryMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncRequest), arg0, arg1, arg2) +} + +// CreateFullSyncResponse mocks base method. +func (m *MockRequestFactory) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse. +func (mr *MockRequestFactoryMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncResponse), arg0, arg1, arg2) +} + +// CreateHeadUpdate mocks base method. +func (m *MockRequestFactory) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateHeadUpdate indicates an expected call of CreateHeadUpdate. +func (mr *MockRequestFactoryMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockRequestFactory)(nil).CreateHeadUpdate), arg0, arg1) +} + +// CreateNewTreeRequest mocks base method. +func (m *MockRequestFactory) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateNewTreeRequest") + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + return ret0 +} + +// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest. +func (mr *MockRequestFactoryMockRecorder) CreateNewTreeRequest() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockRequestFactory)(nil).CreateNewTreeRequest)) +} + +// MockTreeSyncProtocol is a mock of TreeSyncProtocol interface. +type MockTreeSyncProtocol struct { + ctrl *gomock.Controller + recorder *MockTreeSyncProtocolMockRecorder +} + +// MockTreeSyncProtocolMockRecorder is the mock recorder for MockTreeSyncProtocol. +type MockTreeSyncProtocolMockRecorder struct { + mock *MockTreeSyncProtocol +} + +// NewMockTreeSyncProtocol creates a new mock instance. +func NewMockTreeSyncProtocol(ctrl *gomock.Controller) *MockTreeSyncProtocol { + mock := &MockTreeSyncProtocol{ctrl: ctrl} + mock.recorder = &MockTreeSyncProtocolMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTreeSyncProtocol) EXPECT() *MockTreeSyncProtocolMockRecorder { + return m.recorder +} + +// FullSyncRequest mocks base method. +func (m *MockTreeSyncProtocol) FullSyncRequest(arg0 context.Context, arg1 string, arg2 *treechangeproto.TreeFullSyncRequest) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FullSyncRequest", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FullSyncRequest indicates an expected call of FullSyncRequest. +func (mr *MockTreeSyncProtocolMockRecorder) FullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullSyncRequest", reflect.TypeOf((*MockTreeSyncProtocol)(nil).FullSyncRequest), arg0, arg1, arg2) +} + +// FullSyncResponse mocks base method. +func (m *MockTreeSyncProtocol) FullSyncResponse(arg0 context.Context, arg1 string, arg2 *treechangeproto.TreeFullSyncResponse) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FullSyncResponse", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// FullSyncResponse indicates an expected call of FullSyncResponse. +func (mr *MockTreeSyncProtocolMockRecorder) FullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullSyncResponse", reflect.TypeOf((*MockTreeSyncProtocol)(nil).FullSyncResponse), arg0, arg1, arg2) +} + +// HeadUpdate mocks base method. +func (m *MockTreeSyncProtocol) HeadUpdate(arg0 context.Context, arg1 string, arg2 *treechangeproto.TreeHeadUpdate) (*treechangeproto.TreeSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HeadUpdate", arg0, arg1, arg2) + ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HeadUpdate indicates an expected call of HeadUpdate. +func (mr *MockTreeSyncProtocolMockRecorder) HeadUpdate(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeadUpdate", reflect.TypeOf((*MockTreeSyncProtocol)(nil).HeadUpdate), arg0, arg1, arg2) +} diff --git a/commonspace/object/tree/synctree/syncprotocol_test.go b/commonspace/object/tree/synctree/protocolintegration_test.go similarity index 99% rename from commonspace/object/tree/synctree/syncprotocol_test.go rename to commonspace/object/tree/synctree/protocolintegration_test.go index 944d6684..4b880c24 100644 --- a/commonspace/object/tree/synctree/syncprotocol_test.go +++ b/commonspace/object/tree/synctree/protocolintegration_test.go @@ -2,6 +2,10 @@ package synctree import ( "context" + "math/rand" + "testing" + "time" + "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" @@ -10,9 +14,6 @@ import ( "github.com/anyproto/any-sync/util/slice" "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/require" - "math/rand" - "testing" - "time" ) func TestEmptyClientGetsFullHistory(t *testing.T) { diff --git a/commonspace/objectsync/requestfactory.go b/commonspace/object/tree/synctree/requestfactory.go similarity index 99% rename from commonspace/objectsync/requestfactory.go rename to commonspace/object/tree/synctree/requestfactory.go index 1f4f3c7d..8d91add8 100644 --- a/commonspace/objectsync/requestfactory.go +++ b/commonspace/object/tree/synctree/requestfactory.go @@ -1,4 +1,4 @@ -package objectsync +package synctree import ( "fmt" diff --git a/commonspace/object/tree/synctree/syncclient.go b/commonspace/object/tree/synctree/syncclient.go new file mode 100644 index 00000000..912cd5b7 --- /dev/null +++ b/commonspace/object/tree/synctree/syncclient.go @@ -0,0 +1,70 @@ +package synctree + +import ( + "context" + + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/peermanager" + "github.com/anyproto/any-sync/commonspace/requestmanager" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "go.uber.org/zap" +) + +type SyncClient interface { + RequestFactory + Broadcast(msg *treechangeproto.TreeSyncMessage) + SendUpdate(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error) + QueueRequest(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error) + SendRequest(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) +} + +type syncClient struct { + RequestFactory + spaceId string + requestManager requestmanager.RequestManager + peerManager peermanager.PeerManager +} + +func NewSyncClient(spaceId string, requestManager requestmanager.RequestManager, peerManager peermanager.PeerManager) SyncClient { + return &syncClient{ + RequestFactory: &requestFactory{}, + spaceId: spaceId, + requestManager: requestManager, + peerManager: peerManager, + } +} + +func (s *syncClient) Broadcast(msg *treechangeproto.TreeSyncMessage) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, msg.RootChange.Id) + if err != nil { + return + } + err = s.peerManager.Broadcast(context.Background(), objMsg) + if err != nil { + log.Debug("broadcast error", zap.Error(err)) + } +} + +func (s *syncClient) SendUpdate(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, objectId) + if err != nil { + return + } + return s.peerManager.SendPeer(context.Background(), peerId, objMsg) +} + +func (s *syncClient) SendRequest(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, objectId) + if err != nil { + return + } + return s.requestManager.SendRequest(ctx, peerId, objMsg) +} + +func (s *syncClient) QueueRequest(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error) { + objMsg, err := spacesyncproto.MarshallSyncMessage(msg, s.spaceId, objectId) + if err != nil { + return + } + return s.requestManager.QueueRequest(peerId, objMsg) +} diff --git a/commonspace/object/tree/synctree/synctree.go b/commonspace/object/tree/synctree/synctree.go index 2a87030a..0d4712df 100644 --- a/commonspace/object/tree/synctree/synctree.go +++ b/commonspace/object/tree/synctree/synctree.go @@ -1,4 +1,4 @@ -//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anyproto/any-sync/commonspace/object/tree/synctree SyncTree,ReceiveQueue,HeadNotifiable +//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anyproto/any-sync/commonspace/object/tree/synctree SyncTree,ReceiveQueue,HeadNotifiable,SyncClient,RequestFactory,TreeSyncProtocol package synctree import ( @@ -11,7 +11,6 @@ import ( "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" - "github.com/anyproto/any-sync/commonspace/objectsync" "github.com/anyproto/any-sync/commonspace/objectsync/synchandler" "github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/syncstatus" @@ -44,7 +43,7 @@ type SyncTree interface { type syncTree struct { objecttree.ObjectTree synchandler.SyncHandler - syncClient objectsync.SyncClient + syncClient SyncClient syncStatus syncstatus.StatusUpdater notifiable HeadNotifiable listener updatelistener.UpdateListener @@ -60,19 +59,18 @@ type ResponsiblePeersGetter interface { } type BuildDeps struct { - SpaceId string - SyncClient objectsync.SyncClient - Configuration nodeconf.NodeConf - HeadNotifiable HeadNotifiable - Listener updatelistener.UpdateListener - AclList list.AclList - SpaceStorage spacestorage.SpaceStorage - TreeStorage treestorage.TreeStorage - OnClose func(id string) - SyncStatus syncstatus.StatusUpdater - PeerGetter ResponsiblePeersGetter - BuildObjectTree objecttree.BuildObjectTreeFunc - WaitTreeRemoteSync bool + SpaceId string + SyncClient SyncClient + Configuration nodeconf.NodeConf + HeadNotifiable HeadNotifiable + Listener updatelistener.UpdateListener + AclList list.AclList + SpaceStorage spacestorage.SpaceStorage + TreeStorage treestorage.TreeStorage + OnClose func(id string) + SyncStatus syncstatus.StatusUpdater + PeerGetter ResponsiblePeersGetter + BuildObjectTree objecttree.BuildObjectTreeFunc } func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t SyncTree, err error) { @@ -119,7 +117,7 @@ func buildSyncTree(ctx context.Context, sendUpdate bool, deps BuildDeps) (t Sync if sendUpdate { headUpdate := syncTree.syncClient.CreateHeadUpdate(t, nil) // send to everybody, because everybody should know that the node or client got new tree - syncTree.syncClient.Broadcast(ctx, headUpdate) + syncTree.syncClient.Broadcast(headUpdate) } return } @@ -156,7 +154,7 @@ func (s *syncTree) AddContent(ctx context.Context, content objecttree.SignableCh } s.syncStatus.HeadsChange(s.Id(), res.Heads) headUpdate := s.syncClient.CreateHeadUpdate(s, res.Added) - s.syncClient.Broadcast(ctx, headUpdate) + s.syncClient.Broadcast(headUpdate) return } @@ -183,7 +181,7 @@ func (s *syncTree) AddRawChanges(ctx context.Context, changesPayload objecttree. s.notifiable.UpdateHeads(s.Id(), res.Heads) } headUpdate := s.syncClient.CreateHeadUpdate(s, res.Added) - s.syncClient.Broadcast(ctx, headUpdate) + s.syncClient.Broadcast(headUpdate) } return } @@ -207,18 +205,27 @@ func (s *syncTree) Delete() (err error) { } func (s *syncTree) TryClose(objectTTL time.Duration) (bool, error) { - return true, s.Close() + if !s.TryLock() { + return false, nil + } + log.Debug("closing sync tree", zap.String("id", s.Id())) + return true, s.close() } func (s *syncTree) Close() (err error) { log.Debug("closing sync tree", zap.String("id", s.Id())) + s.Lock() + return s.close() +} + +func (s *syncTree) close() (err error) { + defer s.Unlock() defer func() { log.Debug("closed sync tree", zap.Error(err), zap.String("id", s.Id())) }() - s.Lock() - defer s.Unlock() if s.isClosed { - return ErrSyncTreeClosed + err = ErrSyncTreeClosed + return } s.onClose(s.Id()) s.isClosed = true @@ -239,7 +246,7 @@ func (s *syncTree) SyncWithPeer(ctx context.Context, peerId string) (err error) s.Lock() defer s.Unlock() headUpdate := s.syncClient.CreateHeadUpdate(s, nil) - return s.syncClient.SendWithReply(ctx, peerId, headUpdate.RootChange.Id, headUpdate, "") + return s.syncClient.SendUpdate(peerId, headUpdate.RootChange.Id, headUpdate) } func (s *syncTree) afterBuild() { diff --git a/commonspace/object/tree/synctree/synctree_test.go b/commonspace/object/tree/synctree/synctree_test.go index 9900e49b..3bba33b3 100644 --- a/commonspace/object/tree/synctree/synctree_test.go +++ b/commonspace/object/tree/synctree/synctree_test.go @@ -4,21 +4,21 @@ import ( "context" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree" "github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener" "github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener/mock_updatelistener" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/objectsync" - "github.com/anyproto/any-sync/commonspace/objectsync/mock_objectsync" "github.com/anyproto/any-sync/commonspace/syncstatus" "github.com/anyproto/any-sync/nodeconf" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "testing" ) type syncTreeMatcher struct { objTree objecttree.ObjectTree - client objectsync.SyncClient + client SyncClient listener updatelistener.UpdateListener } @@ -34,8 +34,8 @@ func (s syncTreeMatcher) String() string { return "" } -func syncClientFuncCreator(client objectsync.SyncClient) func(spaceId string, factory objectsync.RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.NodeConf) objectsync.SyncClient { - return func(spaceId string, factory objectsync.RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.NodeConf) objectsync.SyncClient { +func syncClientFuncCreator(client SyncClient) func(spaceId string, factory RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.NodeConf) SyncClient { + return func(spaceId string, factory RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.NodeConf) SyncClient { return client } } @@ -46,7 +46,7 @@ func Test_BuildSyncTree(t *testing.T) { defer ctrl.Finish() updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl) - syncClientMock := mock_objectsync.NewMockSyncClient(ctrl) + syncClientMock := mock_synctree.NewMockSyncClient(ctrl) objTreeMock := newTestObjMock(mock_objecttree.NewMockObjectTree(ctrl)) tr := &syncTree{ ObjectTree: objTreeMock, @@ -73,7 +73,7 @@ func Test_BuildSyncTree(t *testing.T) { updateListenerMock.EXPECT().Update(tr) syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) - syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)) + syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate)) res, err := tr.AddRawChanges(ctx, payload) require.NoError(t, err) require.Equal(t, expectedRes, res) @@ -95,7 +95,7 @@ func Test_BuildSyncTree(t *testing.T) { updateListenerMock.EXPECT().Rebuild(tr) syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) - syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)) + syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate)) res, err := tr.AddRawChanges(ctx, payload) require.NoError(t, err) require.Equal(t, expectedRes, res) @@ -133,7 +133,7 @@ func Test_BuildSyncTree(t *testing.T) { Return(expectedRes, nil) syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate) - syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)) + syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate)) res, err := tr.AddContent(ctx, content) require.NoError(t, err) require.Equal(t, expectedRes, res) diff --git a/commonspace/object/tree/synctree/synctreehandler.go b/commonspace/object/tree/synctree/synctreehandler.go index cdb0aa2a..14f30961 100644 --- a/commonspace/object/tree/synctree/synctreehandler.go +++ b/commonspace/object/tree/synctree/synctreehandler.go @@ -2,233 +2,142 @@ package synctree import ( "context" + "errors" + "sync" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" - "github.com/anyproto/any-sync/commonspace/objectsync" "github.com/anyproto/any-sync/commonspace/objectsync/synchandler" "github.com/anyproto/any-sync/commonspace/spacesyncproto" "github.com/anyproto/any-sync/commonspace/syncstatus" "github.com/anyproto/any-sync/util/slice" "github.com/gogo/protobuf/proto" - "go.uber.org/zap" - "sync" +) + +var ( + ErrMessageIsRequest = errors.New("message is request") + ErrMessageIsNotRequest = errors.New("message is not request") + ErrMoreThanOneRequest = errors.New("more than one request for same peer") ) type syncTreeHandler struct { - objTree objecttree.ObjectTree - syncClient objectsync.SyncClient - syncStatus syncstatus.StatusUpdater - handlerLock sync.Mutex - spaceId string - queue ReceiveQueue + objTree objecttree.ObjectTree + syncClient SyncClient + syncProtocol TreeSyncProtocol + syncStatus syncstatus.StatusUpdater + spaceId string + + handlerLock sync.Mutex + pendingRequests map[string]struct{} + heads []string } const maxQueueSize = 5 -func newSyncTreeHandler(spaceId string, objTree objecttree.ObjectTree, syncClient objectsync.SyncClient, syncStatus syncstatus.StatusUpdater) synchandler.SyncHandler { +func newSyncTreeHandler(spaceId string, objTree objecttree.ObjectTree, syncClient SyncClient, syncStatus syncstatus.StatusUpdater) synchandler.SyncHandler { return &syncTreeHandler{ - objTree: objTree, - syncClient: syncClient, - syncStatus: syncStatus, - spaceId: spaceId, - queue: newReceiveQueue(maxQueueSize), + objTree: objTree, + syncProtocol: newTreeSyncProtocol(spaceId, objTree, syncClient), + syncClient: syncClient, + syncStatus: syncStatus, + spaceId: spaceId, + pendingRequests: make(map[string]struct{}), } } +func (s *syncTreeHandler) HandleRequest(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (response *spacesyncproto.ObjectSyncMessage, err error) { + unmarshalled := &treechangeproto.TreeSyncMessage{} + err = proto.Unmarshal(request.Payload, unmarshalled) + if err != nil { + return + } + fullSyncRequest := unmarshalled.GetContent().GetFullSyncRequest() + if fullSyncRequest == nil { + return nil, ErrMessageIsNotRequest + } + // setting pending requests + s.handlerLock.Lock() + _, exists := s.pendingRequests[senderId] + if exists { + s.handlerLock.Unlock() + return nil, ErrMoreThanOneRequest + } + s.pendingRequests[senderId] = struct{}{} + s.handlerLock.Unlock() + + response, err = s.handleRequest(ctx, senderId, fullSyncRequest) + + // removing pending requests + s.handlerLock.Lock() + delete(s.pendingRequests, senderId) + s.handlerLock.Unlock() + return +} + +func (s *syncTreeHandler) handleRequest(ctx context.Context, senderId string, fullSyncRequest *treechangeproto.TreeFullSyncRequest) (response *spacesyncproto.ObjectSyncMessage, err error) { + s.objTree.Lock() + defer s.objTree.Unlock() + treeResp, err := s.syncProtocol.FullSyncRequest(ctx, senderId, fullSyncRequest) + if err != nil { + return + } + response, err = spacesyncproto.MarshallSyncMessage(treeResp, s.spaceId, s.objTree.Id()) + return +} + func (s *syncTreeHandler) HandleMessage(ctx context.Context, senderId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { unmarshalled := &treechangeproto.TreeSyncMessage{} err = proto.Unmarshal(msg.Payload, unmarshalled) if err != nil { return } - s.syncStatus.HeadsReceive(senderId, msg.ObjectId, treechangeproto.GetHeads(unmarshalled)) - - queueFull := s.queue.AddMessage(senderId, unmarshalled, msg.RequestId) - if queueFull { + heads := treechangeproto.GetHeads(unmarshalled) + s.syncStatus.HeadsReceive(senderId, msg.ObjectId, heads) + s.handlerLock.Lock() + // if the update has same heads then returning not to hang on a lock + if unmarshalled.GetContent().GetHeadUpdate() != nil && slice.UnsortedEquals(heads, s.heads) { + s.handlerLock.Unlock() return } - - return s.handleMessage(ctx, senderId) + s.handlerLock.Unlock() + return s.handleMessage(ctx, unmarshalled, senderId) } -func (s *syncTreeHandler) handleMessage(ctx context.Context, senderId string) (err error) { +func (s *syncTreeHandler) handleMessage(ctx context.Context, msg *treechangeproto.TreeSyncMessage, senderId string) (err error) { s.objTree.Lock() defer s.objTree.Unlock() - msg, replyId, err := s.queue.GetMessage(senderId) - if err != nil { - return - } - - defer s.queue.ClearQueue(senderId) - - content := msg.GetContent() - switch { - case content.GetHeadUpdate() != nil: - return s.handleHeadUpdate(ctx, senderId, content.GetHeadUpdate(), replyId) - case content.GetFullSyncRequest() != nil: - return s.handleFullSyncRequest(ctx, senderId, content.GetFullSyncRequest(), replyId) - case content.GetFullSyncResponse() != nil: - return s.handleFullSyncResponse(ctx, senderId, content.GetFullSyncResponse()) - } - return -} - -func (s *syncTreeHandler) handleHeadUpdate( - ctx context.Context, - senderId string, - update *treechangeproto.TreeHeadUpdate, - replyId string) (err error) { var ( - fullRequest *treechangeproto.TreeSyncMessage - isEmptyUpdate = len(update.Changes) == 0 - objTree = s.objTree - treeId = objTree.Id() + copyHeads = make([]string, 0, len(s.objTree.Heads())) + treeId = s.objTree.Id() + content = msg.GetContent() ) - log := log.With( - zap.Strings("update heads", update.Heads), - zap.String("treeId", treeId), - zap.String("spaceId", s.spaceId), - zap.Int("len(update changes)", len(update.Changes))) - log.DebugCtx(ctx, "received head update message") + // getting old heads + copyHeads = append(copyHeads, s.objTree.Heads()...) defer func() { - if err != nil { - log.ErrorCtx(ctx, "head update finished with error", zap.Error(err)) - } else if fullRequest != nil { - cnt := fullRequest.Content.GetFullSyncRequest() - log = log.With(zap.Strings("request heads", cnt.Heads), zap.Int("len(request changes)", len(cnt.Changes))) - log.DebugCtx(ctx, "sending full sync request") - } else { - if !isEmptyUpdate { - log.DebugCtx(ctx, "head update finished correctly") + // checking if something changed + if !slice.UnsortedEquals(copyHeads, s.objTree.Heads()) { + s.handlerLock.Lock() + defer s.handlerLock.Unlock() + s.heads = s.heads[:0] + for _, h := range s.objTree.Heads() { + s.heads = append(s.heads, h) } } }() - // isEmptyUpdate is sent when the tree is brought up from cache - if isEmptyUpdate { - headEquals := slice.UnsortedEquals(objTree.Heads(), update.Heads) - log.DebugCtx(ctx, "is empty update", zap.String("treeId", objTree.Id()), zap.Bool("headEquals", headEquals)) - if headEquals { + switch { + case content.GetHeadUpdate() != nil: + var syncReq *treechangeproto.TreeSyncMessage + syncReq, err = s.syncProtocol.HeadUpdate(ctx, senderId, content.GetHeadUpdate()) + if err != nil || syncReq == nil { return } - - // we need to sync in any case - fullRequest, err = s.syncClient.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath) - if err != nil { - return - } - - return s.syncClient.SendWithReply(ctx, senderId, treeId, fullRequest, replyId) + return s.syncClient.QueueRequest(senderId, treeId, syncReq) + case content.GetFullSyncRequest() != nil: + return ErrMessageIsRequest + case content.GetFullSyncResponse() != nil: + return s.syncProtocol.FullSyncResponse(ctx, senderId, content.GetFullSyncResponse()) } - - if s.alreadyHasHeads(objTree, update.Heads) { - return - } - - _, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{ - NewHeads: update.Heads, - RawChanges: update.Changes, - }) - if err != nil { - return - } - - if s.alreadyHasHeads(objTree, update.Heads) { - return - } - - fullRequest, err = s.syncClient.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath) - if err != nil { - return - } - - return s.syncClient.SendWithReply(ctx, senderId, treeId, fullRequest, replyId) -} - -func (s *syncTreeHandler) handleFullSyncRequest( - ctx context.Context, - senderId string, - request *treechangeproto.TreeFullSyncRequest, - replyId string) (err error) { - var ( - fullResponse *treechangeproto.TreeSyncMessage - header = s.objTree.Header() - objTree = s.objTree - treeId = s.objTree.Id() - ) - - log := log.With(zap.String("senderId", senderId), - zap.Strings("request heads", request.Heads), - zap.String("treeId", treeId), - zap.String("replyId", replyId), - zap.String("spaceId", s.spaceId), - zap.Int("len(request changes)", len(request.Changes))) - log.DebugCtx(ctx, "received full sync request message") - - defer func() { - if err != nil { - log.ErrorCtx(ctx, "full sync request finished with error", zap.Error(err)) - s.syncClient.SendWithReply(ctx, senderId, treeId, treechangeproto.WrapError(treechangeproto.ErrFullSync, header), replyId) - return - } else if fullResponse != nil { - cnt := fullResponse.Content.GetFullSyncResponse() - log = log.With(zap.Strings("response heads", cnt.Heads), zap.Int("len(response changes)", len(cnt.Changes))) - log.DebugCtx(ctx, "full sync response sent") - } - }() - - if len(request.Changes) != 0 && !s.alreadyHasHeads(objTree, request.Heads) { - _, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{ - NewHeads: request.Heads, - RawChanges: request.Changes, - }) - if err != nil { - return - } - } - fullResponse, err = s.syncClient.CreateFullSyncResponse(objTree, request.Heads, request.SnapshotPath) - if err != nil { - return - } - - return s.syncClient.SendWithReply(ctx, senderId, treeId, fullResponse, replyId) -} - -func (s *syncTreeHandler) handleFullSyncResponse( - ctx context.Context, - senderId string, - response *treechangeproto.TreeFullSyncResponse) (err error) { - var ( - objTree = s.objTree - treeId = s.objTree.Id() - ) - log := log.With( - zap.Strings("heads", response.Heads), - zap.String("treeId", treeId), - zap.String("spaceId", s.spaceId), - zap.Int("len(changes)", len(response.Changes))) - log.DebugCtx(ctx, "received full sync response message") - - defer func() { - if err != nil { - log.ErrorCtx(ctx, "full sync response failed", zap.Error(err)) - } else { - log.DebugCtx(ctx, "full sync response succeeded") - } - }() - - if s.alreadyHasHeads(objTree, response.Heads) { - return - } - - _, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{ - NewHeads: response.Heads, - RawChanges: response.Changes, - }) return } - -func (s *syncTreeHandler) alreadyHasHeads(t objecttree.ObjectTree, heads []string) bool { - return slice.UnsortedEquals(t.Heads(), heads) || t.HasChanges(heads...) -} diff --git a/commonspace/object/tree/synctree/synctreehandler_test.go b/commonspace/object/tree/synctree/synctreehandler_test.go index c81ca5f4..4152a698 100644 --- a/commonspace/object/tree/synctree/synctreehandler_test.go +++ b/commonspace/object/tree/synctree/synctreehandler_test.go @@ -2,20 +2,16 @@ package synctree import ( "context" - "fmt" - "github.com/anyproto/any-sync/commonspace/objectsync" - "github.com/anyproto/any-sync/commonspace/objectsync/mock_objectsync" "sync" "testing" - "github.com/anyproto/any-sync/app/logger" - "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" "github.com/anyproto/any-sync/commonspace/syncstatus" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - "go.uber.org/zap" + "go.uber.org/mock/gomock" ) type testObjTreeMock struct { @@ -55,31 +51,40 @@ func (t *testObjTreeMock) TryRLock() bool { type syncHandlerFixture struct { ctrl *gomock.Controller - syncClientMock *mock_objectsync.MockSyncClient + syncClientMock *mock_synctree.MockSyncClient objectTreeMock *testObjTreeMock - receiveQueueMock ReceiveQueue + syncProtocolMock *mock_synctree.MockTreeSyncProtocol + spaceId string + senderId string + treeId string syncHandler *syncTreeHandler } func newSyncHandlerFixture(t *testing.T) *syncHandlerFixture { ctrl := gomock.NewController(t) - syncClientMock := mock_objectsync.NewMockSyncClient(ctrl) objectTreeMock := newTestObjMock(mock_objecttree.NewMockObjectTree(ctrl)) - receiveQueue := newReceiveQueue(5) + syncClientMock := mock_synctree.NewMockSyncClient(ctrl) + syncProtocolMock := mock_synctree.NewMockTreeSyncProtocol(ctrl) + spaceId := "spaceId" syncHandler := &syncTreeHandler{ - objTree: objectTreeMock, - syncClient: syncClientMock, - queue: receiveQueue, - syncStatus: syncstatus.NewNoOpSyncStatus(), + objTree: objectTreeMock, + syncClient: syncClientMock, + syncProtocol: syncProtocolMock, + spaceId: spaceId, + syncStatus: syncstatus.NewNoOpSyncStatus(), + pendingRequests: map[string]struct{}{}, } return &syncHandlerFixture{ ctrl: ctrl, - syncClientMock: syncClientMock, objectTreeMock: objectTreeMock, - receiveQueueMock: receiveQueue, + syncProtocolMock: syncProtocolMock, + syncClientMock: syncClientMock, syncHandler: syncHandler, + spaceId: spaceId, + senderId: "senderId", + treeId: "treeId", } } @@ -87,341 +92,149 @@ func (fx *syncHandlerFixture) stop() { fx.ctrl.Finish() } -func TestSyncHandler_HandleHeadUpdate(t *testing.T) { +func TestSyncTreeHandler_HandleMessage(t *testing.T) { ctx := context.Background() - log = logger.CtxLogger{Logger: zap.NewNop()} - fullRequest := &treechangeproto.TreeSyncMessage{ - Content: &treechangeproto.TreeSyncContentValue{ - Value: &treechangeproto.TreeSyncContentValue_FullSyncRequest{ - FullSyncRequest: &treechangeproto.TreeFullSyncRequest{}, - }, - }, - } - t.Run("head update non empty all heads added", func(t *testing.T) { + t.Run("handle head update message, heads not equal, request returned", func(t *testing.T) { fx := newSyncHandlerFixture(t) defer fx.stop() treeId := "treeId" - senderId := "senderId" chWithId := &treechangeproto.RawTreeChangeWithId{} headUpdate := &treechangeproto.TreeHeadUpdate{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, + Heads: []string{"h3"}, } treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") + objectMsg, _ := spacesyncproto.MarshallSyncMessage(treeMsg, "spaceId", treeId) - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).Times(2) - fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) - fx.objectTreeMock.EXPECT(). - AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ - NewHeads: []string{"h1"}, - RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, - })). - Return(objecttree.AddResult{}, nil) - fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(true) + syncReq := &treechangeproto.TreeSyncMessage{} + fx.syncHandler.heads = []string{"h2"} + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h2"}) + fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h3"}) + fx.syncProtocolMock.EXPECT().HeadUpdate(ctx, fx.senderId, gomock.Any()).Return(syncReq, nil) + fx.syncClientMock.EXPECT().QueueRequest(fx.senderId, fx.treeId, syncReq).Return(nil) - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) require.NoError(t, err) + require.Equal(t, []string{"h3"}, fx.syncHandler.heads) }) - t.Run("head update non empty heads not added", func(t *testing.T) { + t.Run("handle head update message, heads equal", func(t *testing.T) { fx := newSyncHandlerFixture(t) defer fx.stop() treeId := "treeId" - senderId := "senderId" chWithId := &treechangeproto.RawTreeChangeWithId{} headUpdate := &treechangeproto.TreeHeadUpdate{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, + Heads: []string{"h1"}, } treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") + objectMsg, _ := spacesyncproto.MarshallSyncMessage(treeMsg, "spaceId", treeId) - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes() - fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) - fx.objectTreeMock.EXPECT(). - AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ - NewHeads: []string{"h1"}, - RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, - })). - Return(objecttree.AddResult{}, nil) - fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) - fx.syncClientMock.EXPECT(). - CreateFullSyncRequest(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). - Return(fullRequest, nil) - fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(treeId), gomock.Eq(fullRequest), gomock.Eq("")) + fx.syncHandler.heads = []string{"h1"} + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) require.NoError(t, err) }) - t.Run("head update non empty equal heads", func(t *testing.T) { + t.Run("handle head update message, no sync request returned", func(t *testing.T) { fx := newSyncHandlerFixture(t) defer fx.stop() treeId := "treeId" - senderId := "senderId" chWithId := &treechangeproto.RawTreeChangeWithId{} headUpdate := &treechangeproto.TreeHeadUpdate{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, + Heads: []string{"h3"}, } treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") + objectMsg, _ := spacesyncproto.MarshallSyncMessage(treeMsg, "spaceId", treeId) - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes() + fx.syncHandler.heads = []string{"h2"} + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h2"}) + fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h3"}) + fx.syncProtocolMock.EXPECT().HeadUpdate(ctx, fx.senderId, gomock.Any()).Return(nil, nil) - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) require.NoError(t, err) + require.Equal(t, []string{"h3"}, fx.syncHandler.heads) }) - t.Run("head update empty", func(t *testing.T) { + t.Run("handle full sync request returns error", func(t *testing.T) { fx := newSyncHandlerFixture(t) defer fx.stop() treeId := "treeId" - senderId := "senderId" chWithId := &treechangeproto.RawTreeChangeWithId{} - headUpdate := &treechangeproto.TreeHeadUpdate{ - Heads: []string{"h1"}, - Changes: nil, - SnapshotPath: []string{"h1"}, + fullRequest := &treechangeproto.TreeFullSyncRequest{ + Heads: []string{"h3"}, } - treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") + treeMsg := treechangeproto.WrapFullRequest(fullRequest, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(treeMsg, "spaceId", treeId) - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes() - fx.syncClientMock.EXPECT(). - CreateFullSyncRequest(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). - Return(fullRequest, nil) - fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(treeId), gomock.Eq(fullRequest), gomock.Eq("")) + fx.syncHandler.heads = []string{"h2"} + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Times(3).Return([]string{"h2"}) - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) - require.NoError(t, err) + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) + require.Equal(t, err, ErrMessageIsRequest) }) - t.Run("head update empty equal heads", func(t *testing.T) { + t.Run("handle full sync response", func(t *testing.T) { fx := newSyncHandlerFixture(t) defer fx.stop() treeId := "treeId" - senderId := "senderId" - chWithId := &treechangeproto.RawTreeChangeWithId{} - headUpdate := &treechangeproto.TreeHeadUpdate{ - Heads: []string{"h1"}, - Changes: nil, - SnapshotPath: []string{"h1"}, - } - treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") - - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes() - - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) - require.NoError(t, err) - }) -} - -func TestSyncHandler_HandleFullSyncRequest(t *testing.T) { - ctx := context.Background() - log = logger.CtxLogger{Logger: zap.NewNop()} - fullResponse := &treechangeproto.TreeSyncMessage{ - Content: &treechangeproto.TreeSyncContentValue{ - Value: &treechangeproto.TreeSyncContentValue_FullSyncResponse{ - FullSyncResponse: &treechangeproto.TreeFullSyncResponse{}, - }, - }, - } - - t.Run("full sync request with change", func(t *testing.T) { - fx := newSyncHandlerFixture(t) - defer fx.stop() - treeId := "treeId" - senderId := "senderId" - chWithId := &treechangeproto.RawTreeChangeWithId{} - fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, - } - treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") - - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Header().Return(nil) - fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes() - fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) - fx.objectTreeMock.EXPECT(). - AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ - NewHeads: []string{"h1"}, - RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, - })). - Return(objecttree.AddResult{}, nil) - fx.syncClientMock.EXPECT(). - CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). - Return(fullResponse, nil) - fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(treeId), gomock.Eq(fullResponse), gomock.Eq("")) - - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) - require.NoError(t, err) - }) - - t.Run("full sync request with change same heads", func(t *testing.T) { - fx := newSyncHandlerFixture(t) - defer fx.stop() - treeId := "treeId" - senderId := "senderId" - chWithId := &treechangeproto.RawTreeChangeWithId{} - fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, - } - treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") - - fx.objectTreeMock.EXPECT(). - Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Header().Return(nil) - fx.objectTreeMock.EXPECT(). - Heads(). - Return([]string{"h1"}).AnyTimes() - fx.syncClientMock.EXPECT(). - CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). - Return(fullResponse, nil) - fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(treeId), gomock.Eq(fullResponse), gomock.Eq("")) - - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) - require.NoError(t, err) - }) - - t.Run("full sync request without change but with reply id", func(t *testing.T) { - fx := newSyncHandlerFixture(t) - defer fx.stop() - treeId := "treeId" - senderId := "senderId" - replyId := "replyId" - chWithId := &treechangeproto.RawTreeChangeWithId{} - fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ - Heads: []string{"h1"}, - SnapshotPath: []string{"h1"}, - } - treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") - objectMsg.RequestId = replyId - - fx.objectTreeMock.EXPECT(). - Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Header().Return(nil) - fx.syncClientMock.EXPECT(). - CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). - Return(fullResponse, nil) - fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(treeId), gomock.Eq(fullResponse), gomock.Eq(replyId)) - - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) - require.NoError(t, err) - }) - - t.Run("full sync request with add raw changes error", func(t *testing.T) { - fx := newSyncHandlerFixture(t) - defer fx.stop() - treeId := "treeId" - senderId := "senderId" - chWithId := &treechangeproto.RawTreeChangeWithId{} - fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, - } - treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, "") - - fx.objectTreeMock.EXPECT(). - Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT().Header().Return(nil) - fx.objectTreeMock.EXPECT(). - Heads(). - Return([]string{"h2"}) - fx.objectTreeMock.EXPECT(). - HasChanges(gomock.Eq([]string{"h1"})). - Return(false) - fx.objectTreeMock.EXPECT(). - AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ - NewHeads: []string{"h1"}, - RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, - })). - Return(objecttree.AddResult{}, fmt.Errorf("")) - fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(treeId), gomock.Any(), gomock.Eq("")) - - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) - require.Error(t, err) - }) -} - -func TestSyncHandler_HandleFullSyncResponse(t *testing.T) { - ctx := context.Background() - log = logger.CtxLogger{Logger: zap.NewNop()} - - t.Run("full sync response with change", func(t *testing.T) { - fx := newSyncHandlerFixture(t) - defer fx.stop() - treeId := "treeId" - senderId := "senderId" - replyId := "replyId" chWithId := &treechangeproto.RawTreeChangeWithId{} fullSyncResponse := &treechangeproto.TreeFullSyncResponse{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, + Heads: []string{"h3"}, } treeMsg := treechangeproto.WrapFullResponse(fullSyncResponse, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, replyId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(treeMsg, "spaceId", treeId) - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT(). - Heads(). - Return([]string{"h2"}).AnyTimes() - fx.objectTreeMock.EXPECT(). - HasChanges(gomock.Eq([]string{"h1"})). - Return(false) - fx.objectTreeMock.EXPECT(). - AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ - NewHeads: []string{"h1"}, - RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, - })). - Return(objecttree.AddResult{}, nil) + fx.syncHandler.heads = []string{"h2"} + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h2"}) + fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h3"}) + fx.syncProtocolMock.EXPECT().FullSyncResponse(ctx, fx.senderId, gomock.Any()).Return(nil) - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) - require.NoError(t, err) - }) - - t.Run("full sync response with same heads", func(t *testing.T) { - fx := newSyncHandlerFixture(t) - defer fx.stop() - treeId := "treeId" - senderId := "senderId" - replyId := "replyId" - chWithId := &treechangeproto.RawTreeChangeWithId{} - fullSyncResponse := &treechangeproto.TreeFullSyncResponse{ - Heads: []string{"h1"}, - Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, - SnapshotPath: []string{"h1"}, - } - treeMsg := treechangeproto.WrapFullResponse(fullSyncResponse, chWithId) - objectMsg, _ := objectsync.MarshallTreeMessage(treeMsg, "spaceId", treeId, replyId) - - fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId) - fx.objectTreeMock.EXPECT(). - Heads(). - Return([]string{"h1"}).AnyTimes() - - err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg) + err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg) require.NoError(t, err) }) } + +func TestSyncTreeHandler_HandleRequest(t *testing.T) { + ctx := context.Background() + + t.Run("handle request", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + treeId := "treeId" + chWithId := &treechangeproto.RawTreeChangeWithId{} + fullRequest := &treechangeproto.TreeFullSyncRequest{} + treeMsg := treechangeproto.WrapFullRequest(fullRequest, chWithId) + objectMsg, _ := spacesyncproto.MarshallSyncMessage(treeMsg, "spaceId", treeId) + + syncResp := &treechangeproto.TreeSyncMessage{} + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.syncProtocolMock.EXPECT().FullSyncRequest(ctx, fx.senderId, gomock.Any()).Return(syncResp, nil) + + res, err := fx.syncHandler.HandleRequest(ctx, fx.senderId, objectMsg) + require.NoError(t, err) + require.NotNil(t, res) + }) + + t.Run("handle other message", func(t *testing.T) { + fx := newSyncHandlerFixture(t) + defer fx.stop() + treeId := "treeId" + chWithId := &treechangeproto.RawTreeChangeWithId{} + fullResponse := &treechangeproto.TreeFullSyncResponse{} + responseMsg := treechangeproto.WrapFullResponse(fullResponse, chWithId) + headUpdate := &treechangeproto.TreeHeadUpdate{} + headUpdateMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId) + for _, msg := range []*treechangeproto.TreeSyncMessage{responseMsg, headUpdateMsg} { + objectMsg, _ := spacesyncproto.MarshallSyncMessage(msg, "spaceId", treeId) + + _, err := fx.syncHandler.HandleRequest(ctx, fx.senderId, objectMsg) + require.Equal(t, err, ErrMessageIsNotRequest) + } + }) +} diff --git a/commonspace/object/tree/synctree/treeremotegetter.go b/commonspace/object/tree/synctree/treeremotegetter.go index 3006ab5b..64cd1d9c 100644 --- a/commonspace/object/tree/synctree/treeremotegetter.go +++ b/commonspace/object/tree/synctree/treeremotegetter.go @@ -2,16 +2,19 @@ package synctree import ( "context" - "fmt" + "errors" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/net/peer" - "github.com/anyproto/any-sync/net/rpc/rpcerr" "github.com/gogo/protobuf/proto" "go.uber.org/zap" - "time" +) + +var ( + ErrNoResponsiblePeers = errors.New("no responsible peers") ) type treeRemoteGetter struct { @@ -36,7 +39,7 @@ func (t treeRemoteGetter) getPeers(ctx context.Context) (peerIds []string, err e return } if len(respPeers) == 0 { - err = fmt.Errorf("no responsible peers") + err = ErrNoResponsiblePeers return } for _, p := range respPeers { @@ -47,7 +50,7 @@ func (t treeRemoteGetter) getPeers(ctx context.Context) (peerIds []string, err e func (t treeRemoteGetter) treeRequest(ctx context.Context, peerId string) (msg *treechangeproto.TreeSyncMessage, err error) { newTreeRequest := t.deps.SyncClient.CreateNewTreeRequest() - resp, err := t.deps.SyncClient.SendSync(ctx, peerId, t.treeId, newTreeRequest) + resp, err := t.deps.SyncClient.SendRequest(ctx, peerId, t.treeId, newTreeRequest) if err != nil { return } @@ -57,37 +60,13 @@ func (t treeRemoteGetter) treeRequest(ctx context.Context, peerId string) (msg * return } -func (t treeRemoteGetter) treeRequestLoop(ctx context.Context, wait bool) (msg *treechangeproto.TreeSyncMessage, err error) { - peerIdx := 0 -Loop: - for { - select { - case <-ctx.Done(): - return nil, fmt.Errorf("waiting for object %s interrupted, context closed", t.treeId) - default: - break - } - availablePeers, err := t.getPeers(ctx) - if err != nil { - if !wait { - return nil, err - } - select { - // wait for peers to connect - case <-time.After(1 * time.Second): - continue Loop - case <-ctx.Done(): - return nil, fmt.Errorf("waiting for object %s interrupted, context closed", t.treeId) - } - } - - peerIdx = peerIdx % len(availablePeers) - msg, err = t.treeRequest(ctx, availablePeers[peerIdx]) - if err == nil || !wait { - return msg, err - } - peerIdx++ +func (t treeRemoteGetter) treeRequestLoop(ctx context.Context) (msg *treechangeproto.TreeSyncMessage, err error) { + availablePeers, err := t.getPeers(ctx) + if err != nil { + return } + // in future we will try to load from different peers + return t.treeRequest(ctx, availablePeers[0]) } func (t treeRemoteGetter) getTree(ctx context.Context) (treeStorage treestorage.TreeStorage, isRemote bool, err error) { @@ -109,22 +88,15 @@ func (t treeRemoteGetter) getTree(ctx context.Context) (treeStorage treestorage. } isRemote = true - resp, err := t.treeRequestLoop(ctx, t.deps.WaitTreeRemoteSync) + resp, err := t.treeRequestLoop(ctx) if err != nil { return } - switch { - case resp.GetContent().GetErrorResponse() != nil: - errResp := resp.GetContent().GetErrorResponse() - err = rpcerr.Err(errResp.ErrCode) - return - case resp.GetContent().GetFullSyncResponse() == nil: + fullSyncResp := resp.GetContent().GetFullSyncResponse() + if fullSyncResp == nil { err = treechangeproto.ErrUnexpected return - default: - break } - fullSyncResp := resp.GetContent().GetFullSyncResponse() payload := treestorage.TreeStorageCreatePayload{ RootRawChange: resp.RootChange, diff --git a/commonspace/object/tree/synctree/treeremotegetter_test.go b/commonspace/object/tree/synctree/treeremotegetter_test.go new file mode 100644 index 00000000..a191b678 --- /dev/null +++ b/commonspace/object/tree/synctree/treeremotegetter_test.go @@ -0,0 +1,87 @@ +package synctree + +import ( + "context" + "fmt" + "testing" + + "github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/peermanager/mock_peermanager" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/net/peer/mock_peer" + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +type treeRemoteGetterFixture struct { + ctrl *gomock.Controller + + treeGetter treeRemoteGetter + syncClientMock *mock_synctree.MockSyncClient + peerGetterMock *mock_peermanager.MockPeerManager +} + +func newTreeRemoteGetterFixture(t *testing.T) *treeRemoteGetterFixture { + ctrl := gomock.NewController(t) + syncClientMock := mock_synctree.NewMockSyncClient(ctrl) + peerGetterMock := mock_peermanager.NewMockPeerManager(ctrl) + treeGetter := treeRemoteGetter{ + deps: BuildDeps{ + SyncClient: syncClientMock, + PeerGetter: peerGetterMock, + }, + treeId: "treeId", + } + return &treeRemoteGetterFixture{ + ctrl: ctrl, + treeGetter: treeGetter, + syncClientMock: syncClientMock, + peerGetterMock: peerGetterMock, + } +} + +func (fx *treeRemoteGetterFixture) stop() { + fx.ctrl.Finish() +} + +func TestTreeRemoteGetter(t *testing.T) { + ctx := context.Background() + peerId := "peerId" + treeRequest := &treechangeproto.TreeSyncMessage{} + treeResponse := &treechangeproto.TreeSyncMessage{ + RootChange: &treechangeproto.RawTreeChangeWithId{Id: "id"}, + } + marshalled, _ := proto.Marshal(treeResponse) + objectResponse := &spacesyncproto.ObjectSyncMessage{ + Payload: marshalled, + } + + t.Run("request works", func(t *testing.T) { + fx := newTreeRemoteGetterFixture(t) + defer fx.stop() + mockPeer := mock_peer.NewMockPeer(fx.ctrl) + mockPeer.EXPECT().Id().AnyTimes().Return(peerId) + fx.peerGetterMock.EXPECT().GetResponsiblePeers(ctx).Return([]peer.Peer{mockPeer}, nil) + fx.syncClientMock.EXPECT().CreateNewTreeRequest().Return(treeRequest) + fx.syncClientMock.EXPECT().SendRequest(ctx, peerId, fx.treeGetter.treeId, treeRequest).Return(objectResponse, nil) + resp, err := fx.treeGetter.treeRequestLoop(ctx) + require.NoError(t, err) + require.Equal(t, "id", resp.RootChange.Id) + }) + + t.Run("request fails", func(t *testing.T) { + fx := newTreeRemoteGetterFixture(t) + defer fx.stop() + treeRequest := &treechangeproto.TreeSyncMessage{} + mockPeer := mock_peer.NewMockPeer(fx.ctrl) + mockPeer.EXPECT().Id().AnyTimes().Return(peerId) + fx.peerGetterMock.EXPECT().GetResponsiblePeers(ctx).Return([]peer.Peer{mockPeer}, nil) + fx.syncClientMock.EXPECT().CreateNewTreeRequest().Return(treeRequest) + fx.syncClientMock.EXPECT().SendRequest(ctx, peerId, fx.treeGetter.treeId, treeRequest).AnyTimes().Return(nil, fmt.Errorf("some")) + _, err := fx.treeGetter.treeRequestLoop(ctx) + require.Error(t, err) + }) +} diff --git a/commonspace/object/tree/synctree/treesyncprotocol.go b/commonspace/object/tree/synctree/treesyncprotocol.go new file mode 100644 index 00000000..2750e745 --- /dev/null +++ b/commonspace/object/tree/synctree/treesyncprotocol.go @@ -0,0 +1,153 @@ +package synctree + +import ( + "context" + + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/util/slice" + "go.uber.org/zap" +) + +type TreeSyncProtocol interface { + HeadUpdate(ctx context.Context, senderId string, update *treechangeproto.TreeHeadUpdate) (request *treechangeproto.TreeSyncMessage, err error) + FullSyncRequest(ctx context.Context, senderId string, request *treechangeproto.TreeFullSyncRequest) (response *treechangeproto.TreeSyncMessage, err error) + FullSyncResponse(ctx context.Context, senderId string, response *treechangeproto.TreeFullSyncResponse) (err error) +} + +type treeSyncProtocol struct { + log logger.CtxLogger + spaceId string + objTree objecttree.ObjectTree + reqFactory RequestFactory +} + +func newTreeSyncProtocol(spaceId string, objTree objecttree.ObjectTree, reqFactory RequestFactory) *treeSyncProtocol { + return &treeSyncProtocol{ + log: log.With(zap.String("spaceId", spaceId), zap.String("treeId", objTree.Id())), + spaceId: spaceId, + objTree: objTree, + reqFactory: reqFactory, + } +} + +func (t *treeSyncProtocol) HeadUpdate(ctx context.Context, senderId string, update *treechangeproto.TreeHeadUpdate) (fullRequest *treechangeproto.TreeSyncMessage, err error) { + var ( + isEmptyUpdate = len(update.Changes) == 0 + objTree = t.objTree + ) + log := t.log.With( + zap.String("senderId", senderId), + zap.Strings("update heads", update.Heads), + zap.Int("len(update changes)", len(update.Changes))) + log.DebugCtx(ctx, "received head update message") + + defer func() { + if err != nil { + log.ErrorCtx(ctx, "head update finished with error", zap.Error(err)) + } else if fullRequest != nil { + cnt := fullRequest.Content.GetFullSyncRequest() + log = log.With(zap.Strings("request heads", cnt.Heads), zap.Int("len(request changes)", len(cnt.Changes))) + log.DebugCtx(ctx, "returning full sync request") + } else { + if !isEmptyUpdate { + log.DebugCtx(ctx, "head update finished correctly") + } + } + }() + + // isEmptyUpdate is sent when the tree is brought up from cache + if isEmptyUpdate { + headEquals := slice.UnsortedEquals(objTree.Heads(), update.Heads) + log.DebugCtx(ctx, "is empty update", zap.Bool("headEquals", headEquals)) + if headEquals { + return + } + + // we need to sync in any case + fullRequest, err = t.reqFactory.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath) + return + } + + if t.hasHeads(objTree, update.Heads) { + return + } + + _, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{ + NewHeads: update.Heads, + RawChanges: update.Changes, + }) + if err != nil { + return + } + + if t.hasHeads(objTree, update.Heads) { + return + } + + fullRequest, err = t.reqFactory.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath) + return +} + +func (t *treeSyncProtocol) FullSyncRequest(ctx context.Context, senderId string, request *treechangeproto.TreeFullSyncRequest) (fullResponse *treechangeproto.TreeSyncMessage, err error) { + var ( + objTree = t.objTree + ) + log := t.log.With(zap.String("senderId", senderId), + zap.Strings("request heads", request.Heads), + zap.Int("len(request changes)", len(request.Changes))) + log.DebugCtx(ctx, "received full sync request message") + + defer func() { + if err != nil { + log.ErrorCtx(ctx, "full sync request finished with error", zap.Error(err)) + } else if fullResponse != nil { + cnt := fullResponse.Content.GetFullSyncResponse() + log = log.With(zap.Strings("response heads", cnt.Heads), zap.Int("len(response changes)", len(cnt.Changes))) + log.DebugCtx(ctx, "full sync response sent") + } + }() + + if len(request.Changes) != 0 && !t.hasHeads(objTree, request.Heads) { + _, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{ + NewHeads: request.Heads, + RawChanges: request.Changes, + }) + if err != nil { + return + } + } + fullResponse, err = t.reqFactory.CreateFullSyncResponse(objTree, request.Heads, request.SnapshotPath) + return +} + +func (t *treeSyncProtocol) FullSyncResponse(ctx context.Context, senderId string, response *treechangeproto.TreeFullSyncResponse) (err error) { + var ( + objTree = t.objTree + ) + log := log.With( + zap.Strings("heads", response.Heads), + zap.Int("len(changes)", len(response.Changes))) + log.DebugCtx(ctx, "received full sync response message") + defer func() { + if err != nil { + log.ErrorCtx(ctx, "full sync response failed", zap.Error(err)) + } else { + log.DebugCtx(ctx, "full sync response succeeded") + } + }() + if t.hasHeads(objTree, response.Heads) { + return + } + + _, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{ + NewHeads: response.Heads, + RawChanges: response.Changes, + }) + return +} + +func (t *treeSyncProtocol) hasHeads(ot objecttree.ObjectTree, heads []string) bool { + return slice.UnsortedEquals(ot.Heads(), heads) || ot.HasChanges(heads...) +} diff --git a/commonspace/object/tree/synctree/treesyncprotocol_test.go b/commonspace/object/tree/synctree/treesyncprotocol_test.go new file mode 100644 index 00000000..a88dd168 --- /dev/null +++ b/commonspace/object/tree/synctree/treesyncprotocol_test.go @@ -0,0 +1,293 @@ +package synctree + +import ( + "context" + "fmt" + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree" + "github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "testing" +) + +type treeSyncProtocolFixture struct { + log logger.CtxLogger + spaceId string + senderId string + treeId string + objectTreeMock *testObjTreeMock + reqFactory *mock_synctree.MockRequestFactory + ctrl *gomock.Controller + syncProtocol TreeSyncProtocol +} + +func newSyncProtocolFixture(t *testing.T) *treeSyncProtocolFixture { + ctrl := gomock.NewController(t) + objTree := &testObjTreeMock{ + MockObjectTree: mock_objecttree.NewMockObjectTree(ctrl), + } + spaceId := "spaceId" + reqFactory := mock_synctree.NewMockRequestFactory(ctrl) + objTree.EXPECT().Id().Return("treeId") + syncProtocol := newTreeSyncProtocol(spaceId, objTree, reqFactory) + return &treeSyncProtocolFixture{ + log: log, + spaceId: spaceId, + senderId: "senderId", + treeId: "treeId", + objectTreeMock: objTree, + reqFactory: reqFactory, + ctrl: ctrl, + syncProtocol: syncProtocol, + } +} + +func (fx *treeSyncProtocolFixture) stop() { + fx.ctrl.Finish() +} + +func TestTreeSyncProtocol_HeadUpdate(t *testing.T) { + ctx := context.Background() + fullRequest := &treechangeproto.TreeSyncMessage{ + Content: &treechangeproto.TreeSyncContentValue{ + Value: &treechangeproto.TreeSyncContentValue_FullSyncRequest{ + FullSyncRequest: &treechangeproto.TreeFullSyncRequest{}, + }, + }, + } + + t.Run("head update non empty all heads added", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &treechangeproto.RawTreeChangeWithId{} + headUpdate := &treechangeproto.TreeHeadUpdate{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).Times(2) + fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) + fx.objectTreeMock.EXPECT(). + AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ + NewHeads: []string{"h1"}, + RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, + })). + Return(objecttree.AddResult{}, nil) + fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(true) + + res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.NoError(t, err) + require.Nil(t, res) + }) + + t.Run("head update non empty equal heads", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &treechangeproto.RawTreeChangeWithId{} + headUpdate := &treechangeproto.TreeHeadUpdate{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes() + + res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.NoError(t, err) + require.Nil(t, res) + }) + + t.Run("head update empty", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + headUpdate := &treechangeproto.TreeHeadUpdate{ + Heads: []string{"h1"}, + Changes: nil, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes() + fx.reqFactory.EXPECT(). + CreateFullSyncRequest(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). + Return(fullRequest, nil) + + res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.NoError(t, err) + require.Equal(t, fullRequest, res) + }) + + t.Run("head update empty equal heads", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + headUpdate := &treechangeproto.TreeHeadUpdate{ + Heads: []string{"h1"}, + Changes: nil, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes() + + res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate) + require.NoError(t, err) + require.Nil(t, res) + }) +} + +func TestTreeSyncProtocol_FullSyncRequest(t *testing.T) { + ctx := context.Background() + fullResponse := &treechangeproto.TreeSyncMessage{ + Content: &treechangeproto.TreeSyncContentValue{ + Value: &treechangeproto.TreeSyncContentValue_FullSyncResponse{ + FullSyncResponse: &treechangeproto.TreeFullSyncResponse{}, + }, + }, + } + + t.Run("full sync request with change", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &treechangeproto.RawTreeChangeWithId{} + fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes() + fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) + fx.objectTreeMock.EXPECT(). + AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ + NewHeads: []string{"h1"}, + RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, + })). + Return(objecttree.AddResult{}, nil) + fx.reqFactory.EXPECT(). + CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). + Return(fullResponse, nil) + + res, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest) + require.NoError(t, err) + require.Equal(t, fullResponse, res) + }) + + t.Run("full sync request with change same heads", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &treechangeproto.RawTreeChangeWithId{} + fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT(). + Heads(). + Return([]string{"h1"}).AnyTimes() + fx.reqFactory.EXPECT(). + CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). + Return(fullResponse, nil) + + res, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest) + require.NoError(t, err) + require.Equal(t, fullResponse, res) + }) + + t.Run("full sync request without changes", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ + Heads: []string{"h1"}, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.reqFactory.EXPECT(). + CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})). + Return(fullResponse, nil) + + res, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest) + require.NoError(t, err) + require.Equal(t, fullResponse, res) + }) + + t.Run("full sync request with change, raw changes error", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &treechangeproto.RawTreeChangeWithId{} + fullSyncRequest := &treechangeproto.TreeFullSyncRequest{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes() + fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false) + fx.objectTreeMock.EXPECT(). + AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ + NewHeads: []string{"h1"}, + RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, + })). + Return(objecttree.AddResult{}, fmt.Errorf("addRawChanges error")) + + _, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest) + require.Error(t, err) + }) +} + +func TestTreeSyncProtocol_FullSyncResponse(t *testing.T) { + ctx := context.Background() + + t.Run("full sync response with change", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &treechangeproto.RawTreeChangeWithId{} + fullSyncResponse := &treechangeproto.TreeFullSyncResponse{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT(). + Heads(). + Return([]string{"h2"}).AnyTimes() + fx.objectTreeMock.EXPECT(). + HasChanges(gomock.Eq([]string{"h1"})). + Return(false) + fx.objectTreeMock.EXPECT(). + AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{ + NewHeads: []string{"h1"}, + RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId}, + })). + Return(objecttree.AddResult{}, nil) + + err := fx.syncProtocol.FullSyncResponse(ctx, fx.senderId, fullSyncResponse) + require.NoError(t, err) + }) + + t.Run("full sync response with same heads", func(t *testing.T) { + fx := newSyncProtocolFixture(t) + defer fx.stop() + chWithId := &treechangeproto.RawTreeChangeWithId{} + fullSyncResponse := &treechangeproto.TreeFullSyncResponse{ + Heads: []string{"h1"}, + Changes: []*treechangeproto.RawTreeChangeWithId{chWithId}, + SnapshotPath: []string{"h1"}, + } + + fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId) + fx.objectTreeMock.EXPECT(). + Heads(). + Return([]string{"h1"}).AnyTimes() + + err := fx.syncProtocol.FullSyncResponse(ctx, fx.senderId, fullSyncResponse) + require.NoError(t, err) + }) +} diff --git a/commonspace/object/tree/synctree/updatelistener/mock_updatelistener/mock_updatelistener.go b/commonspace/object/tree/synctree/updatelistener/mock_updatelistener/mock_updatelistener.go index 794b9381..050f458e 100644 --- a/commonspace/object/tree/synctree/updatelistener/mock_updatelistener/mock_updatelistener.go +++ b/commonspace/object/tree/synctree/updatelistener/mock_updatelistener/mock_updatelistener.go @@ -8,7 +8,7 @@ import ( reflect "reflect" objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockUpdateListener is a mock of UpdateListener interface. diff --git a/commonspace/object/tree/synctree/utils_test.go b/commonspace/object/tree/synctree/utils_test.go index 46936560..6aab234b 100644 --- a/commonspace/object/tree/synctree/utils_test.go +++ b/commonspace/object/tree/synctree/utils_test.go @@ -3,11 +3,11 @@ package synctree import ( "context" "fmt" + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" - "github.com/anyproto/any-sync/commonspace/objectsync" "github.com/anyproto/any-sync/commonspace/objectsync/synchandler" "github.com/anyproto/any-sync/commonspace/spacesyncproto" "github.com/anyproto/any-sync/commonspace/syncstatus" @@ -82,51 +82,124 @@ func (m *messageLog) addMessage(msg protocolMsg) { m.batcher.Add(context.Background(), msg) } +type requestPeerManager struct { + peerId string + handlers map[string]*testSyncHandler + log *messageLog +} + +func newRequestPeerManager(peerId string, log *messageLog) *requestPeerManager { + return &requestPeerManager{ + peerId: peerId, + handlers: map[string]*testSyncHandler{}, + log: log, + } +} + +func (r *requestPeerManager) addHandler(peerId string, handler *testSyncHandler) { + r.handlers[peerId] = handler +} + +func (r *requestPeerManager) Run(ctx context.Context) (err error) { + return nil +} + +func (r *requestPeerManager) Close(ctx context.Context) (err error) { + return nil +} + +func (r *requestPeerManager) SendRequest(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { + panic("should not be called") +} + +func (r *requestPeerManager) QueueRequest(peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { + pMsg := protocolMsg{ + msg: msg, + senderId: r.peerId, + receiverId: peerId, + } + r.log.addMessage(pMsg) + return r.handlers[peerId].send(context.Background(), pMsg) +} + +func (r *requestPeerManager) Init(a *app.App) (err error) { + return +} + +func (r *requestPeerManager) Name() (name string) { + return +} + +func (r *requestPeerManager) SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { + pMsg := protocolMsg{ + msg: msg, + senderId: r.peerId, + receiverId: peerId, + } + r.log.addMessage(pMsg) + return r.handlers[peerId].send(context.Background(), pMsg) +} + +func (r *requestPeerManager) Broadcast(ctx context.Context, msg *spacesyncproto.ObjectSyncMessage) (err error) { + for _, handler := range r.handlers { + pMsg := protocolMsg{ + msg: msg, + senderId: r.peerId, + receiverId: handler.peerId, + } + r.log.addMessage(pMsg) + handler.send(context.Background(), pMsg) + } + return +} + +func (r *requestPeerManager) GetResponsiblePeers(ctx context.Context) (peers []peer.Peer, err error) { + return nil, nil +} + // testSyncHandler is the wrapper around individual tree to test sync protocol type testSyncHandler struct { synchandler.SyncHandler - batcher *mb.MB[protocolMsg] - peerId string - aclList list.AclList - log *messageLog - syncClient objectsync.SyncClient - builder objecttree.BuildObjectTreeFunc + batcher *mb.MB[protocolMsg] + peerId string + aclList list.AclList + log *messageLog + syncClient SyncClient + builder objecttree.BuildObjectTreeFunc + peerManager *requestPeerManager } // createSyncHandler creates a sync handler when a tree is already created func createSyncHandler(peerId, spaceId string, objTree objecttree.ObjectTree, log *messageLog) *testSyncHandler { - factory := objectsync.NewRequestFactory() - syncClient := objectsync.NewSyncClient(spaceId, newTestMessagePool(peerId, log), factory) + peerManager := newRequestPeerManager(peerId, log) + syncClient := NewSyncClient(spaceId, peerManager, peerManager) netTree := &broadcastTree{ ObjectTree: objTree, SyncClient: syncClient, } handler := newSyncTreeHandler(spaceId, netTree, syncClient, syncstatus.NewNoOpSyncStatus()) - return newTestSyncHandler(peerId, handler) + return &testSyncHandler{ + SyncHandler: handler, + batcher: mb.New[protocolMsg](0), + peerId: peerId, + peerManager: peerManager, + } } // createEmptySyncHandler creates a sync handler when the tree will be provided later (this emulates the situation when we have no tree) func createEmptySyncHandler(peerId, spaceId string, builder objecttree.BuildObjectTreeFunc, aclList list.AclList, log *messageLog) *testSyncHandler { - factory := objectsync.NewRequestFactory() - syncClient := objectsync.NewSyncClient(spaceId, newTestMessagePool(peerId, log), factory) + peerManager := newRequestPeerManager(peerId, log) + syncClient := NewSyncClient(spaceId, peerManager, peerManager) batcher := mb.New[protocolMsg](0) return &testSyncHandler{ - batcher: batcher, - peerId: peerId, - aclList: aclList, - log: log, - syncClient: syncClient, - builder: builder, - } -} - -func newTestSyncHandler(peerId string, syncHandler synchandler.SyncHandler) *testSyncHandler { - batcher := mb.New[protocolMsg](0) - return &testSyncHandler{ - SyncHandler: syncHandler, batcher: batcher, peerId: peerId, + aclList: aclList, + log: log, + syncClient: syncClient, + builder: builder, + peerManager: peerManager, } } @@ -140,13 +213,8 @@ func (h *testSyncHandler) HandleMessage(ctx context.Context, senderId string, re return } if unmarshalled.Content.GetFullSyncResponse() == nil { - newTreeRequest := objectsync.NewRequestFactory().CreateNewTreeRequest() - var objMsg *spacesyncproto.ObjectSyncMessage - objMsg, err = objectsync.MarshallTreeMessage(newTreeRequest, request.SpaceId, request.ObjectId, "") - if err != nil { - return - } - return h.manager().SendPeer(context.Background(), senderId, objMsg) + newTreeRequest := NewRequestFactory().CreateNewTreeRequest() + return h.syncClient.QueueRequest(senderId, request.ObjectId, newTreeRequest) } fullSyncResponse := unmarshalled.Content.GetFullSyncResponse() treeStorage, _ := treestorage.NewInMemoryTreeStorage(unmarshalled.RootChange, []string{unmarshalled.RootChange.Id}, nil) @@ -166,20 +234,13 @@ func (h *testSyncHandler) HandleMessage(ctx context.Context, senderId string, re return } h.SyncHandler = newSyncTreeHandler(request.SpaceId, netTree, h.syncClient, syncstatus.NewNoOpSyncStatus()) - var objMsg *spacesyncproto.ObjectSyncMessage - newTreeRequest := objectsync.NewRequestFactory().CreateHeadUpdate(netTree, res.Added) - objMsg, err = objectsync.MarshallTreeMessage(newTreeRequest, request.SpaceId, request.ObjectId, "") - if err != nil { - return - } - return h.manager().Broadcast(context.Background(), objMsg) + headUpdate := NewRequestFactory().CreateHeadUpdate(netTree, res.Added) + h.syncClient.Broadcast(headUpdate) + return nil } -func (h *testSyncHandler) manager() *testMessagePool { - if h.SyncHandler != nil { - return h.SyncHandler.(*syncTreeHandler).syncClient.MessagePool().(*testMessagePool) - } - return h.syncClient.MessagePool().(*testMessagePool) +func (h *testSyncHandler) manager() *requestPeerManager { + return h.peerManager } func (h *testSyncHandler) tree() *broadcastTree { @@ -211,74 +272,28 @@ func (h *testSyncHandler) run(ctx context.Context, t *testing.T, wg *sync.WaitGr h.tree().Unlock() continue } - err = h.HandleMessage(ctx, res.senderId, res.msg) - if err != nil { - fmt.Println("error handling message", err.Error()) - continue + if res.description().name == "FullSyncRequest" { + resp, err := h.HandleRequest(ctx, res.senderId, res.msg) + if err != nil { + fmt.Println("error handling request", err.Error()) + continue + } + h.peerManager.SendPeer(ctx, res.senderId, resp) + } else { + err = h.HandleMessage(ctx, res.senderId, res.msg) + if err != nil { + fmt.Println("error handling message", err.Error()) + } } } }() } -// testMessagePool captures all other handlers and sends messages to them -type testMessagePool struct { - peerId string - handlers map[string]*testSyncHandler - log *messageLog -} - -func newTestMessagePool(peerId string, log *messageLog) *testMessagePool { - return &testMessagePool{handlers: map[string]*testSyncHandler{}, peerId: peerId, log: log} -} - -func (m *testMessagePool) addHandler(peerId string, handler *testSyncHandler) { - m.handlers[peerId] = handler -} - -func (m *testMessagePool) SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { - pMsg := protocolMsg{ - msg: msg, - senderId: m.peerId, - receiverId: peerId, - } - m.log.addMessage(pMsg) - return m.handlers[peerId].send(context.Background(), pMsg) -} - -func (m *testMessagePool) Broadcast(ctx context.Context, msg *spacesyncproto.ObjectSyncMessage) (err error) { - for _, handler := range m.handlers { - pMsg := protocolMsg{ - msg: msg, - senderId: m.peerId, - receiverId: handler.peerId, - } - m.log.addMessage(pMsg) - handler.send(context.Background(), pMsg) - } - return -} - -func (m *testMessagePool) GetResponsiblePeers(ctx context.Context) (peers []peer.Peer, err error) { - panic("should not be called") -} - -func (m *testMessagePool) LastUsage() time.Time { - panic("should not be called") -} - -func (m *testMessagePool) HandleMessage(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (err error) { - panic("should not be called") -} - -func (m *testMessagePool) SendSync(ctx context.Context, peerId string, message *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { - panic("should not be called") -} - // broadcastTree is the tree that broadcasts changes to everyone when changes are added // it is a simplified version of SyncTree which is easier to use in the test environment type broadcastTree struct { objecttree.ObjectTree - objectsync.SyncClient + SyncClient } func (b *broadcastTree) AddRawChanges(ctx context.Context, changes objecttree.RawChangesPayload) (objecttree.AddResult, error) { @@ -287,7 +302,7 @@ func (b *broadcastTree) AddRawChanges(ctx context.Context, changes objecttree.Ra return objecttree.AddResult{}, err } upd := b.SyncClient.CreateHeadUpdate(b.ObjectTree, res.Added) - b.SyncClient.Broadcast(ctx, upd) + b.SyncClient.Broadcast(upd) return res, nil } diff --git a/commonspace/object/tree/treestorage/mock_treestorage/mock_treestorage.go b/commonspace/object/tree/treestorage/mock_treestorage/mock_treestorage.go index 2db9a949..3a20051d 100644 --- a/commonspace/object/tree/treestorage/mock_treestorage/mock_treestorage.go +++ b/commonspace/object/tree/treestorage/mock_treestorage/mock_treestorage.go @@ -9,7 +9,7 @@ import ( reflect "reflect" treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockTreeStorage is a mock of TreeStorage interface. diff --git a/commonspace/object/treemanager/mock_treemanager/mock_treemanager.go b/commonspace/object/treemanager/mock_treemanager/mock_treemanager.go index ee285aac..3ef2892f 100644 --- a/commonspace/object/treemanager/mock_treemanager/mock_treemanager.go +++ b/commonspace/object/treemanager/mock_treemanager/mock_treemanager.go @@ -11,7 +11,7 @@ import ( app "github.com/anyproto/any-sync/app" objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" treemanager "github.com/anyproto/any-sync/commonspace/object/treemanager" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockTreeManager is a mock of TreeManager interface. diff --git a/commonspace/objectmanager/objectmanager.go b/commonspace/objectmanager/objectmanager.go new file mode 100644 index 00000000..3ebef3d5 --- /dev/null +++ b/commonspace/objectmanager/objectmanager.go @@ -0,0 +1,98 @@ +package objectmanager + +import ( + "context" + "errors" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl" + "github.com/anyproto/any-sync/commonspace/object/syncobjectgetter" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" + "github.com/anyproto/any-sync/commonspace/object/treemanager" + "github.com/anyproto/any-sync/commonspace/settings" + "github.com/anyproto/any-sync/commonspace/spacestate" + "sync/atomic" +) + +var ( + ErrSpaceClosed = errors.New("space is closed") +) + +type ObjectManager interface { + treemanager.TreeManager + AddObject(object syncobjectgetter.SyncObject) + GetObject(ctx context.Context, objectId string) (obj syncobjectgetter.SyncObject, err error) +} + +type objectManager struct { + treemanager.TreeManager + spaceId string + reservedObjects []syncobjectgetter.SyncObject + spaceIsClosed *atomic.Bool +} + +func New(manager treemanager.TreeManager) ObjectManager { + return &objectManager{ + TreeManager: manager, + } +} + +func (o *objectManager) Init(a *app.App) (err error) { + state := a.MustComponent(spacestate.CName).(*spacestate.SpaceState) + o.spaceId = state.SpaceId + o.spaceIsClosed = state.SpaceIsClosed + settingsObject := a.MustComponent(settings.CName).(settings.Settings).SettingsObject() + acl := a.MustComponent(syncacl.CName).(syncacl.SyncAcl) + o.AddObject(settingsObject) + o.AddObject(acl) + return nil +} + +func (o *objectManager) Run(ctx context.Context) (err error) { + return nil +} + +func (o *objectManager) Close(ctx context.Context) (err error) { + return nil +} + +func (o *objectManager) AddObject(object syncobjectgetter.SyncObject) { + o.reservedObjects = append(o.reservedObjects, object) +} + +func (o *objectManager) Name() string { + return treemanager.CName +} + +func (o *objectManager) GetTree(ctx context.Context, spaceId, treeId string) (objecttree.ObjectTree, error) { + if o.spaceIsClosed.Load() { + return nil, ErrSpaceClosed + } + if obj := o.getReservedObject(treeId); obj != nil { + return obj.(objecttree.ObjectTree), nil + } + return o.TreeManager.GetTree(ctx, spaceId, treeId) +} + +func (o *objectManager) getReservedObject(id string) syncobjectgetter.SyncObject { + for _, obj := range o.reservedObjects { + if obj != nil && obj.Id() == id { + return obj + } + } + return nil +} + +func (o *objectManager) GetObject(ctx context.Context, objectId string) (obj syncobjectgetter.SyncObject, err error) { + if o.spaceIsClosed.Load() { + return nil, ErrSpaceClosed + } + if obj := o.getReservedObject(objectId); obj != nil { + return obj, nil + } + t, err := o.TreeManager.GetTree(ctx, o.spaceId, objectId) + if err != nil { + return + } + obj = t.(syncobjectgetter.SyncObject) + return +} diff --git a/commonspace/objectsync/mock_objectsync/mock_objectsync.go b/commonspace/objectsync/mock_objectsync/mock_objectsync.go index 5aee48e8..d1c4666d 100644 --- a/commonspace/objectsync/mock_objectsync/mock_objectsync.go +++ b/commonspace/objectsync/mock_objectsync/mock_objectsync.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/anyproto/any-sync/commonspace/objectsync (interfaces: SyncClient) +// Source: github.com/anyproto/any-sync/commonspace/objectsync (interfaces: ObjectSync) // Package mock_objectsync is a generated GoMock package. package mock_objectsync @@ -7,146 +7,146 @@ package mock_objectsync import ( context "context" reflect "reflect" + time "time" - objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" - treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + app "github.com/anyproto/any-sync/app" objectsync "github.com/anyproto/any-sync/commonspace/objectsync" spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) -// MockSyncClient is a mock of SyncClient interface. -type MockSyncClient struct { +// MockObjectSync is a mock of ObjectSync interface. +type MockObjectSync struct { ctrl *gomock.Controller - recorder *MockSyncClientMockRecorder + recorder *MockObjectSyncMockRecorder } -// MockSyncClientMockRecorder is the mock recorder for MockSyncClient. -type MockSyncClientMockRecorder struct { - mock *MockSyncClient +// MockObjectSyncMockRecorder is the mock recorder for MockObjectSync. +type MockObjectSyncMockRecorder struct { + mock *MockObjectSync } -// NewMockSyncClient creates a new mock instance. -func NewMockSyncClient(ctrl *gomock.Controller) *MockSyncClient { - mock := &MockSyncClient{ctrl: ctrl} - mock.recorder = &MockSyncClientMockRecorder{mock} +// NewMockObjectSync creates a new mock instance. +func NewMockObjectSync(ctrl *gomock.Controller) *MockObjectSync { + mock := &MockObjectSync{ctrl: ctrl} + mock.recorder = &MockObjectSyncMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSyncClient) EXPECT() *MockSyncClientMockRecorder { +func (m *MockObjectSync) EXPECT() *MockObjectSyncMockRecorder { return m.recorder } -// Broadcast mocks base method. -func (m *MockSyncClient) Broadcast(arg0 context.Context, arg1 *treechangeproto.TreeSyncMessage) { +// Close mocks base method. +func (m *MockObjectSync) Close(arg0 context.Context) error { m.ctrl.T.Helper() - m.ctrl.Call(m, "Broadcast", arg0, arg1) -} - -// Broadcast indicates an expected call of Broadcast. -func (mr *MockSyncClientMockRecorder) Broadcast(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockSyncClient)(nil).Broadcast), arg0, arg1) -} - -// CreateFullSyncRequest mocks base method. -func (m *MockSyncClient) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2) - ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest. -func (mr *MockSyncClientMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncRequest), arg0, arg1, arg2) -} - -// CreateFullSyncResponse mocks base method. -func (m *MockSyncClient) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2) - ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse. -func (mr *MockSyncClientMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncResponse), arg0, arg1, arg2) -} - -// CreateHeadUpdate mocks base method. -func (m *MockSyncClient) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1) - ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret := m.ctrl.Call(m, "Close", arg0) + ret0, _ := ret[0].(error) return ret0 } -// CreateHeadUpdate indicates an expected call of CreateHeadUpdate. -func (mr *MockSyncClientMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call { +// Close indicates an expected call of Close. +func (mr *MockObjectSyncMockRecorder) Close(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockSyncClient)(nil).CreateHeadUpdate), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockObjectSync)(nil).Close), arg0) } -// CreateNewTreeRequest mocks base method. -func (m *MockSyncClient) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage { +// CloseThread mocks base method. +func (m *MockObjectSync) CloseThread(arg0 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateNewTreeRequest") - ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage) + ret := m.ctrl.Call(m, "CloseThread", arg0) + ret0, _ := ret[0].(error) return ret0 } -// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest. -func (mr *MockSyncClientMockRecorder) CreateNewTreeRequest() *gomock.Call { +// CloseThread indicates an expected call of CloseThread. +func (mr *MockObjectSyncMockRecorder) CloseThread(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateNewTreeRequest)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseThread", reflect.TypeOf((*MockObjectSync)(nil).CloseThread), arg0) } -// MessagePool mocks base method. -func (m *MockSyncClient) MessagePool() objectsync.MessagePool { +// HandleMessage mocks base method. +func (m *MockObjectSync) HandleMessage(arg0 context.Context, arg1 objectsync.HandleMessage) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MessagePool") - ret0, _ := ret[0].(objectsync.MessagePool) + ret := m.ctrl.Call(m, "HandleMessage", arg0, arg1) + ret0, _ := ret[0].(error) return ret0 } -// MessagePool indicates an expected call of MessagePool. -func (mr *MockSyncClientMockRecorder) MessagePool() *gomock.Call { +// HandleMessage indicates an expected call of HandleMessage. +func (mr *MockObjectSyncMockRecorder) HandleMessage(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessagePool", reflect.TypeOf((*MockSyncClient)(nil).MessagePool)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockObjectSync)(nil).HandleMessage), arg0, arg1) } -// SendSync mocks base method. -func (m *MockSyncClient) SendSync(arg0 context.Context, arg1, arg2 string, arg3 *treechangeproto.TreeSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { +// HandleRequest mocks base method. +func (m *MockObjectSync) HandleRequest(arg0 context.Context, arg1 *spacesyncproto.ObjectSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendSync", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "HandleRequest", arg0, arg1) ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) ret1, _ := ret[1].(error) return ret0, ret1 } -// SendSync indicates an expected call of SendSync. -func (mr *MockSyncClientMockRecorder) SendSync(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// HandleRequest indicates an expected call of HandleRequest. +func (mr *MockObjectSyncMockRecorder) HandleRequest(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSync", reflect.TypeOf((*MockSyncClient)(nil).SendSync), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleRequest", reflect.TypeOf((*MockObjectSync)(nil).HandleRequest), arg0, arg1) } -// SendWithReply mocks base method. -func (m *MockSyncClient) SendWithReply(arg0 context.Context, arg1, arg2 string, arg3 *treechangeproto.TreeSyncMessage, arg4 string) error { +// Init mocks base method. +func (m *MockObjectSync) Init(arg0 *app.App) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendWithReply", arg0, arg1, arg2, arg3, arg4) + ret := m.ctrl.Call(m, "Init", arg0) ret0, _ := ret[0].(error) return ret0 } -// SendWithReply indicates an expected call of SendWithReply. -func (mr *MockSyncClientMockRecorder) SendWithReply(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +// Init indicates an expected call of Init. +func (mr *MockObjectSyncMockRecorder) Init(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWithReply", reflect.TypeOf((*MockSyncClient)(nil).SendWithReply), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockObjectSync)(nil).Init), arg0) +} + +// LastUsage mocks base method. +func (m *MockObjectSync) LastUsage() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LastUsage") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// LastUsage indicates an expected call of LastUsage. +func (mr *MockObjectSyncMockRecorder) LastUsage() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastUsage", reflect.TypeOf((*MockObjectSync)(nil).LastUsage)) +} + +// Name mocks base method. +func (m *MockObjectSync) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockObjectSyncMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockObjectSync)(nil).Name)) +} + +// Run mocks base method. +func (m *MockObjectSync) Run(arg0 context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Run", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Run indicates an expected call of Run. +func (mr *MockObjectSyncMockRecorder) Run(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockObjectSync)(nil).Run), arg0) } diff --git a/commonspace/objectsync/msgpool.go b/commonspace/objectsync/msgpool.go deleted file mode 100644 index 19098ace..00000000 --- a/commonspace/objectsync/msgpool.go +++ /dev/null @@ -1,142 +0,0 @@ -package objectsync - -import ( - "context" - "fmt" - "github.com/anyproto/any-sync/commonspace/objectsync/synchandler" - "github.com/anyproto/any-sync/commonspace/peermanager" - "github.com/anyproto/any-sync/commonspace/spacesyncproto" - "go.uber.org/zap" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" -) - -type LastUsage interface { - LastUsage() time.Time -} - -// MessagePool can be made generic to work with different streams -type MessagePool interface { - LastUsage - synchandler.SyncHandler - peermanager.PeerManager - SendSync(ctx context.Context, peerId string, message *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) -} - -type MessageHandler func(ctx context.Context, senderId string, message *spacesyncproto.ObjectSyncMessage) (err error) - -type responseWaiter struct { - ch chan *spacesyncproto.ObjectSyncMessage -} - -type messagePool struct { - sync.Mutex - peermanager.PeerManager - messageHandler MessageHandler - waiters map[string]responseWaiter - waitersMx sync.Mutex - counter atomic.Uint64 - lastUsage atomic.Int64 -} - -func newMessagePool(peerManager peermanager.PeerManager, messageHandler MessageHandler) MessagePool { - s := &messagePool{ - PeerManager: peerManager, - messageHandler: messageHandler, - waiters: make(map[string]responseWaiter), - } - return s -} - -func (s *messagePool) SendSync(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { - s.updateLastUsage() - if _, ok := ctx.Deadline(); !ok { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Minute) - defer cancel() - } - newCounter := s.counter.Add(1) - msg.RequestId = genReplyKey(peerId, msg.ObjectId, newCounter) - log.InfoCtx(ctx, "mpool sendSync", zap.String("requestId", msg.RequestId)) - s.waitersMx.Lock() - waiter := responseWaiter{ - ch: make(chan *spacesyncproto.ObjectSyncMessage, 1), - } - s.waiters[msg.RequestId] = waiter - s.waitersMx.Unlock() - - err = s.SendPeer(ctx, peerId, msg) - if err != nil { - return - } - select { - case <-ctx.Done(): - s.waitersMx.Lock() - delete(s.waiters, msg.RequestId) - s.waitersMx.Unlock() - - log.With(zap.String("requestId", msg.RequestId)).DebugCtx(ctx, "time elapsed when waiting") - err = fmt.Errorf("sendSync context error: %v", ctx.Err()) - case reply = <-waiter.ch: - // success - } - return -} - -func (s *messagePool) SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { - s.updateLastUsage() - return s.PeerManager.SendPeer(ctx, peerId, msg) -} - -func (s *messagePool) Broadcast(ctx context.Context, msg *spacesyncproto.ObjectSyncMessage) (err error) { - s.updateLastUsage() - return s.PeerManager.Broadcast(ctx, msg) -} - -func (s *messagePool) HandleMessage(ctx context.Context, senderId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { - s.updateLastUsage() - if msg.ReplyId != "" { - log.InfoCtx(ctx, "mpool receive reply", zap.String("replyId", msg.ReplyId)) - // we got reply, send it to waiter - if s.stopWaiter(msg) { - return - } - log.WarnCtx(ctx, "reply id does not exist", zap.String("replyId", msg.ReplyId)) - return - } - return s.messageHandler(ctx, senderId, msg) -} - -func (s *messagePool) LastUsage() time.Time { - return time.Unix(s.lastUsage.Load(), 0) -} - -func (s *messagePool) updateLastUsage() { - s.lastUsage.Store(time.Now().Unix()) -} - -func (s *messagePool) stopWaiter(msg *spacesyncproto.ObjectSyncMessage) bool { - s.waitersMx.Lock() - waiter, exists := s.waiters[msg.ReplyId] - if exists { - delete(s.waiters, msg.ReplyId) - s.waitersMx.Unlock() - waiter.ch <- msg - return true - } - s.waitersMx.Unlock() - return false -} - -func genReplyKey(peerId, treeId string, counter uint64) string { - b := &strings.Builder{} - b.WriteString(peerId) - b.WriteString(".") - b.WriteString(treeId) - b.WriteString(".") - b.WriteString(strconv.FormatUint(counter, 36)) - return b.String() -} diff --git a/commonspace/objectsync/objectsync.go b/commonspace/objectsync/objectsync.go index e56ee6ed..c98bf8db 100644 --- a/commonspace/objectsync/objectsync.go +++ b/commonspace/objectsync/objectsync.go @@ -1,18 +1,24 @@ -//go:generate mockgen -destination mock_objectsync/mock_objectsync.go github.com/anyproto/any-sync/commonspace/objectsync SyncClient +//go:generate mockgen -destination mock_objectsync/mock_objectsync.go github.com/anyproto/any-sync/commonspace/objectsync ObjectSync package objectsync import ( "context" "fmt" - "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" - "github.com/gogo/protobuf/proto" "sync/atomic" "time" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/object/treemanager" + "github.com/anyproto/any-sync/commonspace/spacestate" + "github.com/anyproto/any-sync/metric" + "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/util/multiqueue" + "github.com/cheggaaa/mb/v3" + "github.com/gogo/protobuf/proto" + "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/commonspace/object/syncobjectgetter" - "github.com/anyproto/any-sync/commonspace/objectsync/synchandler" - "github.com/anyproto/any-sync/commonspace/peermanager" "github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/spacesyncproto" "github.com/anyproto/any-sync/nodeconf" @@ -20,138 +26,208 @@ import ( "golang.org/x/exp/slices" ) -var log = logger.NewNamed("common.commonspace.objectsync") +const CName = "common.commonspace.objectsync" + +var log = logger.NewNamed(CName) type ObjectSync interface { - LastUsage - synchandler.SyncHandler - SyncClient() SyncClient + LastUsage() time.Time + HandleMessage(ctx context.Context, hm HandleMessage) (err error) + HandleRequest(ctx context.Context, req *spacesyncproto.ObjectSyncMessage) (resp *spacesyncproto.ObjectSyncMessage, err error) + CloseThread(id string) (err error) + app.ComponentRunnable +} - Close() (err error) +type HandleMessage struct { + Id uint64 + ReceiveTime time.Time + StartHandlingTime time.Time + Deadline time.Time + SenderId string + Message *spacesyncproto.ObjectSyncMessage + PeerCtx context.Context +} + +func (m HandleMessage) LogFields(fields ...zap.Field) []zap.Field { + return append(fields, + metric.SpaceId(m.Message.SpaceId), + metric.ObjectId(m.Message.ObjectId), + metric.QueueDur(m.StartHandlingTime.Sub(m.ReceiveTime)), + metric.TotalDur(time.Since(m.ReceiveTime)), + ) } type objectSync struct { spaceId string - messagePool MessagePool - syncClient SyncClient objectGetter syncobjectgetter.SyncObjectGetter configuration nodeconf.NodeConf spaceStorage spacestorage.SpaceStorage + metric metric.Metric - syncCtx context.Context - cancelSync context.CancelFunc spaceIsDeleted *atomic.Bool + handleQueue multiqueue.MultiQueue[HandleMessage] } -func NewObjectSync( - spaceId string, - spaceIsDeleted *atomic.Bool, - configuration nodeconf.NodeConf, - peerManager peermanager.PeerManager, - objectGetter syncobjectgetter.SyncObjectGetter, - storage spacestorage.SpaceStorage) ObjectSync { - syncCtx, cancel := context.WithCancel(context.Background()) - os := &objectSync{ - objectGetter: objectGetter, - spaceStorage: storage, - spaceId: spaceId, - syncCtx: syncCtx, - cancelSync: cancel, - spaceIsDeleted: spaceIsDeleted, - configuration: configuration, +func (s *objectSync) Init(a *app.App) (err error) { + s.spaceStorage = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + s.objectGetter = a.MustComponent(treemanager.CName).(syncobjectgetter.SyncObjectGetter) + s.configuration = a.MustComponent(nodeconf.CName).(nodeconf.NodeConf) + sharedData := a.MustComponent(spacestate.CName).(*spacestate.SpaceState) + mc := a.Component(metric.CName) + if mc != nil { + s.metric = mc.(metric.Metric) } - os.messagePool = newMessagePool(peerManager, os.handleMessage) - os.syncClient = NewSyncClient(spaceId, os.messagePool, NewRequestFactory()) - return os + s.spaceIsDeleted = sharedData.SpaceIsDeleted + s.spaceId = sharedData.SpaceId + s.handleQueue = multiqueue.New[HandleMessage](s.processHandleMessage, 30) + return nil } -func (s *objectSync) Close() (err error) { - s.cancelSync() - return +func (s *objectSync) Name() (name string) { + return CName +} + +func (s *objectSync) Run(ctx context.Context) (err error) { + return nil +} + +func (s *objectSync) Close(ctx context.Context) (err error) { + return s.handleQueue.Close() +} + +func New() ObjectSync { + return &objectSync{} } func (s *objectSync) LastUsage() time.Time { - return s.messagePool.LastUsage() + // TODO: add time + return time.Time{} } -func (s *objectSync) HandleMessage(ctx context.Context, senderId string, message *spacesyncproto.ObjectSyncMessage) (err error) { - return s.messagePool.HandleMessage(ctx, senderId, message) +func (s *objectSync) HandleRequest(ctx context.Context, req *spacesyncproto.ObjectSyncMessage) (resp *spacesyncproto.ObjectSyncMessage, err error) { + peerId, err := peer.CtxPeerId(ctx) + if err != nil { + return nil, err + } + return s.handleRequest(ctx, peerId, req) } -func (s *objectSync) handleMessage(ctx context.Context, senderId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { - log := log.With( - zap.String("objectId", msg.ObjectId), - zap.String("requestId", msg.RequestId), - zap.String("replyId", msg.ReplyId)) +func (s *objectSync) HandleMessage(ctx context.Context, hm HandleMessage) (err error) { + threadId := hm.Message.ObjectId + hm.ReceiveTime = time.Now() + if hm.PeerCtx == nil { + hm.PeerCtx = ctx + } + err = s.handleQueue.Add(ctx, threadId, hm) + if err == mb.ErrOverflowed { + log.InfoCtx(ctx, "queue overflowed", zap.String("spaceId", s.spaceId), zap.String("objectId", threadId)) + // skip overflowed error + return nil + } + return +} + +func (s *objectSync) processHandleMessage(msg HandleMessage) { + var err error + msg.StartHandlingTime = time.Now() + ctx := peer.CtxWithPeerId(context.Background(), msg.SenderId) + ctx = logger.CtxWithFields(ctx, zap.Uint64("msgId", msg.Id), zap.String("senderId", msg.SenderId)) + defer func() { + if s.metric == nil { + return + } + s.metric.RequestLog(msg.PeerCtx, "space.streamOp", msg.LogFields( + zap.Error(err), + )...) + }() + + if !msg.Deadline.IsZero() { + now := time.Now() + if now.After(msg.Deadline) { + log.InfoCtx(ctx, "skip message: deadline exceed") + err = context.DeadlineExceeded + return + } + } + if err = s.handleMessage(ctx, msg.SenderId, msg.Message); err != nil { + if msg.Message.ObjectId != "" { + // cleanup thread on error + _ = s.handleQueue.CloseThread(msg.Message.ObjectId) + } + log.InfoCtx(ctx, "handleMessage error", zap.Error(err)) + } +} + +func (s *objectSync) handleRequest(ctx context.Context, senderId string, msg *spacesyncproto.ObjectSyncMessage) (response *spacesyncproto.ObjectSyncMessage, err error) { + log := log.With(zap.String("objectId", msg.ObjectId)) if s.spaceIsDeleted.Load() { log = log.With(zap.Bool("isDeleted", true)) // preventing sync with other clients if they are not just syncing the settings tree if !slices.Contains(s.configuration.NodeIds(s.spaceId), senderId) && msg.ObjectId != s.spaceStorage.SpaceSettingsId() { - s.unmarshallSendError(ctx, msg, spacesyncproto.ErrUnexpected, senderId, msg.ObjectId) - return fmt.Errorf("can't perform operation with object, space is deleted") + return nil, spacesyncproto.ErrSpaceIsDeleted } } - log.DebugCtx(ctx, "handling message") + err = s.checkEmptyFullSync(log, msg) + if err != nil { + return nil, err + } + obj, err := s.objectGetter.GetObject(ctx, msg.ObjectId) + if err != nil { + return nil, treechangeproto.ErrGetTree + } + return obj.HandleRequest(ctx, senderId, msg) +} + +func (s *objectSync) handleMessage(ctx context.Context, senderId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { + log := log.With(zap.String("objectId", msg.ObjectId)) + if s.spaceIsDeleted.Load() { + log = log.With(zap.Bool("isDeleted", true)) + // preventing sync with other clients if they are not just syncing the settings tree + if !slices.Contains(s.configuration.NodeIds(s.spaceId), senderId) && msg.ObjectId != s.spaceStorage.SpaceSettingsId() { + return spacesyncproto.ErrSpaceIsDeleted + } + } + err = s.checkEmptyFullSync(log, msg) + if err != nil { + return err + } + obj, err := s.objectGetter.GetObject(ctx, msg.ObjectId) + if err != nil { + return fmt.Errorf("failed to get object from cache: %w", err) + } + err = obj.HandleMessage(ctx, senderId, msg) + if err != nil { + return fmt.Errorf("failed to handle message: %w", err) + } + return +} + +func (s *objectSync) CloseThread(id string) (err error) { + return s.handleQueue.CloseThread(id) +} + +func (s *objectSync) checkEmptyFullSync(log logger.CtxLogger, msg *spacesyncproto.ObjectSyncMessage) (err error) { hasTree, err := s.spaceStorage.HasTree(msg.ObjectId) if err != nil { - s.unmarshallSendError(ctx, msg, spacesyncproto.ErrUnexpected, senderId, msg.ObjectId) - return fmt.Errorf("falied to execute get operation on storage has tree: %w", err) + log.Warn("failed to execute get operation on storage has tree", zap.Error(err)) + return spacesyncproto.ErrUnexpected } // in this case we will try to get it from remote, unless the sender also sent us the same request :-) if !hasTree { treeMsg := &treechangeproto.TreeSyncMessage{} err = proto.Unmarshal(msg.Payload, treeMsg) if err != nil { - s.sendError(ctx, nil, spacesyncproto.ErrUnexpected, senderId, msg.ObjectId, msg.RequestId) - return fmt.Errorf("failed to unmarshall tree sync message: %w", err) + return nil } // this means that we don't have the tree locally and therefore can't return it if s.isEmptyFullSyncRequest(treeMsg) { - err = treechangeproto.ErrGetTree - s.sendError(ctx, nil, treechangeproto.ErrGetTree, senderId, msg.ObjectId, msg.RequestId) - return fmt.Errorf("failed to get tree from storage on full sync: %w", err) + return treechangeproto.ErrGetTree } } - obj, err := s.objectGetter.GetObject(ctx, msg.ObjectId) - if err != nil { - // TODO: write tests for object sync https://linear.app/anytype/issue/GO-1299/write-tests-for-commonspaceobjectsync - s.unmarshallSendError(ctx, msg, spacesyncproto.ErrUnexpected, senderId, msg.ObjectId) - return fmt.Errorf("failed to get object from cache: %w", err) - } - // TODO: unmarshall earlier - err = obj.HandleMessage(ctx, senderId, msg) - if err != nil { - s.unmarshallSendError(ctx, msg, spacesyncproto.ErrUnexpected, senderId, msg.ObjectId) - return fmt.Errorf("failed to handle message: %w", err) - } return } -func (s *objectSync) SyncClient() SyncClient { - return s.syncClient -} - -func (s *objectSync) unmarshallSendError(ctx context.Context, msg *spacesyncproto.ObjectSyncMessage, respErr error, senderId, objectId string) { - unmarshalled := &treechangeproto.TreeSyncMessage{} - err := proto.Unmarshal(msg.Payload, unmarshalled) - if err != nil { - return - } - s.sendError(ctx, unmarshalled.RootChange, respErr, senderId, objectId, msg.RequestId) -} - -func (s *objectSync) sendError(ctx context.Context, root *treechangeproto.RawTreeChangeWithId, respErr error, senderId, objectId, replyId string) { - // we don't send errors if have no reply id, this can lead to bugs and also nobody needs this error - if replyId == "" { - return - } - resp := treechangeproto.WrapError(respErr, root) - if err := s.syncClient.SendWithReply(ctx, senderId, objectId, resp, replyId); err != nil { - log.InfoCtx(ctx, "failed to send error to client") - } -} - func (s *objectSync) isEmptyFullSyncRequest(msg *treechangeproto.TreeSyncMessage) bool { return msg.GetContent().GetFullSyncRequest() != nil && len(msg.GetContent().GetFullSyncRequest().GetHeads()) == 0 } diff --git a/commonspace/objectsync/syncclient.go b/commonspace/objectsync/syncclient.go deleted file mode 100644 index aad712bd..00000000 --- a/commonspace/objectsync/syncclient.go +++ /dev/null @@ -1,78 +0,0 @@ -package objectsync - -import ( - "context" - "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" - "github.com/anyproto/any-sync/commonspace/spacesyncproto" - "go.uber.org/zap" -) - -type SyncClient interface { - RequestFactory - Broadcast(ctx context.Context, msg *treechangeproto.TreeSyncMessage) - SendWithReply(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage, replyId string) (err error) - SendSync(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) - MessagePool() MessagePool -} - -type syncClient struct { - RequestFactory - spaceId string - messagePool MessagePool -} - -func NewSyncClient( - spaceId string, - messagePool MessagePool, - factory RequestFactory) SyncClient { - return &syncClient{ - messagePool: messagePool, - RequestFactory: factory, - spaceId: spaceId, - } -} - -func (s *syncClient) Broadcast(ctx context.Context, msg *treechangeproto.TreeSyncMessage) { - objMsg, err := MarshallTreeMessage(msg, s.spaceId, msg.RootChange.Id, "") - if err != nil { - return - } - err = s.messagePool.Broadcast(ctx, objMsg) - if err != nil { - log.DebugCtx(ctx, "broadcast error", zap.Error(err)) - } -} - -func (s *syncClient) SendSync(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { - objMsg, err := MarshallTreeMessage(msg, s.spaceId, objectId, "") - if err != nil { - return - } - return s.messagePool.SendSync(ctx, peerId, objMsg) -} - -func (s *syncClient) SendWithReply(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage, replyId string) (err error) { - objMsg, err := MarshallTreeMessage(msg, s.spaceId, objectId, replyId) - if err != nil { - return - } - return s.messagePool.SendPeer(ctx, peerId, objMsg) -} - -func (s *syncClient) MessagePool() MessagePool { - return s.messagePool -} - -func MarshallTreeMessage(message *treechangeproto.TreeSyncMessage, spaceId, objectId, replyId string) (objMsg *spacesyncproto.ObjectSyncMessage, err error) { - payload, err := message.Marshal() - if err != nil { - return - } - objMsg = &spacesyncproto.ObjectSyncMessage{ - ReplyId: replyId, - Payload: payload, - ObjectId: objectId, - SpaceId: spaceId, - } - return -} diff --git a/commonspace/objectsync/synchandler/synchhandler.go b/commonspace/objectsync/synchandler/synchhandler.go index 35aebb1e..090118cd 100644 --- a/commonspace/objectsync/synchandler/synchhandler.go +++ b/commonspace/objectsync/synchandler/synchhandler.go @@ -6,5 +6,6 @@ import ( ) type SyncHandler interface { - HandleMessage(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (err error) + HandleMessage(ctx context.Context, senderId string, message *spacesyncproto.ObjectSyncMessage) (err error) + HandleRequest(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (response *spacesyncproto.ObjectSyncMessage, err error) } diff --git a/commonspace/objecttreebuilder/mock_objecttreebuilder/mock_objecttreebuilder.go b/commonspace/objecttreebuilder/mock_objecttreebuilder/mock_objecttreebuilder.go new file mode 100644 index 00000000..b44d028f --- /dev/null +++ b/commonspace/objecttreebuilder/mock_objecttreebuilder/mock_objecttreebuilder.go @@ -0,0 +1,99 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/anyproto/any-sync/commonspace/objecttreebuilder (interfaces: TreeBuilder) + +// Package mock_objecttreebuilder is a generated GoMock package. +package mock_objecttreebuilder + +import ( + context "context" + reflect "reflect" + + objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" + updatelistener "github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener" + treestorage "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" + objecttreebuilder "github.com/anyproto/any-sync/commonspace/objecttreebuilder" + gomock "go.uber.org/mock/gomock" +) + +// MockTreeBuilder is a mock of TreeBuilder interface. +type MockTreeBuilder struct { + ctrl *gomock.Controller + recorder *MockTreeBuilderMockRecorder +} + +// MockTreeBuilderMockRecorder is the mock recorder for MockTreeBuilder. +type MockTreeBuilderMockRecorder struct { + mock *MockTreeBuilder +} + +// NewMockTreeBuilder creates a new mock instance. +func NewMockTreeBuilder(ctrl *gomock.Controller) *MockTreeBuilder { + mock := &MockTreeBuilder{ctrl: ctrl} + mock.recorder = &MockTreeBuilderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTreeBuilder) EXPECT() *MockTreeBuilderMockRecorder { + return m.recorder +} + +// BuildHistoryTree mocks base method. +func (m *MockTreeBuilder) BuildHistoryTree(arg0 context.Context, arg1 string, arg2 objecttreebuilder.HistoryTreeOpts) (objecttree.HistoryTree, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BuildHistoryTree", arg0, arg1, arg2) + ret0, _ := ret[0].(objecttree.HistoryTree) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BuildHistoryTree indicates an expected call of BuildHistoryTree. +func (mr *MockTreeBuilderMockRecorder) BuildHistoryTree(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildHistoryTree", reflect.TypeOf((*MockTreeBuilder)(nil).BuildHistoryTree), arg0, arg1, arg2) +} + +// BuildTree mocks base method. +func (m *MockTreeBuilder) BuildTree(arg0 context.Context, arg1 string, arg2 objecttreebuilder.BuildTreeOpts) (objecttree.ObjectTree, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BuildTree", arg0, arg1, arg2) + ret0, _ := ret[0].(objecttree.ObjectTree) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BuildTree indicates an expected call of BuildTree. +func (mr *MockTreeBuilderMockRecorder) BuildTree(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTree", reflect.TypeOf((*MockTreeBuilder)(nil).BuildTree), arg0, arg1, arg2) +} + +// CreateTree mocks base method. +func (m *MockTreeBuilder) CreateTree(arg0 context.Context, arg1 objecttree.ObjectTreeCreatePayload) (treestorage.TreeStorageCreatePayload, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateTree", arg0, arg1) + ret0, _ := ret[0].(treestorage.TreeStorageCreatePayload) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateTree indicates an expected call of CreateTree. +func (mr *MockTreeBuilderMockRecorder) CreateTree(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTree", reflect.TypeOf((*MockTreeBuilder)(nil).CreateTree), arg0, arg1) +} + +// PutTree mocks base method. +func (m *MockTreeBuilder) PutTree(arg0 context.Context, arg1 treestorage.TreeStorageCreatePayload, arg2 updatelistener.UpdateListener) (objecttree.ObjectTree, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PutTree", arg0, arg1, arg2) + ret0, _ := ret[0].(objecttree.ObjectTree) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PutTree indicates an expected call of PutTree. +func (mr *MockTreeBuilderMockRecorder) PutTree(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutTree", reflect.TypeOf((*MockTreeBuilder)(nil).PutTree), arg0, arg1, arg2) +} diff --git a/commonspace/objecttreebuilder/treebuilder.go b/commonspace/objecttreebuilder/treebuilder.go new file mode 100644 index 00000000..8f3d09d8 --- /dev/null +++ b/commonspace/objecttreebuilder/treebuilder.go @@ -0,0 +1,204 @@ +//go:generate mockgen -destination mock_objecttreebuilder/mock_objecttreebuilder.go github.com/anyproto/any-sync/commonspace/objecttreebuilder TreeBuilder +package objecttreebuilder + +import ( + "context" + "errors" + "sync/atomic" + + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/headsync" + "github.com/anyproto/any-sync/commonspace/object/acl/list" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl" + "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/tree/synctree/updatelistener" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" + "github.com/anyproto/any-sync/commonspace/objectsync" + "github.com/anyproto/any-sync/commonspace/peermanager" + "github.com/anyproto/any-sync/commonspace/requestmanager" + "github.com/anyproto/any-sync/commonspace/spacestate" + "github.com/anyproto/any-sync/commonspace/spacestorage" + "github.com/anyproto/any-sync/commonspace/syncstatus" + "github.com/anyproto/any-sync/nodeconf" + "go.uber.org/zap" +) + +type BuildTreeOpts struct { + Listener updatelistener.UpdateListener + TreeBuilder objecttree.BuildObjectTreeFunc +} + +const CName = "common.commonspace.objecttreebuilder" + +var log = logger.NewNamed(CName) +var ErrSpaceClosed = errors.New("space is closed") + +type HistoryTreeOpts struct { + BeforeId string + Include bool + BuildFullTree bool +} + +type TreeBuilder interface { + BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t objecttree.ObjectTree, err error) + BuildHistoryTree(ctx context.Context, id string, opts HistoryTreeOpts) (t objecttree.HistoryTree, err error) + CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) + PutTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload, listener updatelistener.UpdateListener) (t objecttree.ObjectTree, err error) +} + +type TreeBuilderComponent interface { + app.Component + TreeBuilder +} + +func New() TreeBuilderComponent { + return &treeBuilder{} +} + +type treeBuilder struct { + syncClient synctree.SyncClient + configuration nodeconf.NodeConf + headsNotifiable synctree.HeadNotifiable + peerManager peermanager.PeerManager + requestManager requestmanager.RequestManager + spaceStorage spacestorage.SpaceStorage + syncStatus syncstatus.StatusUpdater + objectSync objectsync.ObjectSync + + log logger.CtxLogger + builder objecttree.BuildObjectTreeFunc + spaceId string + aclList list.AclList + treesUsed *atomic.Int32 + isClosed *atomic.Bool +} + +func (t *treeBuilder) Init(a *app.App) (err error) { + state := a.MustComponent(spacestate.CName).(*spacestate.SpaceState) + t.spaceId = state.SpaceId + t.isClosed = state.SpaceIsClosed + t.treesUsed = state.TreesUsed + t.builder = state.TreeBuilderFunc + t.aclList = a.MustComponent(syncacl.CName).(syncacl.SyncAcl) + t.spaceStorage = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + t.configuration = a.MustComponent(nodeconf.CName).(nodeconf.NodeConf) + t.headsNotifiable = a.MustComponent(headsync.CName).(headsync.HeadSync) + t.syncStatus = a.MustComponent(syncstatus.CName).(syncstatus.StatusUpdater) + t.peerManager = a.MustComponent(peermanager.CName).(peermanager.PeerManager) + t.requestManager = a.MustComponent(requestmanager.CName).(requestmanager.RequestManager) + t.objectSync = a.MustComponent(objectsync.CName).(objectsync.ObjectSync) + t.log = log.With(zap.String("spaceId", t.spaceId)) + t.syncClient = synctree.NewSyncClient(t.spaceId, t.requestManager, t.peerManager) + return nil +} + +func (t *treeBuilder) Name() (name string) { + return CName +} + +func (t *treeBuilder) BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (ot objecttree.ObjectTree, err error) { + if t.isClosed.Load() { + // TODO: change to real error + err = ErrSpaceClosed + return + } + treeBuilder := opts.TreeBuilder + if treeBuilder == nil { + treeBuilder = t.builder + } + deps := synctree.BuildDeps{ + SpaceId: t.spaceId, + SyncClient: t.syncClient, + Configuration: t.configuration, + HeadNotifiable: t.headsNotifiable, + Listener: opts.Listener, + AclList: t.aclList, + SpaceStorage: t.spaceStorage, + OnClose: t.onClose, + SyncStatus: t.syncStatus, + PeerGetter: t.peerManager, + BuildObjectTree: treeBuilder, + } + t.treesUsed.Add(1) + t.log.Debug("incrementing counter", zap.String("id", id), zap.Int32("trees", t.treesUsed.Load())) + if ot, err = synctree.BuildSyncTreeOrGetRemote(ctx, id, deps); err != nil { + t.treesUsed.Add(-1) + t.log.Debug("decrementing counter, load failed", zap.String("id", id), zap.Int32("trees", t.treesUsed.Load()), zap.Error(err)) + return nil, err + } + return +} + +func (t *treeBuilder) BuildHistoryTree(ctx context.Context, id string, opts HistoryTreeOpts) (ot objecttree.HistoryTree, err error) { + if t.isClosed.Load() { + // TODO: change to real error + err = ErrSpaceClosed + return + } + + params := objecttree.HistoryTreeParams{ + AclList: t.aclList, + BeforeId: opts.BeforeId, + IncludeBeforeId: opts.Include, + BuildFullTree: opts.BuildFullTree, + } + params.TreeStorage, err = t.spaceStorage.TreeStorage(id) + if err != nil { + return + } + return objecttree.BuildHistoryTree(params) +} + +func (t *treeBuilder) CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) { + if t.isClosed.Load() { + err = ErrSpaceClosed + return + } + root, err := objecttree.CreateObjectTreeRoot(payload, t.aclList) + if err != nil { + return + } + + res = treestorage.TreeStorageCreatePayload{ + RootRawChange: root, + Changes: []*treechangeproto.RawTreeChangeWithId{root}, + Heads: []string{root.Id}, + } + return +} + +func (t *treeBuilder) PutTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload, listener updatelistener.UpdateListener) (ot objecttree.ObjectTree, err error) { + if t.isClosed.Load() { + err = ErrSpaceClosed + return + } + deps := synctree.BuildDeps{ + SpaceId: t.spaceId, + SyncClient: t.syncClient, + Configuration: t.configuration, + HeadNotifiable: t.headsNotifiable, + Listener: listener, + AclList: t.aclList, + SpaceStorage: t.spaceStorage, + OnClose: t.onClose, + SyncStatus: t.syncStatus, + PeerGetter: t.peerManager, + BuildObjectTree: t.builder, + } + ot, err = synctree.PutSyncTree(ctx, payload, deps) + if err != nil { + return + } + t.treesUsed.Add(1) + t.log.Debug("incrementing counter", zap.String("id", payload.RootRawChange.Id), zap.Int32("trees", t.treesUsed.Load())) + return +} + +func (t *treeBuilder) onClose(id string) { + t.treesUsed.Add(-1) + log.Debug("decrementing counter", zap.String("id", id), zap.Int32("trees", t.treesUsed.Load()), zap.String("spaceId", t.spaceId)) + _ = t.objectSync.CloseThread(id) +} diff --git a/commonspace/payloads.go b/commonspace/payloads.go index 5d2284d0..4fb5c59c 100644 --- a/commonspace/payloads.go +++ b/commonspace/payloads.go @@ -2,20 +2,22 @@ package commonspace import ( "errors" + "hash/fnv" + "math/rand" + "strconv" + "strings" + "time" + "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/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/consensus/consensusproto" "github.com/anyproto/any-sync/util/cidutil" "github.com/anyproto/any-sync/util/crypto" "github.com/gogo/protobuf/proto" - "hash/fnv" - "math/rand" - "strconv" - "strings" - "time" ) const ( @@ -71,7 +73,7 @@ func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload sp // building acl root keyStorage := crypto.NewKeyStorage() - aclBuilder := list.NewAclRecordBuilder("", keyStorage) + aclBuilder := list.NewAclRecordBuilder("", keyStorage, nil, list.NoOpAcceptorVerifier{}) aclRoot, err := aclBuilder.BuildRoot(list.RootContent{ PrivKey: payload.SigningKey, MasterKey: payload.MasterKey, @@ -158,7 +160,7 @@ func storagePayloadForSpaceDerive(payload SpaceDerivePayload) (storagePayload sp // building acl root keyStorage := crypto.NewKeyStorage() - aclBuilder := list.NewAclRecordBuilder("", keyStorage) + aclBuilder := list.NewAclRecordBuilder("", keyStorage, nil, list.NoOpAcceptorVerifier{}) aclRoot, err := aclBuilder.BuildRoot(list.RootContent{ PrivKey: payload.SigningKey, MasterKey: payload.MasterKey, @@ -214,14 +216,15 @@ func validateSpaceStorageCreatePayload(payload spacestorage.SpaceStorageCreatePa } func ValidateSpaceHeader(rawHeaderWithId *spacesyncproto.RawSpaceHeaderWithId, identity crypto.PubKey) (err error) { + if rawHeaderWithId == nil { + return spacestorage.ErrIncorrectSpaceHeader + } sepIdx := strings.Index(rawHeaderWithId.Id, ".") if sepIdx == -1 { - err = spacestorage.ErrIncorrectSpaceHeader - return + return spacestorage.ErrIncorrectSpaceHeader } if !cidutil.VerifyCid(rawHeaderWithId.RawHeader, rawHeaderWithId.Id[:sepIdx]) { - err = objecttree.ErrIncorrectCid - return + return objecttree.ErrIncorrectCid } var rawSpaceHeader spacesyncproto.RawSpaceHeader err = proto.Unmarshal(rawHeaderWithId.RawHeader, &rawSpaceHeader) @@ -239,29 +242,26 @@ func ValidateSpaceHeader(rawHeaderWithId *spacesyncproto.RawSpaceHeaderWithId, i } res, err := payloadIdentity.Verify(rawSpaceHeader.SpaceHeader, rawSpaceHeader.Signature) if err != nil || !res { - err = spacestorage.ErrIncorrectSpaceHeader - return + return spacestorage.ErrIncorrectSpaceHeader } if rawHeaderWithId.Id[sepIdx+1:] != strconv.FormatUint(header.ReplicationKey, 36) { - err = spacestorage.ErrIncorrectSpaceHeader - return + return spacestorage.ErrIncorrectSpaceHeader } if identity == nil { return } if !payloadIdentity.Equals(identity) { - err = ErrIncorrectIdentity - return + return ErrIncorrectIdentity } return } -func validateCreateSpaceAclPayload(rawWithId *aclrecordproto.RawAclRecordWithId) (spaceId string, err error) { +func validateCreateSpaceAclPayload(rawWithId *consensusproto.RawRecordWithId) (spaceId string, err error) { if !cidutil.VerifyCid(rawWithId.Payload, rawWithId.Id) { err = objecttree.ErrIncorrectCid return } - var rawAcl aclrecordproto.RawAclRecord + var rawAcl consensusproto.RawRecord err = proto.Unmarshal(rawWithId.Payload, &rawAcl) if err != nil { return diff --git a/commonspace/payloads_test.go b/commonspace/payloads_test.go index f144f623..fbfe0f10 100644 --- a/commonspace/payloads_test.go +++ b/commonspace/payloads_test.go @@ -2,20 +2,22 @@ package commonspace import ( "fmt" + "math/rand" + "strconv" + "testing" + "time" + "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" "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" "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/consensus/consensusproto" "github.com/anyproto/any-sync/util/cidutil" "github.com/anyproto/any-sync/util/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "math/rand" - "strconv" - "testing" - "time" ) func TestSuccessHeaderPayloadForSpaceCreate(t *testing.T) { @@ -188,14 +190,14 @@ func TestFailAclPayloadSpace_IncorrectCid(t *testing.T) { marshalled, err := aclRoot.Marshal() require.NoError(t, err) signature, err := accountKeys.SignKey.Sign(marshalled) - rawAclRecord := &aclrecordproto.RawAclRecord{ + rawAclRecord := &consensusproto.RawRecord{ Payload: marshalled, Signature: signature, } marshalledRaw, err := rawAclRecord.Marshal() require.NoError(t, err) aclHeadId := "rand" - rawWithId := &aclrecordproto.RawAclRecordWithId{ + rawWithId := &consensusproto.RawRecordWithId{ Payload: marshalledRaw, Id: aclHeadId, } @@ -230,7 +232,7 @@ func TestFailedAclPayloadSpace_IncorrectSignature(t *testing.T) { } marshalled, err := aclRoot.Marshal() require.NoError(t, err) - rawAclRecord := &aclrecordproto.RawAclRecord{ + rawAclRecord := &consensusproto.RawRecord{ Payload: marshalled, Signature: marshalled, } @@ -238,7 +240,7 @@ func TestFailedAclPayloadSpace_IncorrectSignature(t *testing.T) { require.NoError(t, err) aclHeadId, err := cidutil.NewCidFromBytes(marshalledRaw) require.NoError(t, err) - rawWithId := &aclrecordproto.RawAclRecordWithId{ + rawWithId := &consensusproto.RawRecordWithId{ Payload: marshalledRaw, Id: aclHeadId, } @@ -286,7 +288,7 @@ func TestFailedAclPayloadSpace_IncorrectIdentitySignature(t *testing.T) { return } signature, err := accountKeys.SignKey.Sign(marshalled) - rawAclRecord := &aclrecordproto.RawAclRecord{ + rawAclRecord := &consensusproto.RawRecord{ Payload: marshalled, Signature: signature, } @@ -298,7 +300,7 @@ func TestFailedAclPayloadSpace_IncorrectIdentitySignature(t *testing.T) { if err != nil { return } - rawWithId := &aclrecordproto.RawAclRecordWithId{ + rawWithId := &consensusproto.RawRecordWithId{ Payload: marshalledRaw, Id: aclHeadId, } @@ -540,7 +542,7 @@ func rawSettingsPayload(accountKeys *accountdata.AccountKeys, spaceId, aclHeadId return } -func rawAclWithId(accountKeys *accountdata.AccountKeys, spaceId string) (aclHeadId string, rawWithId *aclrecordproto.RawAclRecordWithId, err error) { +func rawAclWithId(accountKeys *accountdata.AccountKeys, spaceId string) (aclHeadId string, rawWithId *consensusproto.RawRecordWithId, err error) { // TODO: use same storage creation methods as we use in spaces readKeyBytes := make([]byte, 32) _, err = rand.Read(readKeyBytes) @@ -582,7 +584,7 @@ func rawAclWithId(accountKeys *accountdata.AccountKeys, spaceId string) (aclHead return } signature, err := accountKeys.SignKey.Sign(marshalled) - rawAclRecord := &aclrecordproto.RawAclRecord{ + rawAclRecord := &consensusproto.RawRecord{ Payload: marshalled, Signature: signature, } @@ -594,7 +596,7 @@ func rawAclWithId(accountKeys *accountdata.AccountKeys, spaceId string) (aclHead if err != nil { return } - rawWithId = &aclrecordproto.RawAclRecordWithId{ + rawWithId = &consensusproto.RawRecordWithId{ Payload: marshalledRaw, Id: aclHeadId, } diff --git a/commonspace/peermanager/mock_peermanager/mock_peermanager.go b/commonspace/peermanager/mock_peermanager/mock_peermanager.go index a77e0ab1..d7d99e32 100644 --- a/commonspace/peermanager/mock_peermanager/mock_peermanager.go +++ b/commonspace/peermanager/mock_peermanager/mock_peermanager.go @@ -8,9 +8,10 @@ import ( context "context" reflect "reflect" + app "github.com/anyproto/any-sync/app" spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" peer "github.com/anyproto/any-sync/net/peer" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockPeerManager is a mock of PeerManager interface. @@ -65,6 +66,34 @@ func (mr *MockPeerManagerMockRecorder) GetResponsiblePeers(arg0 interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResponsiblePeers", reflect.TypeOf((*MockPeerManager)(nil).GetResponsiblePeers), arg0) } +// Init mocks base method. +func (m *MockPeerManager) Init(arg0 *app.App) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockPeerManagerMockRecorder) Init(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockPeerManager)(nil).Init), arg0) +} + +// Name mocks base method. +func (m *MockPeerManager) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockPeerManagerMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockPeerManager)(nil).Name)) +} + // SendPeer mocks base method. func (m *MockPeerManager) SendPeer(arg0 context.Context, arg1 string, arg2 *spacesyncproto.ObjectSyncMessage) error { m.ctrl.T.Helper() diff --git a/commonspace/peermanager/peermanager.go b/commonspace/peermanager/peermanager.go index a4075f32..0a5750bf 100644 --- a/commonspace/peermanager/peermanager.go +++ b/commonspace/peermanager/peermanager.go @@ -8,9 +8,12 @@ import ( "github.com/anyproto/any-sync/net/peer" ) -const CName = "common.commonspace.peermanager" +const ( + CName = "common.commonspace.peermanager" +) type PeerManager interface { + app.Component // SendPeer sends a message to a stream by peerId SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) // Broadcast sends a message to all subscribed peers diff --git a/commonspace/requestmanager/requestmanager.go b/commonspace/requestmanager/requestmanager.go new file mode 100644 index 00000000..7a6dd019 --- /dev/null +++ b/commonspace/requestmanager/requestmanager.go @@ -0,0 +1,129 @@ +package requestmanager + +import ( + "context" + "sync" + + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/objectsync" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/net/pool" + "github.com/anyproto/any-sync/net/rpc/rpcerr" + "github.com/anyproto/any-sync/net/streampool" + "go.uber.org/zap" + "storj.io/drpc" +) + +const CName = "common.commonspace.requestmanager" + +var log = logger.NewNamed(CName) + +type RequestManager interface { + app.ComponentRunnable + SendRequest(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) + QueueRequest(peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) +} + +func New() RequestManager { + return &requestManager{ + workers: 10, + queueSize: 300, + pools: map[string]*streampool.ExecPool{}, + } +} + +type MessageHandler interface { + HandleMessage(ctx context.Context, hm objectsync.HandleMessage) (err error) +} + +type requestManager struct { + sync.Mutex + pools map[string]*streampool.ExecPool + peerPool pool.Pool + workers int + queueSize int + handler MessageHandler + ctx context.Context + cancel context.CancelFunc + clientFactory spacesyncproto.ClientFactory +} + +func (r *requestManager) Init(a *app.App) (err error) { + r.ctx, r.cancel = context.WithCancel(context.Background()) + r.handler = a.MustComponent(objectsync.CName).(MessageHandler) + r.peerPool = a.MustComponent(pool.CName).(pool.Pool) + r.clientFactory = spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceSyncClient) + return +} + +func (r *requestManager) Name() (name string) { + return CName +} + +func (r *requestManager) Run(ctx context.Context) (err error) { + return nil +} + +func (r *requestManager) Close(ctx context.Context) (err error) { + r.Lock() + defer r.Unlock() + r.cancel() + for _, p := range r.pools { + _ = p.Close() + } + return nil +} + +func (r *requestManager) SendRequest(ctx context.Context, peerId string, req *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) { + // TODO: limit concurrent sends? + return r.doRequest(ctx, peerId, req) +} + +func (r *requestManager) QueueRequest(peerId string, req *spacesyncproto.ObjectSyncMessage) (err error) { + r.Lock() + defer r.Unlock() + pl, exists := r.pools[peerId] + if !exists { + pl = streampool.NewExecPool(r.workers, r.queueSize) + r.pools[peerId] = pl + pl.Run() + } + // TODO: for later think when many clients are there, + // we need to close pools for inactive clients + return pl.TryAdd(func() { + doRequestAndHandle(r, peerId, req) + }) +} + +var doRequestAndHandle = (*requestManager).requestAndHandle + +func (r *requestManager) requestAndHandle(peerId string, req *spacesyncproto.ObjectSyncMessage) { + ctx := r.ctx + resp, err := r.doRequest(ctx, peerId, req) + if err != nil { + log.Warn("failed to send request", zap.Error(err)) + return + } + ctx = peer.CtxWithPeerId(ctx, peerId) + _ = r.handler.HandleMessage(ctx, objectsync.HandleMessage{ + SenderId: peerId, + Message: resp, + PeerCtx: ctx, + }) +} + +func (r *requestManager) doRequest(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (resp *spacesyncproto.ObjectSyncMessage, err error) { + pr, err := r.peerPool.Get(ctx, peerId) + if err != nil { + return + } + err = pr.DoDrpc(ctx, func(conn drpc.Conn) error { + cl := r.clientFactory.Client(conn) + resp, err = cl.ObjectSync(ctx, msg) + return err + }) + err = rpcerr.Unwrap(err) + return +} diff --git a/commonspace/requestmanager/requestmanager_test.go b/commonspace/requestmanager/requestmanager_test.go new file mode 100644 index 00000000..036d7348 --- /dev/null +++ b/commonspace/requestmanager/requestmanager_test.go @@ -0,0 +1,186 @@ +package requestmanager + +import ( + "context" + "sync" + "testing" + + "github.com/anyproto/any-sync/commonspace/objectsync" + "github.com/anyproto/any-sync/commonspace/objectsync/mock_objectsync" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/commonspace/spacesyncproto/mock_spacesyncproto" + "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/net/peer/mock_peer" + "github.com/anyproto/any-sync/net/pool/mock_pool" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "storj.io/drpc" + "storj.io/drpc/drpcconn" +) + +type fixture struct { + requestManager *requestManager + messageHandlerMock *mock_objectsync.MockObjectSync + peerPoolMock *mock_pool.MockPool + clientMock *mock_spacesyncproto.MockDRPCSpaceSyncClient + ctrl *gomock.Controller +} + +func newFixture(t *testing.T) *fixture { + ctrl := gomock.NewController(t) + manager := New().(*requestManager) + peerPoolMock := mock_pool.NewMockPool(ctrl) + messageHandlerMock := mock_objectsync.NewMockObjectSync(ctrl) + clientMock := mock_spacesyncproto.NewMockDRPCSpaceSyncClient(ctrl) + manager.peerPool = peerPoolMock + manager.handler = messageHandlerMock + manager.clientFactory = spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceSyncClient { + return clientMock + }) + manager.ctx, manager.cancel = context.WithCancel(context.Background()) + return &fixture{ + requestManager: manager, + messageHandlerMock: messageHandlerMock, + peerPoolMock: peerPoolMock, + clientMock: clientMock, + ctrl: ctrl, + } +} + +func (fx *fixture) stop() { + fx.ctrl.Finish() +} + +func TestRequestManager_SyncRequest(t *testing.T) { + ctx := context.Background() + + t.Run("send request", func(t *testing.T) { + fx := newFixture(t) + defer fx.stop() + + peerId := "peerId" + peerMock := mock_peer.NewMockPeer(fx.ctrl) + conn := &drpcconn.Conn{} + msg := &spacesyncproto.ObjectSyncMessage{} + resp := &spacesyncproto.ObjectSyncMessage{} + fx.peerPoolMock.EXPECT().Get(ctx, peerId).Return(peerMock, nil) + fx.clientMock.EXPECT().ObjectSync(ctx, msg).Return(resp, nil) + peerMock.EXPECT().DoDrpc(ctx, gomock.Any()).DoAndReturn(func(ctx context.Context, drpcHandler func(conn drpc.Conn) error) { + drpcHandler(conn) + }).Return(nil) + res, err := fx.requestManager.SendRequest(ctx, peerId, msg) + require.NoError(t, err) + require.Equal(t, resp, res) + }) + + t.Run("request and handle", func(t *testing.T) { + fx := newFixture(t) + defer fx.stop() + ctx = fx.requestManager.ctx + + peerId := "peerId" + peerMock := mock_peer.NewMockPeer(fx.ctrl) + conn := &drpcconn.Conn{} + msg := &spacesyncproto.ObjectSyncMessage{} + resp := &spacesyncproto.ObjectSyncMessage{} + fx.peerPoolMock.EXPECT().Get(ctx, peerId).Return(peerMock, nil) + fx.clientMock.EXPECT().ObjectSync(ctx, msg).Return(resp, nil) + peerMock.EXPECT().DoDrpc(ctx, gomock.Any()).DoAndReturn(func(ctx context.Context, drpcHandler func(conn drpc.Conn) error) { + drpcHandler(conn) + }).Return(nil) + fx.messageHandlerMock.EXPECT().HandleMessage(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, msg objectsync.HandleMessage) { + require.Equal(t, peerId, msg.SenderId) + require.Equal(t, resp, msg.Message) + pId, _ := peer.CtxPeerId(msg.PeerCtx) + require.Equal(t, peerId, pId) + }).Return(nil) + fx.requestManager.requestAndHandle(peerId, msg) + }) +} + +func TestRequestManager_QueueRequest(t *testing.T) { + t.Run("max concurrent reqs for peer, independent reqs for other peer", func(t *testing.T) { + // testing 2 concurrent requests to one peer and simultaneous to another peer + fx := newFixture(t) + defer fx.stop() + fx.requestManager.workers = 2 + msgRelease := make(chan struct{}) + msgWait := make(chan struct{}) + msgs := sync.Map{} + doRequestAndHandle = func(manager *requestManager, peerId string, req *spacesyncproto.ObjectSyncMessage) { + msgs.Store(req.ObjectId, struct{}{}) + <-msgWait + <-msgRelease + } + otherPeer := "otherPeer" + msg1 := &spacesyncproto.ObjectSyncMessage{ObjectId: "id1"} + msg2 := &spacesyncproto.ObjectSyncMessage{ObjectId: "id2"} + msg3 := &spacesyncproto.ObjectSyncMessage{ObjectId: "id3"} + otherMsg1 := &spacesyncproto.ObjectSyncMessage{ObjectId: "otherId1"} + + // sending requests to first peer + peerId := "peerId" + err := fx.requestManager.QueueRequest(peerId, msg1) + require.NoError(t, err) + err = fx.requestManager.QueueRequest(peerId, msg2) + require.NoError(t, err) + err = fx.requestManager.QueueRequest(peerId, msg3) + require.NoError(t, err) + + // waiting until all the messages are loaded + msgWait <- struct{}{} + msgWait <- struct{}{} + _, ok := msgs.Load("id1") + require.True(t, ok) + _, ok = msgs.Load("id2") + require.True(t, ok) + // third message should not be read + _, ok = msgs.Load("id3") + require.False(t, ok) + + // request for other peer should pass + err = fx.requestManager.QueueRequest(otherPeer, otherMsg1) + require.NoError(t, err) + msgWait <- struct{}{} + + _, ok = msgs.Load("otherId1") + require.True(t, ok) + close(msgRelease) + fx.requestManager.Close(context.Background()) + }) + + t.Run("no requests after close", func(t *testing.T) { + fx := newFixture(t) + defer fx.stop() + fx.requestManager.workers = 1 + msgRelease := make(chan struct{}) + msgWait := make(chan struct{}) + msgs := sync.Map{} + doRequestAndHandle = func(manager *requestManager, peerId string, req *spacesyncproto.ObjectSyncMessage) { + msgs.Store(req.ObjectId, struct{}{}) + <-msgWait + <-msgRelease + } + msg1 := &spacesyncproto.ObjectSyncMessage{ObjectId: "id1"} + msg2 := &spacesyncproto.ObjectSyncMessage{ObjectId: "id2"} + + // sending requests to first peer + peerId := "peerId" + err := fx.requestManager.QueueRequest(peerId, msg1) + require.NoError(t, err) + err = fx.requestManager.QueueRequest(peerId, msg2) + require.NoError(t, err) + + // waiting until all the message is loaded + msgWait <- struct{}{} + _, ok := msgs.Load("id1") + require.True(t, ok) + _, ok = msgs.Load("id2") + require.False(t, ok) + + fx.requestManager.Close(context.Background()) + close(msgRelease) + _, ok = msgs.Load("id2") + require.False(t, ok) + }) +} diff --git a/commonspace/settings/deleter.go b/commonspace/settings/deleter.go index 322308de..5d3bd5a1 100644 --- a/commonspace/settings/deleter.go +++ b/commonspace/settings/deleter.go @@ -2,9 +2,9 @@ package settings import ( "context" + "github.com/anyproto/any-sync/commonspace/deletionstate" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/object/treemanager" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate" "github.com/anyproto/any-sync/commonspace/spacestorage" "go.uber.org/zap" ) @@ -15,11 +15,11 @@ type Deleter interface { type deleter struct { st spacestorage.SpaceStorage - state settingsstate.ObjectDeletionState + state deletionstate.ObjectDeletionState getter treemanager.TreeManager } -func newDeleter(st spacestorage.SpaceStorage, state settingsstate.ObjectDeletionState, getter treemanager.TreeManager) Deleter { +func newDeleter(st spacestorage.SpaceStorage, state deletionstate.ObjectDeletionState, getter treemanager.TreeManager) Deleter { return &deleter{st, state, getter} } diff --git a/commonspace/settings/deleter_test.go b/commonspace/settings/deleter_test.go index 54feed56..c057207a 100644 --- a/commonspace/settings/deleter_test.go +++ b/commonspace/settings/deleter_test.go @@ -2,11 +2,11 @@ package settings import ( "fmt" + "github.com/anyproto/any-sync/commonspace/deletionstate/mock_deletionstate" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/object/treemanager/mock_treemanager" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate/mock_settingsstate" "github.com/anyproto/any-sync/commonspace/spacestorage/mock_spacestorage" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" "testing" ) @@ -14,7 +14,7 @@ func TestDeleter_Delete(t *testing.T) { ctrl := gomock.NewController(t) treeManager := mock_treemanager.NewMockTreeManager(ctrl) st := mock_spacestorage.NewMockSpaceStorage(ctrl) - delState := mock_settingsstate.NewMockObjectDeletionState(ctrl) + delState := mock_deletionstate.NewMockObjectDeletionState(ctrl) deleter := newDeleter(st, delState, treeManager) diff --git a/commonspace/settings/deletionmanager.go b/commonspace/settings/deletionmanager.go index 2d3d47ff..2611d28c 100644 --- a/commonspace/settings/deletionmanager.go +++ b/commonspace/settings/deletionmanager.go @@ -2,6 +2,7 @@ package settings import ( "context" + "github.com/anyproto/any-sync/commonspace/deletionstate" "github.com/anyproto/any-sync/commonspace/object/treemanager" "github.com/anyproto/any-sync/commonspace/settings/settingsstate" "go.uber.org/zap" @@ -20,7 +21,7 @@ func newDeletionManager( settingsId string, isResponsible bool, treeManager treemanager.TreeManager, - deletionState settingsstate.ObjectDeletionState, + deletionState deletionstate.ObjectDeletionState, provider SpaceIdsProvider, onSpaceDelete func()) DeletionManager { return &deletionManager{ @@ -35,7 +36,7 @@ func newDeletionManager( } type deletionManager struct { - deletionState settingsstate.ObjectDeletionState + deletionState deletionstate.ObjectDeletionState provider SpaceIdsProvider treeManager treemanager.TreeManager spaceId string diff --git a/commonspace/settings/deletionmanager_test.go b/commonspace/settings/deletionmanager_test.go index 9e6b4f05..fb1e7355 100644 --- a/commonspace/settings/deletionmanager_test.go +++ b/commonspace/settings/deletionmanager_test.go @@ -2,12 +2,12 @@ package settings import ( "context" + "github.com/anyproto/any-sync/commonspace/deletionstate/mock_deletionstate" "github.com/anyproto/any-sync/commonspace/object/treemanager/mock_treemanager" "github.com/anyproto/any-sync/commonspace/settings/mock_settings" "github.com/anyproto/any-sync/commonspace/settings/settingsstate" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate/mock_settingsstate" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "testing" ) @@ -26,7 +26,7 @@ func TestDeletionManager_UpdateState_NotResponsible(t *testing.T) { onDeleted := func() { deleted = true } - delState := mock_settingsstate.NewMockObjectDeletionState(ctrl) + delState := mock_deletionstate.NewMockObjectDeletionState(ctrl) treeManager := mock_treemanager.NewMockTreeManager(ctrl) delState.EXPECT().Add(state.DeletedIds) @@ -58,7 +58,7 @@ func TestDeletionManager_UpdateState_Responsible(t *testing.T) { onDeleted := func() { deleted = true } - delState := mock_settingsstate.NewMockObjectDeletionState(ctrl) + delState := mock_deletionstate.NewMockObjectDeletionState(ctrl) treeManager := mock_treemanager.NewMockTreeManager(ctrl) provider := mock_settings.NewMockSpaceIdsProvider(ctrl) diff --git a/commonspace/settings/mock_settings/mock_settings.go b/commonspace/settings/mock_settings/mock_settings.go index 69598251..2d5da99f 100644 --- a/commonspace/settings/mock_settings/mock_settings.go +++ b/commonspace/settings/mock_settings/mock_settings.go @@ -9,7 +9,7 @@ import ( reflect "reflect" settingsstate "github.com/anyproto/any-sync/commonspace/settings/settingsstate" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockDeletionManager is a mock of DeletionManager interface. diff --git a/commonspace/settings/settings.go b/commonspace/settings/settings.go index 5c2b41cf..c993c715 100644 --- a/commonspace/settings/settings.go +++ b/commonspace/settings/settings.go @@ -1,328 +1,122 @@ -//go:generate mockgen -destination mock_settings/mock_settings.go github.com/anyproto/any-sync/commonspace/settings DeletionManager,Deleter,SpaceIdsProvider package settings import ( "context" - "errors" - "fmt" - "github.com/anyproto/any-sync/util/crypto" + "sync/atomic" "github.com/anyproto/any-sync/accountservice" - "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/deletionstate" + "github.com/anyproto/any-sync/commonspace/headsync" "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/tree/synctree/updatelistener" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/treemanager" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate" + "github.com/anyproto/any-sync/commonspace/objecttreebuilder" + "github.com/anyproto/any-sync/commonspace/spacestate" "github.com/anyproto/any-sync/commonspace/spacestorage" - "github.com/anyproto/any-sync/commonspace/spacesyncproto" "github.com/anyproto/any-sync/nodeconf" - "github.com/gogo/protobuf/proto" "go.uber.org/zap" - "golang.org/x/exp/slices" ) -var log = logger.NewNamed("common.commonspace.settings") +const CName = "common.commonspace.settings" -type SettingsObject interface { - synctree.SyncTree - Init(ctx context.Context) (err error) - DeleteObject(id string) (err error) - DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error) - SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error) +type Settings interface { + DeleteTree(ctx context.Context, id string) (err error) + SpaceDeleteRawChange(ctx context.Context) (raw *treechangeproto.RawTreeChangeWithId, err error) + DeleteSpace(ctx context.Context, deleteChange *treechangeproto.RawTreeChangeWithId) (err error) + SettingsObject() SettingsObject + app.ComponentRunnable } -var ( - ErrDeleteSelf = errors.New("cannot delete self") - ErrAlreadyDeleted = errors.New("the object is already deleted") - ErrObjDoesNotExist = errors.New("the object does not exist") - ErrCantDeleteSpace = errors.New("not able to delete space") -) - -var ( - DoSnapshot = objecttree.DoSnapshot - buildHistoryTree = func(objTree objecttree.ObjectTree) (objecttree.ReadableObjectTree, error) { - return objecttree.BuildHistoryTree(objecttree.HistoryTreeParams{ - TreeStorage: objTree.Storage(), - AclList: objTree.AclList(), - BuildFullTree: true, - }) - } -) - -type BuildTreeFunc func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error) - -type Deps struct { - BuildFunc BuildTreeFunc - Account accountservice.Service - TreeManager treemanager.TreeManager - Store spacestorage.SpaceStorage - Configuration nodeconf.NodeConf - DeletionState settingsstate.ObjectDeletionState - Provider SpaceIdsProvider - OnSpaceDelete func() - // testing dependencies - builder settingsstate.StateBuilder - del Deleter - delManager DeletionManager - changeFactory settingsstate.ChangeFactory +func New() Settings { + return &settings{} } -type settingsObject struct { - synctree.SyncTree - account accountservice.Service - spaceId string - treeManager treemanager.TreeManager - store spacestorage.SpaceStorage - builder settingsstate.StateBuilder - buildFunc BuildTreeFunc - loop *deleteLoop +type settings struct { + account accountservice.Service + treeManager treemanager.TreeManager + storage spacestorage.SpaceStorage + configuration nodeconf.NodeConf + deletionState deletionstate.ObjectDeletionState + headsync headsync.HeadSync + treeBuilder objecttreebuilder.TreeBuilderComponent + spaceIsDeleted *atomic.Bool - state *settingsstate.State - deletionState settingsstate.ObjectDeletionState - deletionManager DeletionManager - changeFactory settingsstate.ChangeFactory + settingsObject SettingsObject } -func NewSettingsObject(deps Deps, spaceId string) (obj SettingsObject) { - var ( - deleter Deleter - deletionManager DeletionManager - builder settingsstate.StateBuilder - changeFactory settingsstate.ChangeFactory - ) - if deps.del == nil { - deleter = newDeleter(deps.Store, deps.DeletionState, deps.TreeManager) - } else { - deleter = deps.del - } - if deps.delManager == nil { - deletionManager = newDeletionManager( - spaceId, - deps.Store.SpaceSettingsId(), - deps.Configuration.IsResponsible(spaceId), - deps.TreeManager, - deps.DeletionState, - deps.Provider, - deps.OnSpaceDelete) - } else { - deletionManager = deps.delManager - } - if deps.builder == nil { - builder = settingsstate.NewStateBuilder() - } else { - builder = deps.builder - } - if deps.changeFactory == nil { - changeFactory = settingsstate.NewChangeFactory() - } else { - changeFactory = deps.changeFactory - } +func (s *settings) Init(a *app.App) (err error) { + s.account = a.MustComponent(accountservice.CName).(accountservice.Service) + s.treeManager = app.MustComponent[treemanager.TreeManager](a) + s.headsync = a.MustComponent(headsync.CName).(headsync.HeadSync) + s.configuration = a.MustComponent(nodeconf.CName).(nodeconf.NodeConf) + s.deletionState = a.MustComponent(deletionstate.CName).(deletionstate.ObjectDeletionState) + s.treeBuilder = a.MustComponent(objecttreebuilder.CName).(objecttreebuilder.TreeBuilderComponent) - loop := newDeleteLoop(func() { - deleter.Delete() - }) - deps.DeletionState.AddObserver(func(ids []string) { - loop.notify() - }) + sharedState := a.MustComponent(spacestate.CName).(*spacestate.SpaceState) + s.storage = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + s.spaceIsDeleted = sharedState.SpaceIsDeleted - s := &settingsObject{ - loop: loop, - spaceId: spaceId, - account: deps.Account, - deletionState: deps.DeletionState, - treeManager: deps.TreeManager, - store: deps.Store, - buildFunc: deps.BuildFunc, - builder: builder, - deletionManager: deletionManager, - changeFactory: changeFactory, - } - obj = s - return -} - -func (s *settingsObject) updateIds(tr objecttree.ObjectTree) { - var err error - s.state, err = s.builder.Build(tr, s.state) - if err != nil { - log.Error("failed to build state", zap.Error(err)) - return - } - log.Debug("updating object state", zap.String("deleted by", s.state.DeleterId)) - if err = s.deletionManager.UpdateState(context.Background(), s.state); err != nil { - log.Error("failed to update state", zap.Error(err)) - } -} - -// Update is called as part of UpdateListener interface -func (s *settingsObject) Update(tr objecttree.ObjectTree) { - s.updateIds(tr) -} - -// Rebuild is called as part of UpdateListener interface (including when the object is built for the first time, e.g. on Init call) -func (s *settingsObject) Rebuild(tr objecttree.ObjectTree) { - // at initial build "s" may not contain the object tree, so it is safer to provide it from the function parameter - s.state = nil - s.updateIds(tr) -} - -func (s *settingsObject) Init(ctx context.Context) (err error) { - settingsId := s.store.SpaceSettingsId() - log.Debug("space settings id", zap.String("id", settingsId)) - s.SyncTree, err = s.buildFunc(ctx, settingsId, s) - if err != nil { - return - } - // TODO: remove this check when everybody updates - if err = s.checkHistoryState(ctx); err != nil { - return - } - s.loop.Run() - return -} - -func (s *settingsObject) checkHistoryState(ctx context.Context) (err error) { - historyTree, err := buildHistoryTree(s.SyncTree) - if err != nil { - return - } - fullState, err := s.builder.Build(historyTree, nil) - if err != nil { - return - } - if len(fullState.DeletedIds) != len(s.state.DeletedIds) { - log.WarnCtx(ctx, "state does not have all deleted ids", - zap.Int("fullstate ids", len(fullState.DeletedIds)), - zap.Int("state ids", len(fullState.DeletedIds))) - s.state = fullState - err = s.deletionManager.UpdateState(context.Background(), s.state) - if err != nil { + deps := Deps{ + BuildFunc: func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error) { + res, err := s.treeBuilder.BuildTree(ctx, id, objecttreebuilder.BuildTreeOpts{ + Listener: listener, + // space settings document should not have empty data + TreeBuilder: objecttree.BuildObjectTree, + }) + log.Debug("building settings tree", zap.String("id", id), zap.String("spaceId", sharedState.SpaceId)) + if err != nil { + return + } + t = res.(synctree.SyncTree) return - } + }, + Account: s.account, + TreeManager: s.treeManager, + Store: s.storage, + Configuration: s.configuration, + DeletionState: s.deletionState, + Provider: s.headsync, + OnSpaceDelete: s.onSpaceDelete, } - return + s.settingsObject = NewSettingsObject(deps, sharedState.SpaceId) + return nil } -func (s *settingsObject) Close() error { - s.loop.Close() - return s.SyncTree.Close() +func (s *settings) Name() (name string) { + return CName } -func (s *settingsObject) DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error) { - s.Lock() - defer s.Unlock() - defer func() { - log.Debug("finished adding delete change", zap.Error(err)) - }() - err = s.verifyDeleteSpace(raw) - if err != nil { - return - } - res, err := s.AddRawChanges(ctx, objecttree.RawChangesPayload{ - NewHeads: []string{raw.Id}, - RawChanges: []*treechangeproto.RawTreeChangeWithId{raw}, - }) - if err != nil { - return - } - if !slices.Contains(res.Heads, raw.Id) { - err = ErrCantDeleteSpace - return - } - return +func (s *settings) Run(ctx context.Context) (err error) { + return s.settingsObject.Init(ctx) } -func (s *settingsObject) SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error) { - accountData := s.account.Account() - data, err := s.changeFactory.CreateSpaceDeleteChange(accountData.PeerId, s.state, false) - if err != nil { - return - } - return s.PrepareChange(objecttree.SignableChangeContent{ - Data: data, - Key: accountData.SignKey, - IsSnapshot: false, - IsEncrypted: false, - }) +func (s *settings) Close(ctx context.Context) (err error) { + return s.settingsObject.Close() } -func (s *settingsObject) DeleteObject(id string) (err error) { - s.Lock() - defer s.Unlock() - if s.Id() == id { - err = ErrDeleteSelf - return - } - if s.state.Exists(id) { - err = ErrAlreadyDeleted - return nil - } - _, err = s.store.TreeStorage(id) - if err != nil { - err = ErrObjDoesNotExist - return - } - isSnapshot := DoSnapshot(s.Len()) - res, err := s.changeFactory.CreateObjectDeleteChange(id, s.state, isSnapshot) - if err != nil { - return - } - - return s.addContent(res, isSnapshot) +func (s *settings) DeleteTree(ctx context.Context, id string) (err error) { + return s.settingsObject.DeleteObject(id) } -func (s *settingsObject) verifyDeleteSpace(raw *treechangeproto.RawTreeChangeWithId) (err error) { - data, err := s.UnpackChange(raw) - if err != nil { - return - } - return verifyDeleteContent(data, "") +func (s *settings) SpaceDeleteRawChange(ctx context.Context) (raw *treechangeproto.RawTreeChangeWithId, err error) { + return s.settingsObject.SpaceDeleteRawChange() } -func (s *settingsObject) addContent(data []byte, isSnapshot bool) (err error) { - accountData := s.account.Account() - res, err := s.AddContent(context.Background(), objecttree.SignableChangeContent{ - Data: data, - Key: accountData.SignKey, - IsSnapshot: isSnapshot, - IsEncrypted: false, - }) - if err != nil { - return - } - if res.Mode == objecttree.Rebuild { - s.Rebuild(s) - } else { - s.Update(s) - } - return +func (s *settings) DeleteSpace(ctx context.Context, deleteChange *treechangeproto.RawTreeChangeWithId) (err error) { + return s.settingsObject.DeleteSpace(ctx, deleteChange) } -func VerifyDeleteChange(raw *treechangeproto.RawTreeChangeWithId, identity crypto.PubKey, peerId string) (err error) { - changeBuilder := objecttree.NewChangeBuilder(crypto.NewKeyStorage(), nil) - res, err := changeBuilder.Unmarshall(raw, true) +func (s *settings) onSpaceDelete() { + err := s.storage.SetSpaceDeleted() if err != nil { - return + log.Warn("failed to set space deleted") } - if !res.Identity.Equals(identity) { - return fmt.Errorf("incorrect identity") - } - return verifyDeleteContent(res.Data, peerId) + s.spaceIsDeleted.Swap(true) } -func verifyDeleteContent(data []byte, peerId string) (err error) { - content := &spacesyncproto.SettingsData{} - err = proto.Unmarshal(data, content) - if err != nil { - return - } - if len(content.GetContent()) != 1 || - content.GetContent()[0].GetSpaceDelete() == nil || - (peerId == "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() == "") || - (peerId != "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() != peerId) { - return fmt.Errorf("incorrect delete change payload") - } - return +func (s *settings) SettingsObject() SettingsObject { + return s.settingsObject } diff --git a/commonspace/settings/settingsobject.go b/commonspace/settings/settingsobject.go new file mode 100644 index 00000000..e6fd0b39 --- /dev/null +++ b/commonspace/settings/settingsobject.go @@ -0,0 +1,329 @@ +//go:generate mockgen -destination mock_settings/mock_settings.go github.com/anyproto/any-sync/commonspace/settings DeletionManager,Deleter,SpaceIdsProvider +package settings + +import ( + "context" + "errors" + "fmt" + "github.com/anyproto/any-sync/commonspace/deletionstate" + "github.com/anyproto/any-sync/util/crypto" + + "github.com/anyproto/any-sync/accountservice" + "github.com/anyproto/any-sync/app/logger" + "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/tree/synctree/updatelistener" + "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anyproto/any-sync/commonspace/object/treemanager" + "github.com/anyproto/any-sync/commonspace/settings/settingsstate" + "github.com/anyproto/any-sync/commonspace/spacestorage" + "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/nodeconf" + "github.com/gogo/protobuf/proto" + "go.uber.org/zap" + "golang.org/x/exp/slices" +) + +var log = logger.NewNamed("common.commonspace.settings") + +type SettingsObject interface { + synctree.SyncTree + Init(ctx context.Context) (err error) + DeleteObject(id string) (err error) + DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error) + SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error) +} + +var ( + ErrDeleteSelf = errors.New("cannot delete self") + ErrAlreadyDeleted = errors.New("the object is already deleted") + ErrObjDoesNotExist = errors.New("the object does not exist") + ErrCantDeleteSpace = errors.New("not able to delete space") +) + +var ( + DoSnapshot = objecttree.DoSnapshot + buildHistoryTree = func(objTree objecttree.ObjectTree) (objecttree.ReadableObjectTree, error) { + return objecttree.BuildHistoryTree(objecttree.HistoryTreeParams{ + TreeStorage: objTree.Storage(), + AclList: objTree.AclList(), + BuildFullTree: true, + }) + } +) + +type BuildTreeFunc func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error) + +type Deps struct { + BuildFunc BuildTreeFunc + Account accountservice.Service + TreeManager treemanager.TreeManager + Store spacestorage.SpaceStorage + Configuration nodeconf.NodeConf + DeletionState deletionstate.ObjectDeletionState + Provider SpaceIdsProvider + OnSpaceDelete func() + // testing dependencies + builder settingsstate.StateBuilder + del Deleter + delManager DeletionManager + changeFactory settingsstate.ChangeFactory +} + +type settingsObject struct { + synctree.SyncTree + account accountservice.Service + spaceId string + treeManager treemanager.TreeManager + store spacestorage.SpaceStorage + builder settingsstate.StateBuilder + buildFunc BuildTreeFunc + loop *deleteLoop + + state *settingsstate.State + deletionState deletionstate.ObjectDeletionState + deletionManager DeletionManager + changeFactory settingsstate.ChangeFactory +} + +func NewSettingsObject(deps Deps, spaceId string) (obj SettingsObject) { + var ( + deleter Deleter + deletionManager DeletionManager + builder settingsstate.StateBuilder + changeFactory settingsstate.ChangeFactory + ) + if deps.del == nil { + deleter = newDeleter(deps.Store, deps.DeletionState, deps.TreeManager) + } else { + deleter = deps.del + } + if deps.delManager == nil { + deletionManager = newDeletionManager( + spaceId, + deps.Store.SpaceSettingsId(), + deps.Configuration.IsResponsible(spaceId), + deps.TreeManager, + deps.DeletionState, + deps.Provider, + deps.OnSpaceDelete) + } else { + deletionManager = deps.delManager + } + if deps.builder == nil { + builder = settingsstate.NewStateBuilder() + } else { + builder = deps.builder + } + if deps.changeFactory == nil { + changeFactory = settingsstate.NewChangeFactory() + } else { + changeFactory = deps.changeFactory + } + + loop := newDeleteLoop(func() { + deleter.Delete() + }) + deps.DeletionState.AddObserver(func(ids []string) { + loop.notify() + }) + + s := &settingsObject{ + loop: loop, + spaceId: spaceId, + account: deps.Account, + deletionState: deps.DeletionState, + treeManager: deps.TreeManager, + store: deps.Store, + buildFunc: deps.BuildFunc, + builder: builder, + deletionManager: deletionManager, + changeFactory: changeFactory, + } + obj = s + return +} + +func (s *settingsObject) updateIds(tr objecttree.ObjectTree) { + var err error + s.state, err = s.builder.Build(tr, s.state) + if err != nil { + log.Error("failed to build state", zap.Error(err)) + return + } + log.Debug("updating object state", zap.String("deleted by", s.state.DeleterId)) + if err = s.deletionManager.UpdateState(context.Background(), s.state); err != nil { + log.Error("failed to update state", zap.Error(err)) + } +} + +// Update is called as part of UpdateListener interface +func (s *settingsObject) Update(tr objecttree.ObjectTree) { + s.updateIds(tr) +} + +// Rebuild is called as part of UpdateListener interface (including when the object is built for the first time, e.g. on Init call) +func (s *settingsObject) Rebuild(tr objecttree.ObjectTree) { + // at initial build "s" may not contain the object tree, so it is safer to provide it from the function parameter + s.state = nil + s.updateIds(tr) +} + +func (s *settingsObject) Init(ctx context.Context) (err error) { + settingsId := s.store.SpaceSettingsId() + log.Debug("space settings id", zap.String("id", settingsId)) + s.SyncTree, err = s.buildFunc(ctx, settingsId, s) + if err != nil { + return + } + // TODO: remove this check when everybody updates + if err = s.checkHistoryState(ctx); err != nil { + return + } + s.loop.Run() + return +} + +func (s *settingsObject) checkHistoryState(ctx context.Context) (err error) { + historyTree, err := buildHistoryTree(s.SyncTree) + if err != nil { + return + } + fullState, err := s.builder.Build(historyTree, nil) + if err != nil { + return + } + if len(fullState.DeletedIds) != len(s.state.DeletedIds) { + log.WarnCtx(ctx, "state does not have all deleted ids", + zap.Int("fullstate ids", len(fullState.DeletedIds)), + zap.Int("state ids", len(fullState.DeletedIds))) + s.state = fullState + err = s.deletionManager.UpdateState(context.Background(), s.state) + if err != nil { + return + } + } + return +} + +func (s *settingsObject) Close() error { + s.loop.Close() + return s.SyncTree.Close() +} + +func (s *settingsObject) DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error) { + s.Lock() + defer s.Unlock() + defer func() { + log.Debug("finished adding delete change", zap.Error(err)) + }() + err = s.verifyDeleteSpace(raw) + if err != nil { + return + } + res, err := s.AddRawChanges(ctx, objecttree.RawChangesPayload{ + NewHeads: []string{raw.Id}, + RawChanges: []*treechangeproto.RawTreeChangeWithId{raw}, + }) + if err != nil { + return + } + if !slices.Contains(res.Heads, raw.Id) { + err = ErrCantDeleteSpace + return + } + return +} + +func (s *settingsObject) SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error) { + accountData := s.account.Account() + data, err := s.changeFactory.CreateSpaceDeleteChange(accountData.PeerId, s.state, false) + if err != nil { + return + } + return s.PrepareChange(objecttree.SignableChangeContent{ + Data: data, + Key: accountData.SignKey, + IsSnapshot: false, + IsEncrypted: false, + }) +} + +func (s *settingsObject) DeleteObject(id string) (err error) { + s.Lock() + defer s.Unlock() + if s.Id() == id { + err = ErrDeleteSelf + return + } + if s.state.Exists(id) { + err = ErrAlreadyDeleted + return nil + } + _, err = s.store.TreeStorage(id) + if err != nil { + err = ErrObjDoesNotExist + return + } + isSnapshot := DoSnapshot(s.Len()) + res, err := s.changeFactory.CreateObjectDeleteChange(id, s.state, isSnapshot) + if err != nil { + return + } + + return s.addContent(res, isSnapshot) +} + +func (s *settingsObject) verifyDeleteSpace(raw *treechangeproto.RawTreeChangeWithId) (err error) { + data, err := s.UnpackChange(raw) + if err != nil { + return + } + return verifyDeleteContent(data, "") +} + +func (s *settingsObject) addContent(data []byte, isSnapshot bool) (err error) { + accountData := s.account.Account() + res, err := s.AddContent(context.Background(), objecttree.SignableChangeContent{ + Data: data, + Key: accountData.SignKey, + IsSnapshot: isSnapshot, + IsEncrypted: false, + }) + if err != nil { + return + } + if res.Mode == objecttree.Rebuild { + s.Rebuild(s) + } else { + s.Update(s) + } + return +} + +func VerifyDeleteChange(raw *treechangeproto.RawTreeChangeWithId, identity crypto.PubKey, peerId string) (err error) { + changeBuilder := objecttree.NewChangeBuilder(crypto.NewKeyStorage(), nil) + res, err := changeBuilder.Unmarshall(raw, true) + if err != nil { + return + } + if !res.Identity.Equals(identity) { + return fmt.Errorf("incorrect identity") + } + return verifyDeleteContent(res.Data, peerId) +} + +func verifyDeleteContent(data []byte, peerId string) (err error) { + content := &spacesyncproto.SettingsData{} + err = proto.Unmarshal(data, content) + if err != nil { + return + } + if len(content.GetContent()) != 1 || + content.GetContent()[0].GetSpaceDelete() == nil || + (peerId == "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() == "") || + (peerId != "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() != peerId) { + return fmt.Errorf("incorrect delete change payload") + } + return +} diff --git a/commonspace/settings/settings_test.go b/commonspace/settings/settingsobject_test.go similarity index 97% rename from commonspace/settings/settings_test.go rename to commonspace/settings/settingsobject_test.go index 31956c81..7d2f4eaa 100644 --- a/commonspace/settings/settings_test.go +++ b/commonspace/settings/settingsobject_test.go @@ -3,6 +3,7 @@ package settings import ( "context" "github.com/anyproto/any-sync/accountservice/mock_accountservice" + "github.com/anyproto/any-sync/commonspace/deletionstate/mock_deletionstate" "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree" @@ -15,8 +16,8 @@ import ( "github.com/anyproto/any-sync/commonspace/settings/settingsstate" "github.com/anyproto/any-sync/commonspace/settings/settingsstate/mock_settingsstate" "github.com/anyproto/any-sync/commonspace/spacestorage/mock_spacestorage" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "sync" "testing" "time" @@ -54,7 +55,7 @@ type settingsFixture struct { deleter *mock_settings.MockDeleter syncTree *mock_synctree.MockSyncTree historyTree *mock_objecttree.MockObjectTree - delState *mock_settingsstate.MockObjectDeletionState + delState *mock_deletionstate.MockObjectDeletionState account *mock_accountservice.MockService } @@ -66,7 +67,7 @@ func newSettingsFixture(t *testing.T) *settingsFixture { acc := mock_accountservice.NewMockService(ctrl) treeManager := mock_treemanager.NewMockTreeManager(ctrl) st := mock_spacestorage.NewMockSpaceStorage(ctrl) - delState := mock_settingsstate.NewMockObjectDeletionState(ctrl) + delState := mock_deletionstate.NewMockObjectDeletionState(ctrl) delManager := mock_settings.NewMockDeletionManager(ctrl) stateBuilder := mock_settingsstate.NewMockStateBuilder(ctrl) changeFactory := mock_settingsstate.NewMockChangeFactory(ctrl) diff --git a/commonspace/settings/settingsstate/mock_settingsstate/mock_settingsstate.go b/commonspace/settings/settingsstate/mock_settingsstate/mock_settingsstate.go index 0bc9bf23..091c3ea8 100644 --- a/commonspace/settings/settingsstate/mock_settingsstate/mock_settingsstate.go +++ b/commonspace/settings/settingsstate/mock_settingsstate/mock_settingsstate.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/anyproto/any-sync/commonspace/settings/settingsstate (interfaces: ObjectDeletionState,StateBuilder,ChangeFactory) +// Source: github.com/anyproto/any-sync/commonspace/settings/settingsstate (interfaces: StateBuilder,ChangeFactory) // Package mock_settingsstate is a generated GoMock package. package mock_settingsstate @@ -9,112 +9,9 @@ import ( objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" settingsstate "github.com/anyproto/any-sync/commonspace/settings/settingsstate" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) -// MockObjectDeletionState is a mock of ObjectDeletionState interface. -type MockObjectDeletionState struct { - ctrl *gomock.Controller - recorder *MockObjectDeletionStateMockRecorder -} - -// MockObjectDeletionStateMockRecorder is the mock recorder for MockObjectDeletionState. -type MockObjectDeletionStateMockRecorder struct { - mock *MockObjectDeletionState -} - -// NewMockObjectDeletionState creates a new mock instance. -func NewMockObjectDeletionState(ctrl *gomock.Controller) *MockObjectDeletionState { - mock := &MockObjectDeletionState{ctrl: ctrl} - mock.recorder = &MockObjectDeletionStateMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockObjectDeletionState) EXPECT() *MockObjectDeletionStateMockRecorder { - return m.recorder -} - -// Add mocks base method. -func (m *MockObjectDeletionState) Add(arg0 map[string]struct{}) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Add", arg0) -} - -// Add indicates an expected call of Add. -func (mr *MockObjectDeletionStateMockRecorder) Add(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockObjectDeletionState)(nil).Add), arg0) -} - -// AddObserver mocks base method. -func (m *MockObjectDeletionState) AddObserver(arg0 settingsstate.StateUpdateObserver) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "AddObserver", arg0) -} - -// AddObserver indicates an expected call of AddObserver. -func (mr *MockObjectDeletionStateMockRecorder) AddObserver(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddObserver", reflect.TypeOf((*MockObjectDeletionState)(nil).AddObserver), arg0) -} - -// Delete mocks base method. -func (m *MockObjectDeletionState) Delete(arg0 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete. -func (mr *MockObjectDeletionStateMockRecorder) Delete(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockObjectDeletionState)(nil).Delete), arg0) -} - -// Exists mocks base method. -func (m *MockObjectDeletionState) Exists(arg0 string) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Exists", arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// Exists indicates an expected call of Exists. -func (mr *MockObjectDeletionStateMockRecorder) Exists(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockObjectDeletionState)(nil).Exists), arg0) -} - -// Filter mocks base method. -func (m *MockObjectDeletionState) Filter(arg0 []string) []string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Filter", arg0) - ret0, _ := ret[0].([]string) - return ret0 -} - -// Filter indicates an expected call of Filter. -func (mr *MockObjectDeletionStateMockRecorder) Filter(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockObjectDeletionState)(nil).Filter), arg0) -} - -// GetQueued mocks base method. -func (m *MockObjectDeletionState) GetQueued() []string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetQueued") - ret0, _ := ret[0].([]string) - return ret0 -} - -// GetQueued indicates an expected call of GetQueued. -func (mr *MockObjectDeletionStateMockRecorder) GetQueued() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueued", reflect.TypeOf((*MockObjectDeletionState)(nil).GetQueued)) -} - // MockStateBuilder is a mock of StateBuilder interface. type MockStateBuilder struct { ctrl *gomock.Controller diff --git a/commonspace/settings/settingsstate/settingsstate.go b/commonspace/settings/settingsstate/settingsstate.go index 0b62c8d1..20619cb2 100644 --- a/commonspace/settings/settingsstate/settingsstate.go +++ b/commonspace/settings/settingsstate/settingsstate.go @@ -1,3 +1,4 @@ +//go:generate mockgen -destination mock_settingsstate/mock_settingsstate.go github.com/anyproto/any-sync/commonspace/settings/settingsstate StateBuilder,ChangeFactory package settingsstate import "github.com/anyproto/any-sync/commonspace/spacesyncproto" diff --git a/commonspace/settings/settingsstate/statebuilder_test.go b/commonspace/settings/settingsstate/statebuilder_test.go index a2cf3072..c2d1d4ed 100644 --- a/commonspace/settings/settingsstate/statebuilder_test.go +++ b/commonspace/settings/settingsstate/statebuilder_test.go @@ -4,8 +4,8 @@ import ( "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree" "github.com/anyproto/any-sync/commonspace/spacesyncproto" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "testing" ) diff --git a/commonspace/space.go b/commonspace/space.go index 70bf6e0c..d2d6d486 100644 --- a/commonspace/space.go +++ b/commonspace/space.go @@ -2,44 +2,26 @@ package commonspace import ( "context" - "errors" - "github.com/anyproto/any-sync/accountservice" - "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/commonspace/headsync" "github.com/anyproto/any-sync/commonspace/object/acl/list" "github.com/anyproto/any-sync/commonspace/object/acl/syncacl" - "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/tree/synctree/updatelistener" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" - "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/objectsync" - "github.com/anyproto/any-sync/commonspace/peermanager" + "github.com/anyproto/any-sync/commonspace/objecttreebuilder" "github.com/anyproto/any-sync/commonspace/settings" - "github.com/anyproto/any-sync/commonspace/settings/settingsstate" + "github.com/anyproto/any-sync/commonspace/spacestate" "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/metric" - "github.com/anyproto/any-sync/net/peer" - "github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/util/crypto" - "github.com/anyproto/any-sync/util/multiqueue" - "github.com/anyproto/any-sync/util/slice" - "github.com/cheggaaa/mb/v3" - "github.com/zeebo/errs" "go.uber.org/zap" "strconv" "strings" "sync" - "sync/atomic" "time" ) -var ( - ErrSpaceClosed = errors.New("space is closed") -) - type SpaceCreatePayload struct { // SigningKey is the signing key of the owner SigningKey crypto.PrivKey @@ -55,25 +37,6 @@ type SpaceCreatePayload struct { MasterKey crypto.PrivKey } -type HandleMessage struct { - Id uint64 - ReceiveTime time.Time - StartHandlingTime time.Time - Deadline time.Time - SenderId string - Message *spacesyncproto.ObjectSyncMessage - PeerCtx context.Context -} - -func (m HandleMessage) LogFields(fields ...zap.Field) []zap.Field { - return append(fields, - metric.SpaceId(m.Message.SpaceId), - metric.ObjectId(m.Message.ObjectId), - metric.QueueDur(m.StartHandlingTime.Sub(m.ReceiveTime)), - metric.TotalDur(time.Since(m.ReceiveTime)), - ) -} - type SpaceDerivePayload struct { SigningKey crypto.PrivKey MasterKey crypto.PrivKey @@ -96,58 +59,42 @@ func NewSpaceId(id string, repKey uint64) string { type Space interface { Id() string Init(ctx context.Context) error + Acl() list.AclList StoredIds() []string DebugAllHeads() []headsync.TreeHeads - Description() (SpaceDescription, error) + Description() (desc SpaceDescription, err error) - CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) - PutTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload, listener updatelistener.UpdateListener) (t objecttree.ObjectTree, err error) - BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t objecttree.ObjectTree, err error) - DeleteTree(ctx context.Context, id string) (err error) - BuildHistoryTree(ctx context.Context, id string, opts HistoryTreeOpts) (t objecttree.HistoryTree, err error) - - SpaceDeleteRawChange(ctx context.Context) (raw *treechangeproto.RawTreeChangeWithId, err error) - DeleteSpace(ctx context.Context, deleteChange *treechangeproto.RawTreeChangeWithId) (err error) - - HeadSync() headsync.HeadSync - ObjectSync() objectsync.ObjectSync + TreeBuilder() objecttreebuilder.TreeBuilder SyncStatus() syncstatus.StatusUpdater Storage() spacestorage.SpaceStorage - HandleMessage(ctx context.Context, msg HandleMessage) (err error) + DeleteTree(ctx context.Context, id string) (err error) + SpaceDeleteRawChange(ctx context.Context) (raw *treechangeproto.RawTreeChangeWithId, err error) + DeleteSpace(ctx context.Context, deleteChange *treechangeproto.RawTreeChangeWithId) (err error) + + HandleMessage(ctx context.Context, msg objectsync.HandleMessage) (err error) + HandleSyncRequest(ctx context.Context, req *spacesyncproto.ObjectSyncMessage) (resp *spacesyncproto.ObjectSyncMessage, err error) + HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) TryClose(objectTTL time.Duration) (close bool, err error) Close() error } type space struct { - id string mu sync.RWMutex header *spacesyncproto.RawSpaceHeaderWithId - objectSync objectsync.ObjectSync - headSync headsync.HeadSync - syncStatus syncstatus.StatusUpdater - storage spacestorage.SpaceStorage - treeManager *commonGetter - account accountservice.Service - aclList *syncacl.SyncAcl - configuration nodeconf.NodeConf - settingsObject settings.SettingsObject - peerManager peermanager.PeerManager - treeBuilder objecttree.BuildObjectTreeFunc - metric metric.Metric + state *spacestate.SpaceState + app *app.App - handleQueue multiqueue.MultiQueue[HandleMessage] - - isClosed *atomic.Bool - isDeleted *atomic.Bool - treesUsed *atomic.Int32 -} - -func (s *space) Id() string { - return s.id + treeBuilder objecttreebuilder.TreeBuilderComponent + headSync headsync.HeadSync + objectSync objectsync.ObjectSync + syncStatus syncstatus.StatusService + settings settings.Settings + storage spacestorage.SpaceStorage + aclList list.AclList } func (s *space) Description() (desc SpaceDescription, err error) { @@ -171,72 +118,64 @@ func (s *space) Description() (desc SpaceDescription, err error) { return } +func (s *space) StoredIds() []string { + return s.headSync.ExternalIds() +} + +func (s *space) DebugAllHeads() []headsync.TreeHeads { + return s.headSync.DebugAllHeads() +} + +func (s *space) DeleteTree(ctx context.Context, id string) (err error) { + return s.settings.DeleteTree(ctx, id) +} + +func (s *space) SpaceDeleteRawChange(ctx context.Context) (raw *treechangeproto.RawTreeChangeWithId, err error) { + return s.settings.SpaceDeleteRawChange(ctx) +} + +func (s *space) DeleteSpace(ctx context.Context, deleteChange *treechangeproto.RawTreeChangeWithId) (err error) { + return s.settings.DeleteSpace(ctx, deleteChange) +} + +func (s *space) HandleMessage(ctx context.Context, msg objectsync.HandleMessage) (err error) { + return s.objectSync.HandleMessage(ctx, msg) +} + +func (s *space) HandleSyncRequest(ctx context.Context, req *spacesyncproto.ObjectSyncMessage) (resp *spacesyncproto.ObjectSyncMessage, err error) { + return s.objectSync.HandleRequest(ctx, req) +} + +func (s *space) HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) { + return s.headSync.HandleRangeRequest(ctx, req) +} + +func (s *space) TreeBuilder() objecttreebuilder.TreeBuilder { + return s.treeBuilder +} + +func (s *space) Acl() list.AclList { + return s.aclList +} + +func (s *space) Id() string { + return s.state.SpaceId +} + func (s *space) Init(ctx context.Context) (err error) { - log.With(zap.String("spaceId", s.id)).Debug("initializing space") - s.storage = newCommonStorage(s.storage) - - header, err := s.storage.SpaceHeader() + err = s.app.Start(ctx) if err != nil { return } - s.header = header - initialIds, err := s.storage.StoredIds() - if err != nil { - return - } - aclStorage, err := s.storage.AclStorage() - if err != nil { - return - } - aclList, err := list.BuildAclListWithIdentity(s.account.Account(), aclStorage) - if err != nil { - return - } - s.aclList = syncacl.NewSyncAcl(aclList, s.objectSync.SyncClient().MessagePool()) - s.treeManager.AddObject(s.aclList) - - deletionState := settingsstate.NewObjectDeletionState(log, s.storage) - deps := settings.Deps{ - BuildFunc: func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error) { - res, err := s.BuildTree(ctx, id, BuildTreeOpts{ - Listener: listener, - WaitTreeRemoteSync: false, - // space settings document should not have empty data - treeBuilder: objecttree.BuildObjectTree, - }) - log.Debug("building settings tree", zap.String("id", id), zap.String("spaceId", s.id)) - if err != nil { - return - } - t = res.(synctree.SyncTree) - return - }, - Account: s.account, - TreeManager: s.treeManager, - Store: s.storage, - DeletionState: deletionState, - Provider: s.headSync, - Configuration: s.configuration, - OnSpaceDelete: s.onSpaceDelete, - } - s.settingsObject = settings.NewSettingsObject(deps, s.id) - s.headSync.Init(initialIds, deletionState) - err = s.settingsObject.Init(ctx) - if err != nil { - return - } - s.treeManager.AddObject(s.settingsObject) - s.syncStatus.Run() - s.handleQueue = multiqueue.New[HandleMessage](s.handleMessage, 100) - return nil -} - -func (s *space) ObjectSync() objectsync.ObjectSync { - return s.objectSync -} - -func (s *space) HeadSync() headsync.HeadSync { - return s.headSync + s.treeBuilder = s.app.MustComponent(objecttreebuilder.CName).(objecttreebuilder.TreeBuilderComponent) + s.headSync = s.app.MustComponent(headsync.CName).(headsync.HeadSync) + s.syncStatus = s.app.MustComponent(syncstatus.CName).(syncstatus.StatusService) + s.settings = s.app.MustComponent(settings.CName).(settings.Settings) + s.objectSync = s.app.MustComponent(objectsync.CName).(objectsync.ObjectSync) + s.storage = s.app.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + s.aclList = s.app.MustComponent(syncacl.CName).(list.AclList) + s.header, err = s.storage.SpaceHeader() + return } func (s *space) SyncStatus() syncstatus.StatusUpdater { @@ -247,246 +186,25 @@ func (s *space) Storage() spacestorage.SpaceStorage { return s.storage } -func (s *space) StoredIds() []string { - return slice.DiscardFromSlice(s.headSync.AllIds(), func(id string) bool { - return id == s.settingsObject.Id() - }) -} - -func (s *space) DebugAllHeads() []headsync.TreeHeads { - return s.headSync.DebugAllHeads() -} - -func (s *space) CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) { - if s.isClosed.Load() { - err = ErrSpaceClosed - return - } - root, err := objecttree.CreateObjectTreeRoot(payload, s.aclList) - if err != nil { - return - } - - res = treestorage.TreeStorageCreatePayload{ - RootRawChange: root, - Changes: []*treechangeproto.RawTreeChangeWithId{root}, - Heads: []string{root.Id}, - } - return -} - -func (s *space) PutTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload, listener updatelistener.UpdateListener) (t objecttree.ObjectTree, err error) { - if s.isClosed.Load() { - err = ErrSpaceClosed - return - } - deps := synctree.BuildDeps{ - SpaceId: s.id, - SyncClient: s.objectSync.SyncClient(), - Configuration: s.configuration, - HeadNotifiable: s.headSync, - Listener: listener, - AclList: s.aclList, - SpaceStorage: s.storage, - OnClose: s.onObjectClose, - SyncStatus: s.syncStatus, - PeerGetter: s.peerManager, - BuildObjectTree: s.treeBuilder, - } - t, err = synctree.PutSyncTree(ctx, payload, deps) - if err != nil { - return - } - s.treesUsed.Add(1) - log.Debug("incrementing counter", zap.String("id", payload.RootRawChange.Id), zap.Int32("trees", s.treesUsed.Load()), zap.String("spaceId", s.id)) - return -} - -type BuildTreeOpts struct { - Listener updatelistener.UpdateListener - WaitTreeRemoteSync bool - treeBuilder objecttree.BuildObjectTreeFunc -} - -type HistoryTreeOpts struct { - BeforeId string - Include bool - BuildFullTree bool -} - -func (s *space) BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t objecttree.ObjectTree, err error) { - if s.isClosed.Load() { - err = ErrSpaceClosed - return - } - treeBuilder := opts.treeBuilder - if treeBuilder == nil { - treeBuilder = s.treeBuilder - } - deps := synctree.BuildDeps{ - SpaceId: s.id, - SyncClient: s.objectSync.SyncClient(), - Configuration: s.configuration, - HeadNotifiable: s.headSync, - Listener: opts.Listener, - AclList: s.aclList, - SpaceStorage: s.storage, - OnClose: s.onObjectClose, - SyncStatus: s.syncStatus, - WaitTreeRemoteSync: opts.WaitTreeRemoteSync, - PeerGetter: s.peerManager, - BuildObjectTree: treeBuilder, - } - s.treesUsed.Add(1) - log.Debug("incrementing counter", zap.String("id", id), zap.Int32("trees", s.treesUsed.Load()), zap.String("spaceId", s.id)) - if t, err = synctree.BuildSyncTreeOrGetRemote(ctx, id, deps); err != nil { - s.treesUsed.Add(-1) - log.Debug("decrementing counter, load failed", zap.String("id", id), zap.Int32("trees", s.treesUsed.Load()), zap.String("spaceId", s.id), zap.Error(err)) - return nil, err - } - return -} - -func (s *space) BuildHistoryTree(ctx context.Context, id string, opts HistoryTreeOpts) (t objecttree.HistoryTree, err error) { - if s.isClosed.Load() { - err = ErrSpaceClosed - return - } - - params := objecttree.HistoryTreeParams{ - AclList: s.aclList, - BeforeId: opts.BeforeId, - IncludeBeforeId: opts.Include, - BuildFullTree: opts.BuildFullTree, - } - params.TreeStorage, err = s.storage.TreeStorage(id) - if err != nil { - return - } - return objecttree.BuildHistoryTree(params) -} - -func (s *space) DeleteTree(ctx context.Context, id string) (err error) { - return s.settingsObject.DeleteObject(id) -} - -func (s *space) SpaceDeleteRawChange(ctx context.Context) (raw *treechangeproto.RawTreeChangeWithId, err error) { - return s.settingsObject.SpaceDeleteRawChange() -} - -func (s *space) DeleteSpace(ctx context.Context, deleteChange *treechangeproto.RawTreeChangeWithId) (err error) { - return s.settingsObject.DeleteSpace(ctx, deleteChange) -} - -func (s *space) HandleMessage(ctx context.Context, hm HandleMessage) (err error) { - threadId := hm.Message.ObjectId - hm.ReceiveTime = time.Now() - if hm.Message.ReplyId != "" { - threadId += hm.Message.ReplyId - defer func() { - _ = s.handleQueue.CloseThread(threadId) - }() - } - if hm.PeerCtx == nil { - hm.PeerCtx = ctx - } - err = s.handleQueue.Add(ctx, threadId, hm) - if err == mb.ErrOverflowed { - log.InfoCtx(ctx, "queue overflowed", zap.String("spaceId", s.id), zap.String("objectId", threadId)) - // skip overflowed error - return nil - } - return -} - -func (s *space) handleMessage(msg HandleMessage) { - var err error - msg.StartHandlingTime = time.Now() - ctx := peer.CtxWithPeerId(context.Background(), msg.SenderId) - ctx = logger.CtxWithFields(ctx, zap.Uint64("msgId", msg.Id), zap.String("senderId", msg.SenderId)) - defer func() { - if s.metric == nil { - return - } - s.metric.RequestLog(msg.PeerCtx, "space.streamOp", msg.LogFields( - zap.Error(err), - )...) - }() - - if !msg.Deadline.IsZero() { - now := time.Now() - if now.After(msg.Deadline) { - log.InfoCtx(ctx, "skip message: deadline exceed") - err = context.DeadlineExceeded - return - } - var cancel context.CancelFunc - ctx, cancel = context.WithDeadline(ctx, msg.Deadline) - defer cancel() - } - - if err = s.objectSync.HandleMessage(ctx, msg.SenderId, msg.Message); err != nil { - if msg.Message.ObjectId != "" { - // cleanup thread on error - _ = s.handleQueue.CloseThread(msg.Message.ObjectId) - } - log.InfoCtx(ctx, "handleMessage error", zap.Error(err)) - } -} - -func (s *space) onObjectClose(id string) { - s.treesUsed.Add(-1) - log.Debug("decrementing counter", zap.String("id", id), zap.Int32("trees", s.treesUsed.Load()), zap.String("spaceId", s.id)) - _ = s.handleQueue.CloseThread(id) -} - -func (s *space) onSpaceDelete() { - err := s.storage.SetSpaceDeleted() - if err != nil { - log.Debug("failed to set space deleted") - } - s.isDeleted.Swap(true) -} - func (s *space) Close() error { - if s.isClosed.Swap(true) { - log.Warn("call space.Close on closed space", zap.String("id", s.id)) + if s.state.SpaceIsClosed.Swap(true) { + log.Warn("call space.Close on closed space", zap.String("id", s.state.SpaceId)) return nil } - log.With(zap.String("id", s.id)).Debug("space is closing") + log := log.With(zap.String("spaceId", s.state.SpaceId)) + log.Debug("space is closing") - var mError errs.Group - if err := s.handleQueue.Close(); err != nil { - mError.Add(err) - } - if err := s.headSync.Close(); err != nil { - mError.Add(err) - } - if err := s.objectSync.Close(); err != nil { - mError.Add(err) - } - if err := s.settingsObject.Close(); err != nil { - mError.Add(err) - } - if err := s.aclList.Close(); err != nil { - mError.Add(err) - } - if err := s.storage.Close(); err != nil { - mError.Add(err) - } - if err := s.syncStatus.Close(); err != nil { - mError.Add(err) - } - log.With(zap.String("id", s.id)).Debug("space closed") - return mError.Err() + err := s.app.Close(context.Background()) + log.Debug("space closed") + return err } func (s *space) TryClose(objectTTL time.Duration) (close bool, err error) { if time.Now().Sub(s.objectSync.LastUsage()) < objectTTL { return false, nil } - locked := s.treesUsed.Load() > 1 - log.With(zap.Int32("trees used", s.treesUsed.Load()), zap.Bool("locked", locked), zap.String("spaceId", s.id)).Debug("space lock status check") + locked := s.state.TreesUsed.Load() > 1 + log.With(zap.Int32("trees used", s.state.TreesUsed.Load()), zap.Bool("locked", locked), zap.String("spaceId", s.state.SpaceId)).Debug("space lock status check") if locked { return false, nil } diff --git a/commonspace/spaceservice.go b/commonspace/spaceservice.go index 0e770dc1..3397e212 100644 --- a/commonspace/spaceservice.go +++ b/commonspace/spaceservice.go @@ -2,26 +2,36 @@ package commonspace import ( "context" + "sync/atomic" + "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/commonspace/config" "github.com/anyproto/any-sync/commonspace/credentialprovider" + "github.com/anyproto/any-sync/commonspace/deletionstate" "github.com/anyproto/any-sync/commonspace/headsync" - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "github.com/anyproto/any-sync/commonspace/object/acl/syncacl" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/treemanager" + "github.com/anyproto/any-sync/commonspace/objectmanager" "github.com/anyproto/any-sync/commonspace/objectsync" + "github.com/anyproto/any-sync/commonspace/objecttreebuilder" "github.com/anyproto/any-sync/commonspace/peermanager" + "github.com/anyproto/any-sync/commonspace/requestmanager" + "github.com/anyproto/any-sync/commonspace/settings" + "github.com/anyproto/any-sync/commonspace/spacestate" "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/consensus/consensusproto" "github.com/anyproto/any-sync/metric" "github.com/anyproto/any-sync/net/peer" "github.com/anyproto/any-sync/net/pool" "github.com/anyproto/any-sync/net/rpc/rpcerr" "github.com/anyproto/any-sync/nodeconf" - "sync/atomic" + "storj.io/drpc" ) const CName = "common.commonspace" @@ -45,32 +55,30 @@ type SpaceService interface { } type spaceService struct { - config Config - account accountservice.Service - configurationService nodeconf.Service - storageProvider spacestorage.SpaceStorageProvider - peermanagerProvider peermanager.PeerManagerProvider - credentialProvider credentialprovider.CredentialProvider - treeManager treemanager.TreeManager - pool pool.Pool - metric metric.Metric + config config.Config + account accountservice.Service + configurationService nodeconf.Service + storageProvider spacestorage.SpaceStorageProvider + peerManagerProvider peermanager.PeerManagerProvider + credentialProvider credentialprovider.CredentialProvider + statusServiceProvider syncstatus.StatusServiceProvider + treeManager treemanager.TreeManager + pool pool.Pool + metric metric.Metric + app *app.App } func (s *spaceService) Init(a *app.App) (err error) { - s.config = a.MustComponent("config").(ConfigGetter).GetSpace() + s.config = a.MustComponent("config").(config.ConfigGetter).GetSpace() s.account = a.MustComponent(accountservice.CName).(accountservice.Service) s.storageProvider = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorageProvider) s.configurationService = a.MustComponent(nodeconf.CName).(nodeconf.Service) s.treeManager = a.MustComponent(treemanager.CName).(treemanager.TreeManager) - s.peermanagerProvider = a.MustComponent(peermanager.CName).(peermanager.PeerManagerProvider) - credProvider := a.Component(credentialprovider.CName) - if credProvider != nil { - s.credentialProvider = credProvider.(credentialprovider.CredentialProvider) - } else { - s.credentialProvider = credentialprovider.NewNoOp() - } + s.peerManagerProvider = a.MustComponent(peermanager.CName).(peermanager.PeerManagerProvider) + s.statusServiceProvider = a.MustComponent(syncstatus.CName).(syncstatus.StatusServiceProvider) s.pool = a.MustComponent(pool.CName).(pool.Pool) s.metric, _ = a.Component(metric.CName).(metric.Metric) + s.app = a return nil } @@ -138,8 +146,6 @@ func (s *spaceService) NewSpace(ctx context.Context, id string) (Space, error) { } } } - - lastConfiguration := s.configurationService var ( spaceIsClosed = &atomic.Bool{} spaceIsDeleted = &atomic.Bool{} @@ -149,49 +155,46 @@ func (s *spaceService) NewSpace(ctx context.Context, id string) (Space, error) { return nil, err } spaceIsDeleted.Swap(isDeleted) - getter := newCommonGetter(st.Id(), s.treeManager, spaceIsClosed) - syncStatus := syncstatus.NewNoOpSyncStatus() - // this will work only for clients, not the best solution, but... - if !lastConfiguration.IsResponsible(st.Id()) { - // TODO: move it to the client package and add possibility to inject StatusProvider from the client - syncStatus = syncstatus.NewSyncStatusProvider(st.Id(), syncstatus.DefaultDeps(lastConfiguration, st)) + state := &spacestate.SpaceState{ + SpaceId: st.Id(), + SpaceIsDeleted: spaceIsDeleted, + SpaceIsClosed: spaceIsClosed, + TreesUsed: &atomic.Int32{}, } - var builder objecttree.BuildObjectTreeFunc if s.config.KeepTreeDataInMemory { - builder = objecttree.BuildObjectTree + state.TreeBuilderFunc = objecttree.BuildObjectTree } else { - builder = objecttree.BuildEmptyDataObjectTree + state.TreeBuilderFunc = objecttree.BuildEmptyDataObjectTree } - - peerManager, err := s.peermanagerProvider.NewPeerManager(ctx, id) + peerManager, err := s.peerManagerProvider.NewPeerManager(ctx, id) if err != nil { return nil, err } + statusService := s.statusServiceProvider.NewStatusService() + spaceApp := s.app.ChildApp() + spaceApp.Register(state). + Register(peerManager). + Register(newCommonStorage(st)). + Register(statusService). + Register(syncacl.New()). + Register(requestmanager.New()). + Register(deletionstate.New()). + Register(settings.New()). + Register(objectmanager.New(s.treeManager)). + Register(objecttreebuilder.New()). + Register(objectsync.New()). + Register(headsync.New()) - headSync := headsync.NewHeadSync(id, spaceIsDeleted, s.config.SyncPeriod, lastConfiguration, st, peerManager, getter, syncStatus, s.credentialProvider, log) - objectSync := objectsync.NewObjectSync(id, spaceIsDeleted, lastConfiguration, peerManager, getter, st) sp := &space{ - id: id, - objectSync: objectSync, - headSync: headSync, - syncStatus: syncStatus, - treeManager: getter, - account: s.account, - configuration: lastConfiguration, - peerManager: peerManager, - storage: st, - treesUsed: &atomic.Int32{}, - treeBuilder: builder, - isClosed: spaceIsClosed, - isDeleted: spaceIsDeleted, - metric: s.metric, + state: state, + app: spaceApp, } return sp, nil } func (s *spaceService) addSpaceStorage(ctx context.Context, spaceDescription SpaceDescription) (st spacestorage.SpaceStorage, err error) { payload := spacestorage.SpaceStorageCreatePayload{ - AclWithId: &aclrecordproto.RawAclRecordWithId{ + AclWithId: &consensusproto.RawRecordWithId{ Payload: spaceDescription.AclPayload, Id: spaceDescription.AclId, }, @@ -226,15 +229,19 @@ func (s *spaceService) getSpaceStorageFromRemote(ctx context.Context, id string) return } - cl := spacesyncproto.NewDRPCSpaceSyncClient(p) - res, err := cl.SpacePull(ctx, &spacesyncproto.SpacePullRequest{Id: id}) + var res *spacesyncproto.SpacePullResponse + err = p.DoDrpc(ctx, func(conn drpc.Conn) error { + cl := spacesyncproto.NewDRPCSpaceSyncClient(conn) + res, err = cl.SpacePull(ctx, &spacesyncproto.SpacePullRequest{Id: id}) + return err + }) if err != nil { err = rpcerr.Unwrap(err) return } st, err = s.createSpaceStorage(spacestorage.SpaceStorageCreatePayload{ - AclWithId: &aclrecordproto.RawAclRecordWithId{ + AclWithId: &consensusproto.RawRecordWithId{ Payload: res.Payload.AclPayload, Id: res.Payload.AclPayloadId, }, diff --git a/commonspace/spacestate/shareddata.go b/commonspace/spacestate/shareddata.go new file mode 100644 index 00000000..b2a53a50 --- /dev/null +++ b/commonspace/spacestate/shareddata.go @@ -0,0 +1,25 @@ +package spacestate + +import ( + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" + "sync/atomic" +) + +const CName = "common.commonspace.spacestate" + +type SpaceState struct { + SpaceId string + SpaceIsDeleted *atomic.Bool + SpaceIsClosed *atomic.Bool + TreesUsed *atomic.Int32 + TreeBuilderFunc objecttree.BuildObjectTreeFunc +} + +func (s *SpaceState) Init(a *app.App) (err error) { + return nil +} + +func (s *SpaceState) Name() (name string) { + return CName +} diff --git a/commonspace/spacestorage/inmemorystorage.go b/commonspace/spacestorage/inmemorystorage.go index b728a8a6..0550ffbc 100644 --- a/commonspace/spacestorage/inmemorystorage.go +++ b/commonspace/spacestorage/inmemorystorage.go @@ -1,11 +1,15 @@ package spacestorage import ( - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" + "context" + + "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/commonspace/object/acl/liststorage" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/consensus/consensusproto" + "sync" ) @@ -21,8 +25,24 @@ type InMemorySpaceStorage struct { sync.Mutex } +func (i *InMemorySpaceStorage) Run(ctx context.Context) (err error) { + return nil +} + +func (i *InMemorySpaceStorage) Close(ctx context.Context) (err error) { + return nil +} + +func (i *InMemorySpaceStorage) Init(a *app.App) (err error) { + return nil +} + +func (i *InMemorySpaceStorage) Name() (name string) { + return CName +} + func NewInMemorySpaceStorage(payload SpaceStorageCreatePayload) (SpaceStorage, error) { - aclStorage, err := liststorage.NewInMemoryAclListStorage(payload.AclWithId.Id, []*aclrecordproto.RawAclRecordWithId{payload.AclWithId}) + aclStorage, err := liststorage.NewInMemoryAclListStorage(payload.AclWithId.Id, []*consensusproto.RawRecordWithId{payload.AclWithId}) if err != nil { return nil, err } @@ -148,10 +168,6 @@ func (i *InMemorySpaceStorage) ReadSpaceHash() (hash string, err error) { return i.spaceHash, nil } -func (i *InMemorySpaceStorage) Close() error { - return nil -} - func (i *InMemorySpaceStorage) AllTrees() map[string]treestorage.TreeStorage { i.Lock() defer i.Unlock() diff --git a/commonspace/spacestorage/mock_spacestorage/mock_spacestorage.go b/commonspace/spacestorage/mock_spacestorage/mock_spacestorage.go index a8410488..a81d0a93 100644 --- a/commonspace/spacestorage/mock_spacestorage/mock_spacestorage.go +++ b/commonspace/spacestorage/mock_spacestorage/mock_spacestorage.go @@ -5,13 +5,15 @@ package mock_spacestorage import ( + context "context" reflect "reflect" + app "github.com/anyproto/any-sync/app" liststorage "github.com/anyproto/any-sync/commonspace/object/acl/liststorage" treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" treestorage "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockSpaceStorage is a mock of SpaceStorage interface. @@ -53,17 +55,17 @@ func (mr *MockSpaceStorageMockRecorder) AclStorage() *gomock.Call { } // Close mocks base method. -func (m *MockSpaceStorage) Close() error { +func (m *MockSpaceStorage) Close(arg0 context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") + ret := m.ctrl.Call(m, "Close", arg0) ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. -func (mr *MockSpaceStorageMockRecorder) Close() *gomock.Call { +func (mr *MockSpaceStorageMockRecorder) Close(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSpaceStorage)(nil).Close)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSpaceStorage)(nil).Close), arg0) } // CreateTreeStorage mocks base method. @@ -110,6 +112,20 @@ func (mr *MockSpaceStorageMockRecorder) Id() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockSpaceStorage)(nil).Id)) } +// Init mocks base method. +func (m *MockSpaceStorage) Init(arg0 *app.App) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockSpaceStorageMockRecorder) Init(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockSpaceStorage)(nil).Init), arg0) +} + // IsSpaceDeleted mocks base method. func (m *MockSpaceStorage) IsSpaceDeleted() (bool, error) { m.ctrl.T.Helper() @@ -125,6 +141,20 @@ func (mr *MockSpaceStorageMockRecorder) IsSpaceDeleted() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSpaceDeleted", reflect.TypeOf((*MockSpaceStorage)(nil).IsSpaceDeleted)) } +// Name mocks base method. +func (m *MockSpaceStorage) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockSpaceStorageMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockSpaceStorage)(nil).Name)) +} + // ReadSpaceHash mocks base method. func (m *MockSpaceStorage) ReadSpaceHash() (string, error) { m.ctrl.T.Helper() @@ -140,6 +170,20 @@ func (mr *MockSpaceStorageMockRecorder) ReadSpaceHash() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadSpaceHash", reflect.TypeOf((*MockSpaceStorage)(nil).ReadSpaceHash)) } +// Run mocks base method. +func (m *MockSpaceStorage) Run(arg0 context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Run", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Run indicates an expected call of Run. +func (mr *MockSpaceStorageMockRecorder) Run(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockSpaceStorage)(nil).Run), arg0) +} + // SetSpaceDeleted mocks base method. func (m *MockSpaceStorage) SetSpaceDeleted() error { m.ctrl.T.Helper() diff --git a/commonspace/spacestorage/spacestorage.go b/commonspace/spacestorage/spacestorage.go index cecd3736..a0976f3e 100644 --- a/commonspace/spacestorage/spacestorage.go +++ b/commonspace/spacestorage/spacestorage.go @@ -4,12 +4,13 @@ package spacestorage import ( "context" "errors" + "github.com/anyproto/any-sync/app" - "github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto" "github.com/anyproto/any-sync/commonspace/object/acl/liststorage" "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/spacesyncproto" + "github.com/anyproto/any-sync/consensus/consensusproto" ) const CName = "common.commonspace.spacestorage" @@ -27,8 +28,8 @@ const ( TreeDeletedStatusDeleted = "deleted" ) -// TODO: consider moving to some file with all common interfaces etc type SpaceStorage interface { + app.ComponentRunnable Id() string SetSpaceDeleted() error IsSpaceDeleted() (bool, error) @@ -44,12 +45,10 @@ type SpaceStorage interface { CreateTreeStorage(payload treestorage.TreeStorageCreatePayload) (treestorage.TreeStorage, error) WriteSpaceHash(hash string) error ReadSpaceHash() (hash string, err error) - - Close() error } type SpaceStorageCreatePayload struct { - AclWithId *aclrecordproto.RawAclRecordWithId + AclWithId *consensusproto.RawRecordWithId SpaceHeaderWithId *spacesyncproto.RawSpaceHeaderWithId SpaceSettingsWithId *treechangeproto.RawTreeChangeWithId } diff --git a/commonspace/spacestorage/spacestorage_test.go b/commonspace/spacestorage/spacestorage_test.go deleted file mode 100644 index 7ca39ae0..00000000 --- a/commonspace/spacestorage/spacestorage_test.go +++ /dev/null @@ -1 +0,0 @@ -package spacestorage diff --git a/commonspace/spacesyncproto/mock_spacesyncproto/mock_spacesyncproto.go b/commonspace/spacesyncproto/mock_spacesyncproto/mock_spacesyncproto.go index db8313a2..a04f5e11 100644 --- a/commonspace/spacesyncproto/mock_spacesyncproto/mock_spacesyncproto.go +++ b/commonspace/spacesyncproto/mock_spacesyncproto/mock_spacesyncproto.go @@ -9,7 +9,7 @@ import ( reflect "reflect" spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" drpc "storj.io/drpc" ) @@ -65,6 +65,21 @@ func (mr *MockDRPCSpaceSyncClientMockRecorder) HeadSync(arg0, arg1 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeadSync", reflect.TypeOf((*MockDRPCSpaceSyncClient)(nil).HeadSync), arg0, arg1) } +// ObjectSync mocks base method. +func (m *MockDRPCSpaceSyncClient) ObjectSync(arg0 context.Context, arg1 *spacesyncproto.ObjectSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ObjectSync", arg0, arg1) + ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ObjectSync indicates an expected call of ObjectSync. +func (mr *MockDRPCSpaceSyncClientMockRecorder) ObjectSync(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ObjectSync", reflect.TypeOf((*MockDRPCSpaceSyncClient)(nil).ObjectSync), arg0, arg1) +} + // ObjectSyncStream mocks base method. func (m *MockDRPCSpaceSyncClient) ObjectSyncStream(arg0 context.Context) (spacesyncproto.DRPCSpaceSync_ObjectSyncStreamClient, error) { m.ctrl.T.Helper() diff --git a/commonspace/spacesyncproto/protos/spacesync.proto b/commonspace/spacesyncproto/protos/spacesync.proto index d5b461cf..f7f44ae4 100644 --- a/commonspace/spacesyncproto/protos/spacesync.proto +++ b/commonspace/spacesyncproto/protos/spacesync.proto @@ -23,6 +23,8 @@ service SpaceSync { rpc SpacePull(SpacePullRequest) returns (SpacePullResponse); // ObjectSyncStream opens object sync stream with node or client rpc ObjectSyncStream(stream ObjectSyncMessage) returns (stream ObjectSyncMessage); + // ObjectSync sends object sync message and synchronously gets response message + rpc ObjectSync(ObjectSyncMessage) returns (ObjectSyncMessage); } // HeadSyncRange presenting a request for one range diff --git a/commonspace/spacesyncproto/spacesync.go b/commonspace/spacesyncproto/spacesync.go index 39f5a25a..417636b2 100644 --- a/commonspace/spacesyncproto/spacesync.go +++ b/commonspace/spacesyncproto/spacesync.go @@ -2,6 +2,7 @@ package spacesyncproto import ( + "github.com/gogo/protobuf/proto" "storj.io/drpc" ) @@ -16,3 +17,16 @@ func (c ClientFactoryFunc) Client(cc drpc.Conn) DRPCSpaceSyncClient { type ClientFactory interface { Client(cc drpc.Conn) DRPCSpaceSyncClient } + +func MarshallSyncMessage(message proto.Marshaler, spaceId, objectId string) (objMsg *ObjectSyncMessage, err error) { + payload, err := message.Marshal() + if err != nil { + return + } + objMsg = &ObjectSyncMessage{ + Payload: payload, + ObjectId: objectId, + SpaceId: spaceId, + } + return +} diff --git a/commonspace/spacesyncproto/spacesync.pb.go b/commonspace/spacesyncproto/spacesync.pb.go index 33caed5b..80571bf6 100644 --- a/commonspace/spacesyncproto/spacesync.pb.go +++ b/commonspace/spacesyncproto/spacesync.pb.go @@ -1254,75 +1254,75 @@ func init() { } var fileDescriptor_80e49f1f4ac27799 = []byte{ - // 1077 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xcd, 0x6e, 0xdb, 0x46, - 0x10, 0x16, 0xe9, 0x5f, 0x8d, 0x65, 0x99, 0xd9, 0x28, 0x89, 0xaa, 0x18, 0x8a, 0xb0, 0x28, 0x0a, - 0x23, 0x07, 0x27, 0xb1, 0x8b, 0x02, 0x49, 0xdb, 0x43, 0x62, 0x3b, 0x0d, 0x51, 0x24, 0x36, 0x56, + // 1083 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0x16, 0xe9, 0x5f, 0x8d, 0x65, 0x85, 0xd9, 0x28, 0x89, 0xaa, 0x18, 0x8a, 0xb0, 0x28, 0x0a, + 0x23, 0x07, 0x27, 0xb1, 0x8b, 0x02, 0x49, 0xdb, 0x43, 0x62, 0x3b, 0x35, 0x51, 0x24, 0x36, 0x56, 0x0d, 0x0a, 0x14, 0xc8, 0x61, 0x4d, 0x8e, 0x2d, 0xb6, 0x14, 0xc9, 0x72, 0x57, 0x89, 0x75, 0xec, - 0xa9, 0xd7, 0x9e, 0xdb, 0x07, 0xe8, 0x0b, 0xf4, 0x21, 0x7a, 0x4c, 0x6f, 0x3d, 0x16, 0xf6, 0x8b, - 0x14, 0xbb, 0x5c, 0xfe, 0xc8, 0xa2, 0x02, 0xe4, 0x22, 0xed, 0x7e, 0x33, 0xf3, 0xcd, 0xdf, 0xee, - 0x0e, 0xe1, 0x91, 0x17, 0x8f, 0xc7, 0x71, 0x24, 0x12, 0xee, 0xe1, 0x03, 0xfd, 0x2b, 0xa6, 0x91, - 0x97, 0xa4, 0xb1, 0x8c, 0x1f, 0xe8, 0x5f, 0x51, 0xa2, 0xbb, 0x1a, 0x20, 0xcd, 0x02, 0xa0, 0x2e, - 0x6c, 0xbe, 0x40, 0xee, 0x0f, 0xa7, 0x91, 0xc7, 0x78, 0x74, 0x8e, 0x84, 0xc0, 0xf2, 0x59, 0x1a, - 0x8f, 0xbb, 0xd6, 0xc0, 0xda, 0x59, 0x66, 0x7a, 0x4d, 0xda, 0x60, 0xcb, 0xb8, 0x6b, 0x6b, 0xc4, - 0x96, 0x31, 0xe9, 0xc0, 0x4a, 0x18, 0x8c, 0x03, 0xd9, 0x5d, 0x1a, 0x58, 0x3b, 0x9b, 0x2c, 0xdb, - 0xd0, 0x0b, 0x68, 0x17, 0x54, 0x28, 0x26, 0xa1, 0x54, 0x5c, 0x23, 0x2e, 0x46, 0x9a, 0xab, 0xc5, - 0xf4, 0x9a, 0x7c, 0x05, 0xeb, 0x18, 0xe2, 0x18, 0x23, 0x29, 0xba, 0xf6, 0x60, 0x69, 0x67, 0x63, - 0x6f, 0xb0, 0x5b, 0xc6, 0x37, 0x4b, 0x70, 0x94, 0x29, 0xb2, 0xc2, 0x42, 0x79, 0xf6, 0xe2, 0x49, - 0x54, 0x78, 0xd6, 0x1b, 0xfa, 0x25, 0xdc, 0xaa, 0x35, 0x54, 0x81, 0x07, 0xbe, 0x76, 0xdf, 0x64, - 0x76, 0xe0, 0xeb, 0x80, 0x90, 0xfb, 0x3a, 0x95, 0x26, 0xd3, 0x6b, 0xfa, 0x06, 0xb6, 0x4a, 0xe3, - 0x9f, 0x27, 0x28, 0x24, 0xe9, 0xc2, 0x9a, 0x0e, 0xc9, 0xcd, 0x6d, 0xf3, 0x2d, 0x79, 0x08, 0xab, - 0xa9, 0x2a, 0x53, 0x1e, 0x7b, 0xb7, 0x2e, 0x76, 0xa5, 0xc0, 0x8c, 0x1e, 0xfd, 0x06, 0x9c, 0x4a, - 0x6c, 0x49, 0x1c, 0x09, 0x24, 0xfb, 0xb0, 0x96, 0xea, 0x38, 0x45, 0xd7, 0xd2, 0x34, 0x9f, 0x2c, - 0x2c, 0x01, 0xcb, 0x35, 0xe9, 0x1f, 0x16, 0xdc, 0x38, 0x3e, 0xfd, 0x11, 0x3d, 0xa9, 0xa4, 0x2f, - 0x51, 0x08, 0x7e, 0x8e, 0x1f, 0x08, 0x75, 0x1b, 0x9a, 0x69, 0x96, 0x8f, 0x9b, 0x27, 0x5c, 0x02, - 0xca, 0x2e, 0xc5, 0x24, 0x9c, 0xba, 0xbe, 0x2e, 0x65, 0x93, 0xe5, 0x5b, 0x25, 0x49, 0xf8, 0x34, - 0x8c, 0xb9, 0xdf, 0x5d, 0xd6, 0x7d, 0xcb, 0xb7, 0xa4, 0x07, 0xeb, 0xb1, 0x0e, 0xc0, 0xf5, 0xbb, - 0x2b, 0xda, 0xa8, 0xd8, 0x53, 0x04, 0x67, 0xa8, 0x1c, 0x9f, 0x4c, 0xc4, 0x28, 0x2f, 0xe3, 0xa3, - 0x92, 0x49, 0xc5, 0xb6, 0xb1, 0x77, 0xa7, 0x92, 0x66, 0xa6, 0x9d, 0x89, 0x4b, 0x17, 0x7d, 0x80, - 0x83, 0x14, 0x7d, 0x8c, 0x64, 0xc0, 0x43, 0x1d, 0x75, 0x8b, 0x55, 0x10, 0x7a, 0x13, 0x6e, 0x54, - 0xdc, 0x64, 0xe5, 0xa4, 0xb4, 0xf0, 0x1d, 0x86, 0xb9, 0xef, 0x6b, 0x9d, 0xa7, 0xcf, 0x0b, 0x43, - 0xa5, 0x63, 0xfa, 0xf0, 0xf1, 0x01, 0xd2, 0x5f, 0x6c, 0x68, 0x55, 0x25, 0xe4, 0x29, 0x6c, 0x68, - 0x1b, 0xd5, 0x36, 0x4c, 0x0d, 0xcf, 0xbd, 0x0a, 0x0f, 0xe3, 0xef, 0x86, 0xa5, 0xc2, 0xf7, 0x81, - 0x1c, 0xb9, 0x3e, 0xab, 0xda, 0xa8, 0xa4, 0xb9, 0x17, 0x1a, 0xc2, 0x3c, 0xe9, 0x12, 0x21, 0x14, - 0x5a, 0xe5, 0xae, 0x68, 0xd8, 0x0c, 0x46, 0xf6, 0xa0, 0xa3, 0x29, 0x87, 0x28, 0x65, 0x10, 0x9d, - 0x8b, 0x93, 0x99, 0x16, 0xd6, 0xca, 0xc8, 0x17, 0x70, 0xbb, 0x0e, 0x2f, 0xba, 0xbb, 0x40, 0x4a, - 0xff, 0xb1, 0x60, 0xa3, 0x92, 0x92, 0x3a, 0x17, 0x81, 0x6e, 0x90, 0x9c, 0x9a, 0xab, 0x5e, 0xec, - 0xd5, 0x29, 0x94, 0xc1, 0x18, 0x85, 0xe4, 0xe3, 0x44, 0xa7, 0xb6, 0xc4, 0x4a, 0x40, 0x49, 0xb5, - 0x8f, 0xef, 0xa6, 0x09, 0x9a, 0xb4, 0x4a, 0x80, 0x7c, 0x06, 0x6d, 0x75, 0x28, 0x03, 0x8f, 0xcb, - 0x20, 0x8e, 0xbe, 0xc5, 0xa9, 0xce, 0x66, 0x99, 0x5d, 0x43, 0xd5, 0xad, 0x16, 0x88, 0x59, 0xd4, - 0x2d, 0xa6, 0xd7, 0x64, 0x17, 0x48, 0xa5, 0xc4, 0x79, 0x35, 0x56, 0xb5, 0x46, 0x8d, 0x84, 0x9e, - 0x40, 0x7b, 0xb6, 0x51, 0x64, 0x30, 0xdf, 0xd8, 0xd6, 0x6c, 0xdf, 0x54, 0xf4, 0xc1, 0x79, 0xc4, - 0xe5, 0x24, 0x45, 0xd3, 0xb6, 0x12, 0xa0, 0x87, 0xd0, 0xa9, 0x6b, 0xbd, 0xbe, 0x97, 0xfc, 0xdd, - 0x0c, 0x6b, 0x09, 0x98, 0x73, 0x6b, 0x17, 0xe7, 0xf6, 0x77, 0x0b, 0x3a, 0xc3, 0x6a, 0x1b, 0x0e, - 0xe2, 0x48, 0xaa, 0xa7, 0xed, 0x6b, 0x68, 0x65, 0x97, 0xef, 0x10, 0x43, 0x94, 0x58, 0x73, 0x80, - 0x8f, 0x2b, 0xe2, 0x17, 0x0d, 0x36, 0xa3, 0x4e, 0x9e, 0x98, 0xec, 0x8c, 0xb5, 0xad, 0xad, 0x6f, - 0x5f, 0x3f, 0xfe, 0x85, 0x71, 0x55, 0xf9, 0xd9, 0x1a, 0xac, 0xbc, 0xe5, 0xe1, 0x04, 0x69, 0x1f, - 0x5a, 0x55, 0x27, 0x73, 0x97, 0x6e, 0xdf, 0x9c, 0x13, 0x23, 0xfe, 0x14, 0x36, 0x7d, 0xbd, 0x4a, - 0x4f, 0x10, 0xd3, 0xe2, 0xc5, 0x9a, 0x05, 0xe9, 0x1b, 0xb8, 0x35, 0x93, 0xf0, 0x30, 0xe2, 0x89, - 0x18, 0xc5, 0x52, 0x5d, 0x93, 0x4c, 0xd3, 0x77, 0xfd, 0xec, 0xe1, 0x6c, 0xb2, 0x0a, 0x32, 0x4f, - 0x6f, 0xd7, 0xd1, 0xff, 0x6a, 0x41, 0x2b, 0xa7, 0x3e, 0xe4, 0x92, 0x93, 0xc7, 0xb0, 0xe6, 0x65, - 0x35, 0x35, 0x8f, 0xf1, 0xbd, 0xeb, 0x55, 0xb8, 0x56, 0x7a, 0x96, 0xeb, 0xab, 0x59, 0x26, 0x4c, - 0x74, 0xa6, 0x82, 0x83, 0x45, 0xb6, 0x79, 0x16, 0xac, 0xb0, 0xa0, 0x3f, 0x99, 0x27, 0x69, 0x38, - 0x39, 0x15, 0x5e, 0x1a, 0x24, 0xea, 0x38, 0xab, 0xbb, 0x64, 0x1e, 0xf0, 0x3c, 0xc5, 0x62, 0x4f, - 0x9e, 0xc0, 0x2a, 0xf7, 0x94, 0x96, 0x76, 0xd6, 0xde, 0xa3, 0x73, 0xce, 0x2a, 0x4c, 0x4f, 0xb5, - 0x26, 0x33, 0x16, 0xf7, 0xff, 0xb4, 0x60, 0xfd, 0x28, 0x4d, 0x0f, 0x62, 0x1f, 0x05, 0x69, 0x03, - 0xbc, 0x8e, 0xf0, 0x22, 0x41, 0x4f, 0xa2, 0xef, 0x34, 0x88, 0x63, 0xde, 0xb4, 0x97, 0x81, 0x10, - 0x41, 0x74, 0xee, 0x58, 0x64, 0xcb, 0x74, 0xee, 0xe8, 0x22, 0x10, 0x52, 0x38, 0x36, 0xb9, 0x09, - 0x5b, 0x1a, 0x78, 0x15, 0x4b, 0x37, 0x3a, 0xe0, 0xde, 0x08, 0x9d, 0x25, 0x42, 0xa0, 0xad, 0x41, - 0x57, 0x64, 0x1d, 0xf6, 0x9d, 0x65, 0xd2, 0x85, 0x8e, 0xae, 0xb4, 0x78, 0x15, 0x4b, 0xf3, 0xd0, - 0x06, 0xa7, 0x21, 0x3a, 0x2b, 0xa4, 0x03, 0x0e, 0x43, 0x0f, 0x83, 0x44, 0xba, 0xc2, 0x8d, 0xde, - 0xf2, 0x30, 0xf0, 0x9d, 0x55, 0xe5, 0xe9, 0x28, 0x4d, 0xe3, 0xf4, 0xf8, 0xec, 0x4c, 0xa0, 0x74, - 0xfc, 0xfb, 0x8f, 0xe1, 0xce, 0x82, 0x64, 0xc8, 0x26, 0x34, 0x0d, 0x7a, 0x8a, 0x4e, 0x43, 0x99, - 0xbe, 0x8e, 0x44, 0x01, 0x58, 0x7b, 0x7f, 0xd9, 0xd0, 0xcc, 0x6c, 0xa7, 0x91, 0x47, 0x0e, 0x60, - 0x3d, 0x9f, 0xa5, 0xa4, 0x57, 0x3b, 0x60, 0xf5, 0xa8, 0xe8, 0xdd, 0xad, 0x1f, 0xbe, 0xd9, 0x88, - 0x78, 0x6e, 0x18, 0xd5, 0xc0, 0x21, 0x77, 0xe7, 0xc6, 0x43, 0x39, 0xed, 0x7a, 0xdb, 0xf5, 0xc2, - 0x39, 0x9e, 0x30, 0xac, 0xe3, 0x29, 0x26, 0x57, 0x1d, 0x4f, 0x65, 0x64, 0x31, 0x70, 0xca, 0x8f, - 0x80, 0xa1, 0x4c, 0x91, 0x8f, 0xc9, 0xf6, 0xdc, 0xa5, 0xaf, 0x7c, 0x21, 0xf4, 0x3e, 0x28, 0xdd, - 0xb1, 0x1e, 0x5a, 0xcf, 0x3e, 0xff, 0xfb, 0xb2, 0x6f, 0xbd, 0xbf, 0xec, 0x5b, 0xff, 0x5d, 0xf6, - 0xad, 0xdf, 0xae, 0xfa, 0x8d, 0xf7, 0x57, 0xfd, 0xc6, 0xbf, 0x57, 0xfd, 0xc6, 0x0f, 0xbd, 0xc5, - 0xdf, 0x96, 0xa7, 0xab, 0xfa, 0x6f, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x01, 0xff, - 0xb5, 0x80, 0x0a, 0x00, 0x00, + 0xa9, 0xd7, 0x9e, 0xdb, 0x07, 0xe8, 0xab, 0xf4, 0x98, 0xde, 0x7a, 0x2c, 0xec, 0xf7, 0x28, 0x8a, + 0x5d, 0x2e, 0x7f, 0x64, 0x51, 0x01, 0x8a, 0x5e, 0xa4, 0xdd, 0x6f, 0x66, 0xbe, 0xf9, 0xdb, 0xdd, + 0x21, 0x3c, 0xf6, 0xe2, 0xf1, 0x38, 0x8e, 0x44, 0xc2, 0x3d, 0x7c, 0xa8, 0x7f, 0xc5, 0x34, 0xf2, + 0x92, 0x34, 0x96, 0xf1, 0x43, 0xfd, 0x2b, 0x4a, 0x74, 0x47, 0x03, 0xa4, 0x59, 0x00, 0xd4, 0x85, + 0xcd, 0x23, 0xe4, 0xfe, 0x70, 0x1a, 0x79, 0x8c, 0x47, 0xe7, 0x48, 0x08, 0x2c, 0x9f, 0xa5, 0xf1, + 0xb8, 0x6b, 0x0d, 0xac, 0xed, 0x65, 0xa6, 0xd7, 0xa4, 0x0d, 0xb6, 0x8c, 0xbb, 0xb6, 0x46, 0x6c, + 0x19, 0x93, 0x0e, 0xac, 0x84, 0xc1, 0x38, 0x90, 0xdd, 0xa5, 0x81, 0xb5, 0xbd, 0xc9, 0xb2, 0x0d, + 0xbd, 0x80, 0x76, 0x41, 0x85, 0x62, 0x12, 0x4a, 0xc5, 0x35, 0xe2, 0x62, 0xa4, 0xb9, 0x5a, 0x4c, + 0xaf, 0xc9, 0x17, 0xb0, 0x8e, 0x21, 0x8e, 0x31, 0x92, 0xa2, 0x6b, 0x0f, 0x96, 0xb6, 0x37, 0x76, + 0x07, 0x3b, 0x65, 0x7c, 0xb3, 0x04, 0x87, 0x99, 0x22, 0x2b, 0x2c, 0x94, 0x67, 0x2f, 0x9e, 0x44, + 0x85, 0x67, 0xbd, 0xa1, 0x9f, 0xc3, 0xed, 0x5a, 0x43, 0x15, 0x78, 0xe0, 0x6b, 0xf7, 0x4d, 0x66, + 0x07, 0xbe, 0x0e, 0x08, 0xb9, 0xaf, 0x53, 0x69, 0x32, 0xbd, 0xa6, 0x6f, 0xe0, 0x46, 0x69, 0xfc, + 0xe3, 0x04, 0x85, 0x24, 0x5d, 0x58, 0xd3, 0x21, 0xb9, 0xb9, 0x6d, 0xbe, 0x25, 0x8f, 0x60, 0x35, + 0x55, 0x65, 0xca, 0x63, 0xef, 0xd6, 0xc5, 0xae, 0x14, 0x98, 0xd1, 0xa3, 0x5f, 0x81, 0x53, 0x89, + 0x2d, 0x89, 0x23, 0x81, 0x64, 0x0f, 0xd6, 0x52, 0x1d, 0xa7, 0xe8, 0x5a, 0x9a, 0xe6, 0xa3, 0x85, + 0x25, 0x60, 0xb9, 0x26, 0xfd, 0xcd, 0x82, 0x9b, 0xc7, 0xa7, 0xdf, 0xa3, 0x27, 0x95, 0xf4, 0x25, + 0x0a, 0xc1, 0xcf, 0xf1, 0x03, 0xa1, 0x6e, 0x41, 0x33, 0xcd, 0xf2, 0x71, 0xf3, 0x84, 0x4b, 0x40, + 0xd9, 0xa5, 0x98, 0x84, 0x53, 0xd7, 0xd7, 0xa5, 0x6c, 0xb2, 0x7c, 0xab, 0x24, 0x09, 0x9f, 0x86, + 0x31, 0xf7, 0xbb, 0xcb, 0xba, 0x6f, 0xf9, 0x96, 0xf4, 0x60, 0x3d, 0xd6, 0x01, 0xb8, 0x7e, 0x77, + 0x45, 0x1b, 0x15, 0x7b, 0x8a, 0xe0, 0x0c, 0x95, 0xe3, 0x93, 0x89, 0x18, 0xe5, 0x65, 0x7c, 0x5c, + 0x32, 0xa9, 0xd8, 0x36, 0x76, 0xef, 0x56, 0xd2, 0xcc, 0xb4, 0x33, 0x71, 0xe9, 0xa2, 0x0f, 0xb0, + 0x9f, 0xa2, 0x8f, 0x91, 0x0c, 0x78, 0xa8, 0xa3, 0x6e, 0xb1, 0x0a, 0x42, 0x6f, 0xc1, 0xcd, 0x8a, + 0x9b, 0xac, 0x9c, 0x94, 0x16, 0xbe, 0xc3, 0x30, 0xf7, 0x7d, 0xad, 0xf3, 0xf4, 0x45, 0x61, 0xa8, + 0x74, 0x4c, 0x1f, 0xfe, 0x7b, 0x80, 0xf4, 0x27, 0x1b, 0x5a, 0x55, 0x09, 0x79, 0x06, 0x1b, 0xda, + 0x46, 0xb5, 0x0d, 0x53, 0xc3, 0x73, 0xbf, 0xc2, 0xc3, 0xf8, 0xbb, 0x61, 0xa9, 0xf0, 0x6d, 0x20, + 0x47, 0xae, 0xcf, 0xaa, 0x36, 0x2a, 0x69, 0xee, 0x85, 0x86, 0x30, 0x4f, 0xba, 0x44, 0x08, 0x85, + 0x56, 0xb9, 0x2b, 0x1a, 0x36, 0x83, 0x91, 0x5d, 0xe8, 0x68, 0xca, 0x21, 0x4a, 0x19, 0x44, 0xe7, + 0xe2, 0x64, 0xa6, 0x85, 0xb5, 0x32, 0xf2, 0x19, 0xdc, 0xa9, 0xc3, 0x8b, 0xee, 0x2e, 0x90, 0xd2, + 0x3f, 0x2d, 0xd8, 0xa8, 0xa4, 0xa4, 0xce, 0x45, 0xa0, 0x1b, 0x24, 0xa7, 0xe6, 0xaa, 0x17, 0x7b, + 0x75, 0x0a, 0x65, 0x30, 0x46, 0x21, 0xf9, 0x38, 0xd1, 0xa9, 0x2d, 0xb1, 0x12, 0x50, 0x52, 0xed, + 0xe3, 0x9b, 0x69, 0x82, 0x26, 0xad, 0x12, 0x20, 0x9f, 0x40, 0x5b, 0x1d, 0xca, 0xc0, 0xe3, 0x32, + 0x88, 0xa3, 0xaf, 0x71, 0xaa, 0xb3, 0x59, 0x66, 0xd7, 0x50, 0x75, 0xab, 0x05, 0x62, 0x16, 0x75, + 0x8b, 0xe9, 0x35, 0xd9, 0x01, 0x52, 0x29, 0x71, 0x5e, 0x8d, 0x55, 0xad, 0x51, 0x23, 0xa1, 0x27, + 0xd0, 0x9e, 0x6d, 0x14, 0x19, 0xcc, 0x37, 0xb6, 0x35, 0xdb, 0x37, 0x15, 0x7d, 0x70, 0x1e, 0x71, + 0x39, 0x49, 0xd1, 0xb4, 0xad, 0x04, 0xe8, 0x01, 0x74, 0xea, 0x5a, 0xaf, 0xef, 0x25, 0x7f, 0x37, + 0xc3, 0x5a, 0x02, 0xe6, 0xdc, 0xda, 0xc5, 0xb9, 0xfd, 0xd5, 0x82, 0xce, 0xb0, 0xda, 0x86, 0xfd, + 0x38, 0x92, 0xea, 0x69, 0xfb, 0x12, 0x5a, 0xd9, 0xe5, 0x3b, 0xc0, 0x10, 0x25, 0xd6, 0x1c, 0xe0, + 0xe3, 0x8a, 0xf8, 0xa8, 0xc1, 0x66, 0xd4, 0xc9, 0x53, 0x93, 0x9d, 0xb1, 0xb6, 0xb5, 0xf5, 0x9d, + 0xeb, 0xc7, 0xbf, 0x30, 0xae, 0x2a, 0x3f, 0x5f, 0x83, 0x95, 0xb7, 0x3c, 0x9c, 0x20, 0xed, 0x43, + 0xab, 0xea, 0x64, 0xee, 0xd2, 0xed, 0x99, 0x73, 0x62, 0xc4, 0x1f, 0xc3, 0xa6, 0xaf, 0x57, 0xe9, + 0x09, 0x62, 0x5a, 0xbc, 0x58, 0xb3, 0x20, 0x7d, 0x03, 0xb7, 0x67, 0x12, 0x1e, 0x46, 0x3c, 0x11, + 0xa3, 0x58, 0xaa, 0x6b, 0x92, 0x69, 0xfa, 0xae, 0x9f, 0x3d, 0x9c, 0x4d, 0x56, 0x41, 0xe6, 0xe9, + 0xed, 0x3a, 0xfa, 0x9f, 0x2d, 0x68, 0xe5, 0xd4, 0x07, 0x5c, 0x72, 0xf2, 0x04, 0xd6, 0xbc, 0xac, + 0xa6, 0xe6, 0x31, 0xbe, 0x7f, 0xbd, 0x0a, 0xd7, 0x4a, 0xcf, 0x72, 0x7d, 0x35, 0xcb, 0x84, 0x89, + 0xce, 0x54, 0x70, 0xb0, 0xc8, 0x36, 0xcf, 0x82, 0x15, 0x16, 0xf4, 0x07, 0xf3, 0x24, 0x0d, 0x27, + 0xa7, 0xc2, 0x4b, 0x83, 0x44, 0x1d, 0x67, 0x75, 0x97, 0xcc, 0x03, 0x9e, 0xa7, 0x58, 0xec, 0xc9, + 0x53, 0x58, 0xe5, 0x9e, 0xd2, 0xd2, 0xce, 0xda, 0xbb, 0x74, 0xce, 0x59, 0x85, 0xe9, 0x99, 0xd6, + 0x64, 0xc6, 0xe2, 0xc1, 0xef, 0x16, 0xac, 0x1f, 0xa6, 0xe9, 0x7e, 0xec, 0xa3, 0x20, 0x6d, 0x80, + 0xd7, 0x11, 0x5e, 0x24, 0xe8, 0x49, 0xf4, 0x9d, 0x06, 0x71, 0xcc, 0x9b, 0xf6, 0x32, 0x10, 0x22, + 0x88, 0xce, 0x1d, 0x8b, 0xdc, 0x30, 0x9d, 0x3b, 0xbc, 0x08, 0x84, 0x14, 0x8e, 0x4d, 0x6e, 0xc1, + 0x0d, 0x0d, 0xbc, 0x8a, 0xa5, 0x1b, 0xed, 0x73, 0x6f, 0x84, 0xce, 0x12, 0x21, 0xd0, 0xd6, 0xa0, + 0x2b, 0xb2, 0x0e, 0xfb, 0xce, 0x32, 0xe9, 0x42, 0x47, 0x57, 0x5a, 0xbc, 0x8a, 0xa5, 0x79, 0x68, + 0x83, 0xd3, 0x10, 0x9d, 0x15, 0xd2, 0x01, 0x87, 0xa1, 0x87, 0x41, 0x22, 0x5d, 0xe1, 0x46, 0x6f, + 0x79, 0x18, 0xf8, 0xce, 0xaa, 0xf2, 0x74, 0x98, 0xa6, 0x71, 0x7a, 0x7c, 0x76, 0x26, 0x50, 0x3a, + 0xfe, 0x83, 0x27, 0x70, 0x77, 0x41, 0x32, 0x64, 0x13, 0x9a, 0x06, 0x3d, 0x45, 0xa7, 0xa1, 0x4c, + 0x5f, 0x47, 0xa2, 0x00, 0xac, 0xdd, 0x7f, 0x6c, 0x68, 0x66, 0xb6, 0xd3, 0xc8, 0x23, 0xfb, 0xb0, + 0x9e, 0xcf, 0x52, 0xd2, 0xab, 0x1d, 0xb0, 0x7a, 0x54, 0xf4, 0xee, 0xd5, 0x0f, 0xdf, 0x6c, 0x44, + 0xbc, 0x30, 0x8c, 0x6a, 0xe0, 0x90, 0x7b, 0x73, 0xe3, 0xa1, 0x9c, 0x76, 0xbd, 0xad, 0x7a, 0xe1, + 0x1c, 0x4f, 0x18, 0xd6, 0xf1, 0x14, 0x93, 0xab, 0x8e, 0xa7, 0x32, 0xb2, 0x18, 0x38, 0xe5, 0x47, + 0xc0, 0x50, 0xa6, 0xc8, 0xc7, 0x64, 0x6b, 0xee, 0xd2, 0x57, 0xbe, 0x10, 0x7a, 0x1f, 0x94, 0x6e, + 0x5b, 0x8f, 0x2c, 0x72, 0x04, 0x50, 0x0a, 0xfe, 0x0f, 0xdb, 0xf3, 0x4f, 0xff, 0xb8, 0xec, 0x5b, + 0xef, 0x2f, 0xfb, 0xd6, 0xdf, 0x97, 0x7d, 0xeb, 0x97, 0xab, 0x7e, 0xe3, 0xfd, 0x55, 0xbf, 0xf1, + 0xd7, 0x55, 0xbf, 0xf1, 0x5d, 0x6f, 0xf1, 0x57, 0xea, 0xe9, 0xaa, 0xfe, 0xdb, 0xfb, 0x37, 0x00, + 0x00, 0xff, 0xff, 0xb6, 0xe1, 0x84, 0x46, 0xca, 0x0a, 0x00, 0x00, } func (m *HeadSyncRange) Marshal() (dAtA []byte, err error) { diff --git a/commonspace/spacesyncproto/spacesync_drpc.pb.go b/commonspace/spacesyncproto/spacesync_drpc.pb.go index 55b3fed4..11e5d715 100644 --- a/commonspace/spacesyncproto/spacesync_drpc.pb.go +++ b/commonspace/spacesyncproto/spacesync_drpc.pb.go @@ -44,6 +44,7 @@ type DRPCSpaceSyncClient interface { SpacePush(ctx context.Context, in *SpacePushRequest) (*SpacePushResponse, error) SpacePull(ctx context.Context, in *SpacePullRequest) (*SpacePullResponse, error) ObjectSyncStream(ctx context.Context) (DRPCSpaceSync_ObjectSyncStreamClient, error) + ObjectSync(ctx context.Context, in *ObjectSyncMessage) (*ObjectSyncMessage, error) } type drpcSpaceSyncClient struct { @@ -122,11 +123,21 @@ func (x *drpcSpaceSync_ObjectSyncStreamClient) RecvMsg(m *ObjectSyncMessage) err return x.MsgRecv(m, drpcEncoding_File_commonspace_spacesyncproto_protos_spacesync_proto{}) } +func (c *drpcSpaceSyncClient) ObjectSync(ctx context.Context, in *ObjectSyncMessage) (*ObjectSyncMessage, error) { + out := new(ObjectSyncMessage) + err := c.cc.Invoke(ctx, "/spacesync.SpaceSync/ObjectSync", drpcEncoding_File_commonspace_spacesyncproto_protos_spacesync_proto{}, in, out) + if err != nil { + return nil, err + } + return out, nil +} + type DRPCSpaceSyncServer interface { HeadSync(context.Context, *HeadSyncRequest) (*HeadSyncResponse, error) SpacePush(context.Context, *SpacePushRequest) (*SpacePushResponse, error) SpacePull(context.Context, *SpacePullRequest) (*SpacePullResponse, error) ObjectSyncStream(DRPCSpaceSync_ObjectSyncStreamStream) error + ObjectSync(context.Context, *ObjectSyncMessage) (*ObjectSyncMessage, error) } type DRPCSpaceSyncUnimplementedServer struct{} @@ -147,9 +158,13 @@ func (s *DRPCSpaceSyncUnimplementedServer) ObjectSyncStream(DRPCSpaceSync_Object return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) } +func (s *DRPCSpaceSyncUnimplementedServer) ObjectSync(context.Context, *ObjectSyncMessage) (*ObjectSyncMessage, error) { + return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) +} + type DRPCSpaceSyncDescription struct{} -func (DRPCSpaceSyncDescription) NumMethods() int { return 4 } +func (DRPCSpaceSyncDescription) NumMethods() int { return 5 } func (DRPCSpaceSyncDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) { switch n { @@ -188,6 +203,15 @@ func (DRPCSpaceSyncDescription) Method(n int) (string, drpc.Encoding, drpc.Recei &drpcSpaceSync_ObjectSyncStreamStream{in1.(drpc.Stream)}, ) }, DRPCSpaceSyncServer.ObjectSyncStream, true + case 4: + return "/spacesync.SpaceSync/ObjectSync", drpcEncoding_File_commonspace_spacesyncproto_protos_spacesync_proto{}, + func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) { + return srv.(DRPCSpaceSyncServer). + ObjectSync( + ctx, + in1.(*ObjectSyncMessage), + ) + }, DRPCSpaceSyncServer.ObjectSync, true default: return "", nil, nil, nil, false } @@ -270,3 +294,19 @@ func (x *drpcSpaceSync_ObjectSyncStreamStream) Recv() (*ObjectSyncMessage, error func (x *drpcSpaceSync_ObjectSyncStreamStream) RecvMsg(m *ObjectSyncMessage) error { return x.MsgRecv(m, drpcEncoding_File_commonspace_spacesyncproto_protos_spacesync_proto{}) } + +type DRPCSpaceSync_ObjectSyncStream interface { + drpc.Stream + SendAndClose(*ObjectSyncMessage) error +} + +type drpcSpaceSync_ObjectSyncStream struct { + drpc.Stream +} + +func (x *drpcSpaceSync_ObjectSyncStream) SendAndClose(m *ObjectSyncMessage) error { + if err := x.MsgSend(m, drpcEncoding_File_commonspace_spacesyncproto_protos_spacesync_proto{}); err != nil { + return err + } + return x.CloseSend() +} diff --git a/commonspace/spaceutils_test.go b/commonspace/spaceutils_test.go index 000d571c..cc82cecd 100644 --- a/commonspace/spaceutils_test.go +++ b/commonspace/spaceutils_test.go @@ -6,12 +6,15 @@ import ( accountService "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/ocache" + "github.com/anyproto/any-sync/commonspace/config" "github.com/anyproto/any-sync/commonspace/credentialprovider" "github.com/anyproto/any-sync/commonspace/object/tree/objecttree" "github.com/anyproto/any-sync/commonspace/object/treemanager" + "github.com/anyproto/any-sync/commonspace/objecttreebuilder" "github.com/anyproto/any-sync/commonspace/peermanager" "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/pool" "github.com/anyproto/any-sync/nodeconf" @@ -128,6 +131,14 @@ func (m *mockConf) NodeTypes(nodeId string) []nodeconf.NodeType { type mockPeerManager struct { } +func (p *mockPeerManager) Init(a *app.App) (err error) { + return nil +} + +func (p *mockPeerManager) Name() (name string) { + return peermanager.CName +} + func (p *mockPeerManager) SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) { return nil } @@ -159,6 +170,25 @@ func (m *mockPeerManagerProvider) NewPeerManager(ctx context.Context, spaceId st return &mockPeerManager{}, nil } +// +// Mock StatusServiceProvider +// + +type mockStatusServiceProvider struct { +} + +func (m *mockStatusServiceProvider) Init(a *app.App) (err error) { + return nil +} + +func (m *mockStatusServiceProvider) Name() (name string) { + return syncstatus.CName +} + +func (m *mockStatusServiceProvider) NewStatusService() syncstatus.StatusService { + return syncstatus.NewNoOpSyncStatus() +} + // // Mock Pool // @@ -166,6 +196,10 @@ func (m *mockPeerManagerProvider) NewPeerManager(ctx context.Context, spaceId st type mockPool struct { } +func (m *mockPool) AddPeer(ctx context.Context, p peer.Peer) (err error) { + return nil +} + func (m *mockPool) Init(a *app.App) (err error) { return nil } @@ -205,8 +239,8 @@ func (m *mockConfig) Name() (name string) { return "config" } -func (m *mockConfig) GetSpace() Config { - return Config{ +func (m *mockConfig) GetSpace() config.Config { + return config.Config{ GCTTL: 60, SyncPeriod: 20, KeepTreeDataInMemory: true, @@ -236,6 +270,7 @@ type mockTreeManager struct { cache ocache.OCache deletedIds []string markedIds []string + waitLoad chan struct{} } func (t *mockTreeManager) NewTreeSyncer(spaceId string, treeManager treemanager.TreeManager) treemanager.TreeSyncer { @@ -249,7 +284,8 @@ func (t *mockTreeManager) MarkTreeDeleted(ctx context.Context, spaceId, treeId s func (t *mockTreeManager) Init(a *app.App) (err error) { t.cache = ocache.New(func(ctx context.Context, id string) (value ocache.Object, err error) { - return t.space.BuildTree(ctx, id, BuildTreeOpts{}) + <-t.waitLoad + return t.space.TreeBuilder().BuildTree(ctx, id, objecttreebuilder.BuildTreeOpts{}) }, ocache.WithGCPeriod(time.Minute), ocache.WithTTL(time.Duration(60)*time.Second)) @@ -318,12 +354,14 @@ func newFixture(t *testing.T) *spaceFixture { configurationService: &mockConf{}, storageProvider: spacestorage.NewInMemorySpaceStorageProvider(), peermanagerProvider: &mockPeerManagerProvider{}, - treeManager: &mockTreeManager{}, + treeManager: &mockTreeManager{waitLoad: make(chan struct{})}, pool: &mockPool{}, spaceService: New(), } fx.app.Register(fx.account). Register(fx.config). + Register(credentialprovider.NewNoOp()). + Register(&mockStatusServiceProvider{}). Register(fx.configurationService). Register(fx.storageProvider). Register(fx.peermanagerProvider). diff --git a/commonspace/syncstatus/noop.go b/commonspace/syncstatus/noop.go index 586b90d2..79424d3f 100644 --- a/commonspace/syncstatus/noop.go +++ b/commonspace/syncstatus/noop.go @@ -1,9 +1,32 @@ package syncstatus +import ( + "context" + "github.com/anyproto/any-sync/app" +) + +func NewNoOpSyncStatus() StatusService { + return &noOpSyncStatus{} +} + type noOpSyncStatus struct{} -func NewNoOpSyncStatus() StatusUpdater { - return &noOpSyncStatus{} +func (n *noOpSyncStatus) Init(a *app.App) (err error) { + return nil +} + +func (n *noOpSyncStatus) Name() (name string) { + return CName +} + +func (n *noOpSyncStatus) Watch(treeId string) (err error) { + return nil +} + +func (n *noOpSyncStatus) Unwatch(treeId string) { +} + +func (n *noOpSyncStatus) SetUpdateReceiver(updater UpdateReceiver) { } func (n *noOpSyncStatus) HeadsChange(treeId string, heads []string) { @@ -22,9 +45,10 @@ func (n *noOpSyncStatus) StateCounter() uint64 { func (n *noOpSyncStatus) RemoveAllExcept(senderId string, differentRemoteIds []string, stateCounter uint64) { } -func (n *noOpSyncStatus) Run() { -} - -func (n *noOpSyncStatus) Close() error { +func (n *noOpSyncStatus) Run(ctx context.Context) error { + return nil +} + +func (n *noOpSyncStatus) Close(ctx context.Context) error { return nil } diff --git a/commonspace/syncstatus/syncstatus.go b/commonspace/syncstatus/syncstatus.go index 91191573..9c580fe6 100644 --- a/commonspace/syncstatus/syncstatus.go +++ b/commonspace/syncstatus/syncstatus.go @@ -6,6 +6,9 @@ import ( "sync" "time" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/spacestate" + "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/commonspace/object/tree/treestorage" "github.com/anyproto/any-sync/commonspace/spacestorage" @@ -20,7 +23,9 @@ const ( syncTimeout = time.Second ) -var log = logger.NewNamed("common.commonspace.syncstatus") +var log = logger.NewNamed(CName) + +const CName = "common.commonspace.syncstatus" type UpdateReceiver interface { UpdateTree(ctx context.Context, treeId string, status SyncStatus) (err error) @@ -34,9 +39,6 @@ type StatusUpdater interface { SetNodesOnline(senderId string, online bool) StateCounter() uint64 RemoveAllExcept(senderId string, differentRemoteIds []string, stateCounter uint64) - - Run() - Close() error } type StatusWatcher interface { @@ -45,7 +47,13 @@ type StatusWatcher interface { SetUpdateReceiver(updater UpdateReceiver) } -type StatusProvider interface { +type StatusServiceProvider interface { + app.Component + NewStatusService() StatusService +} + +type StatusService interface { + app.ComponentRunnable StatusUpdater StatusWatcher } @@ -70,7 +78,7 @@ type treeStatus struct { heads []string } -type syncStatusProvider struct { +type syncStatusService struct { sync.Mutex configuration nodeconf.NodeConf periodicSync periodicsync.PeriodicSync @@ -89,52 +97,45 @@ type syncStatusProvider struct { updateTimeout time.Duration } -type SyncStatusDeps struct { - UpdateIntervalSecs int - UpdateTimeout time.Duration - Configuration nodeconf.NodeConf - Storage spacestorage.SpaceStorage -} - -func DefaultDeps(configuration nodeconf.NodeConf, store spacestorage.SpaceStorage) SyncStatusDeps { - return SyncStatusDeps{ - UpdateIntervalSecs: syncUpdateInterval, - UpdateTimeout: syncTimeout, - Configuration: configuration, - Storage: store, +func NewSyncStatusProvider() StatusService { + return &syncStatusService{ + treeHeads: map[string]treeHeadsEntry{}, + watchers: map[string]struct{}{}, } } -func NewSyncStatusProvider(spaceId string, deps SyncStatusDeps) StatusProvider { - return &syncStatusProvider{ - spaceId: spaceId, - treeHeads: map[string]treeHeadsEntry{}, - watchers: map[string]struct{}{}, - updateIntervalSecs: deps.UpdateIntervalSecs, - updateTimeout: deps.UpdateTimeout, - configuration: deps.Configuration, - storage: deps.Storage, - stateCounter: 0, - } +func (s *syncStatusService) Init(a *app.App) (err error) { + sharedState := a.MustComponent(spacestate.CName).(*spacestate.SpaceState) + s.updateIntervalSecs = syncUpdateInterval + s.updateTimeout = syncTimeout + s.spaceId = sharedState.SpaceId + s.configuration = a.MustComponent(nodeconf.CName).(nodeconf.NodeConf) + s.storage = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage) + return } -func (s *syncStatusProvider) SetUpdateReceiver(updater UpdateReceiver) { +func (s *syncStatusService) Name() (name string) { + return CName +} + +func (s *syncStatusService) SetUpdateReceiver(updater UpdateReceiver) { s.Lock() defer s.Unlock() s.updateReceiver = updater } -func (s *syncStatusProvider) Run() { +func (s *syncStatusService) Run(ctx context.Context) error { s.periodicSync = periodicsync.NewPeriodicSync( s.updateIntervalSecs, s.updateTimeout, s.update, log) s.periodicSync.Run() + return nil } -func (s *syncStatusProvider) HeadsChange(treeId string, heads []string) { +func (s *syncStatusService) HeadsChange(treeId string, heads []string) { s.Lock() defer s.Unlock() @@ -149,7 +150,7 @@ func (s *syncStatusProvider) HeadsChange(treeId string, heads []string) { s.stateCounter++ } -func (s *syncStatusProvider) SetNodesOnline(senderId string, online bool) { +func (s *syncStatusService) SetNodesOnline(senderId string, online bool) { if !s.isSenderResponsible(senderId) { return } @@ -160,7 +161,7 @@ func (s *syncStatusProvider) SetNodesOnline(senderId string, online bool) { s.nodesOnline = online } -func (s *syncStatusProvider) update(ctx context.Context) (err error) { +func (s *syncStatusService) update(ctx context.Context) (err error) { s.treeStatusBuf = s.treeStatusBuf[:0] s.Lock() @@ -178,8 +179,9 @@ func (s *syncStatusProvider) update(ctx context.Context) (err error) { } s.treeStatusBuf = append(s.treeStatusBuf, treeStatus{treeId, treeHeads.syncStatus, treeHeads.heads}) } + nodesOnline := s.nodesOnline s.Unlock() - s.updateReceiver.UpdateNodeConnection(s.nodesOnline) + s.updateReceiver.UpdateNodeConnection(nodesOnline) for _, entry := range s.treeStatusBuf { err = s.updateReceiver.UpdateTree(ctx, entry.treeId, entry.status) if err != nil { @@ -189,7 +191,7 @@ func (s *syncStatusProvider) update(ctx context.Context) (err error) { return } -func (s *syncStatusProvider) HeadsReceive(senderId, treeId string, heads []string) { +func (s *syncStatusService) HeadsReceive(senderId, treeId string, heads []string) { s.Lock() defer s.Unlock() @@ -218,7 +220,7 @@ func (s *syncStatusProvider) HeadsReceive(senderId, treeId string, heads []strin s.treeHeads[treeId] = curTreeHeads } -func (s *syncStatusProvider) Watch(treeId string) (err error) { +func (s *syncStatusService) Watch(treeId string) (err error) { s.Lock() defer s.Unlock() _, ok := s.treeHeads[treeId] @@ -248,7 +250,7 @@ func (s *syncStatusProvider) Watch(treeId string) (err error) { return } -func (s *syncStatusProvider) Unwatch(treeId string) { +func (s *syncStatusService) Unwatch(treeId string) { s.Lock() defer s.Unlock() @@ -257,19 +259,14 @@ func (s *syncStatusProvider) Unwatch(treeId string) { } } -func (s *syncStatusProvider) Close() (err error) { - s.periodicSync.Close() - return -} - -func (s *syncStatusProvider) StateCounter() uint64 { +func (s *syncStatusService) StateCounter() uint64 { s.Lock() defer s.Unlock() return s.stateCounter } -func (s *syncStatusProvider) RemoveAllExcept(senderId string, differentRemoteIds []string, stateCounter uint64) { +func (s *syncStatusService) RemoveAllExcept(senderId string, differentRemoteIds []string, stateCounter uint64) { // if sender is not a responsible node, then this should have no effect if !s.isSenderResponsible(senderId) { return @@ -292,6 +289,11 @@ func (s *syncStatusProvider) RemoveAllExcept(senderId string, differentRemoteIds } } -func (s *syncStatusProvider) isSenderResponsible(senderId string) bool { +func (s *syncStatusService) Close(ctx context.Context) error { + s.periodicSync.Close() + return nil +} + +func (s *syncStatusService) isSenderResponsible(senderId string) bool { return slices.Contains(s.configuration.NodeIds(s.spaceId), senderId) } diff --git a/consensus/consensusclient/client.go b/consensus/consensusclient/client.go new file mode 100644 index 00000000..d745bef6 --- /dev/null +++ b/consensus/consensusclient/client.go @@ -0,0 +1,243 @@ +//go:generate mockgen -destination mock_consensusclient/mock_consensusclient.go github.com/anyproto/any-sync/consensus/consensusclient Service +package consensusclient + +import ( + "context" + "errors" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/app/logger" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/anyproto/any-sync/net/pool" + "github.com/anyproto/any-sync/net/rpc/rpcerr" + "github.com/anyproto/any-sync/nodeconf" + "go.uber.org/zap" + "sync" + "time" +) + +const CName = "consensus.consensusclient" + +var log = logger.NewNamed(CName) + +var ( + ErrWatcherExists = errors.New("watcher exists") + ErrWatcherNotExists = errors.New("watcher not exists") +) + +func New() Service { + return new(service) +} + +// Watcher watches new events by specified logId +type Watcher interface { + AddConsensusRecords(recs []*consensusproto.RawRecordWithId) + AddConsensusError(err error) +} + +type Service interface { + // AddLog adds new log to consensus servers + AddLog(ctx context.Context, rec *consensusproto.RawRecordWithId) (err error) + // AddRecord adds new record to consensus servers + AddRecord(ctx context.Context, logId string, rec *consensusproto.RawRecord) (record *consensusproto.RawRecordWithId, err error) + // Watch starts watching to given logId and calls watcher when any relative event received + Watch(logId string, w Watcher) (err error) + // UnWatch stops watching given logId and removes watcher + UnWatch(logId string) (err error) + app.ComponentRunnable +} + +type service struct { + pool pool.Pool + nodeconf nodeconf.Service + + watchers map[string]Watcher + stream *stream + close chan struct{} + mu sync.Mutex +} + +func (s *service) Init(a *app.App) (err error) { + s.pool = a.MustComponent(pool.CName).(pool.Pool) + s.nodeconf = a.MustComponent(nodeconf.CName).(nodeconf.Service) + s.watchers = make(map[string]Watcher) + s.close = make(chan struct{}) + return nil +} + +func (s *service) Name() (name string) { + return CName +} + +func (s *service) Run(_ context.Context) error { + go s.streamWatcher() + return nil +} + +func (s *service) doClient(ctx context.Context, fn func(cl consensusproto.DRPCConsensusClient) error) error { + peer, err := s.pool.GetOneOf(ctx, s.nodeconf.ConsensusPeers()) + if err != nil { + return err + } + dc, err := peer.AcquireDrpcConn(ctx) + if err != nil { + return err + } + defer peer.ReleaseDrpcConn(dc) + return fn(consensusproto.NewDRPCConsensusClient(dc)) +} + +func (s *service) AddLog(ctx context.Context, rec *consensusproto.RawRecordWithId) (err error) { + return s.doClient(ctx, func(cl consensusproto.DRPCConsensusClient) error { + if _, err = cl.LogAdd(ctx, &consensusproto.LogAddRequest{ + Record: rec, + }); err != nil { + return rpcerr.Unwrap(err) + } + return nil + }) +} + +func (s *service) AddRecord(ctx context.Context, logId string, rec *consensusproto.RawRecord) (record *consensusproto.RawRecordWithId, err error) { + err = s.doClient(ctx, func(cl consensusproto.DRPCConsensusClient) error { + if record, err = cl.RecordAdd(ctx, &consensusproto.RecordAddRequest{ + LogId: logId, + Record: rec, + }); err != nil { + return rpcerr.Unwrap(err) + } + return nil + }) + return +} + +func (s *service) Watch(logId string, w Watcher) (err error) { + s.mu.Lock() + defer s.mu.Unlock() + if _, ok := s.watchers[logId]; ok { + return ErrWatcherExists + } + s.watchers[logId] = w + if s.stream != nil { + if wErr := s.stream.WatchIds([]string{logId}); wErr != nil { + log.Warn("WatchIds error", zap.Error(wErr)) + } + } + return +} + +func (s *service) UnWatch(logId string) (err error) { + s.mu.Lock() + defer s.mu.Unlock() + if _, ok := s.watchers[logId]; !ok { + return ErrWatcherNotExists + } + delete(s.watchers, logId) + if s.stream != nil { + if wErr := s.stream.UnwatchIds([]string{logId}); wErr != nil { + log.Warn("UnWatchIds error", zap.Error(wErr)) + } + } + return +} + +func (s *service) openStream(ctx context.Context) (st *stream, err error) { + pr, err := s.pool.GetOneOf(ctx, s.nodeconf.ConsensusPeers()) + if err != nil { + return nil, err + } + dc, err := pr.AcquireDrpcConn(ctx) + if err != nil { + return nil, err + } + rpcStream, err := consensusproto.NewDRPCConsensusClient(dc).LogWatch(ctx) + if err != nil { + return nil, rpcerr.Unwrap(err) + } + return runStream(rpcStream), nil +} + +func (s *service) streamWatcher() { + var ( + err error + st *stream + i int + ) + for { + // open stream + if st, err = s.openStream(context.Background()); err != nil { + // can't open stream, we will retry until success connection or close + if i < 60 { + i++ + } + sleepTime := time.Second * time.Duration(i) + log.Error("watch log error", zap.Error(err), zap.Duration("waitTime", sleepTime)) + select { + case <-time.After(sleepTime): + continue + case <-s.close: + return + } + } + i = 0 + + // collect ids and setup stream + s.mu.Lock() + var logIds = make([]string, 0, len(s.watchers)) + for id := range s.watchers { + logIds = append(logIds, id) + } + s.stream = st + s.mu.Unlock() + + // restore subscriptions + if len(logIds) > 0 { + if err = s.stream.WatchIds(logIds); err != nil { + log.Error("watch ids error", zap.Error(err)) + continue + } + } + + // read stream + if err = s.streamReader(); err != nil { + log.Error("stream read error", zap.Error(err)) + continue + } + return + } +} + +func (s *service) streamReader() error { + for { + events := s.stream.WaitLogs() + if len(events) == 0 { + return s.stream.Err() + } + s.mu.Lock() + for _, e := range events { + if w, ok := s.watchers[e.LogId]; ok { + if e.Error == nil { + w.AddConsensusRecords(e.Records) + } else { + w.AddConsensusError(rpcerr.Err(uint64(e.Error.Error))) + } + } else { + log.Warn("received unexpected log id", zap.String("logId", e.LogId)) + } + } + s.mu.Unlock() + } +} + +func (s *service) Close(_ context.Context) error { + s.mu.Lock() + if s.stream != nil { + _ = s.stream.Close() + } + s.mu.Unlock() + select { + case <-s.close: + default: + close(s.close) + } + return nil +} diff --git a/consensus/consensusclient/client_test.go b/consensus/consensusclient/client_test.go new file mode 100644 index 00000000..ea47c50a --- /dev/null +++ b/consensus/consensusclient/client_test.go @@ -0,0 +1,241 @@ +package consensusclient + +import ( + "context" + "fmt" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/anyproto/any-sync/consensus/consensusproto/consensuserr" + "github.com/anyproto/any-sync/net/pool" + "github.com/anyproto/any-sync/net/rpc/rpctest" + "github.com/anyproto/any-sync/nodeconf" + "github.com/anyproto/any-sync/nodeconf/mock_nodeconf" + "github.com/anyproto/any-sync/testutil/accounttest" + "github.com/anyproto/any-sync/util/cidutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "sync" + "testing" + "time" +) + +func TestService_Watch(t *testing.T) { + t.Run("not found error", func(t *testing.T) { + fx := newFixture(t).run(t) + defer fx.Finish() + var logId = "1" + w := &testWatcher{ready: make(chan struct{})} + require.NoError(t, fx.Watch(logId, w)) + st := fx.testServer.waitStream(t) + req, err := st.Recv() + require.NoError(t, err) + assert.Equal(t, []string{logId}, req.WatchIds) + require.NoError(t, st.Send(&consensusproto.LogWatchEvent{ + LogId: logId, + Error: &consensusproto.Err{ + Error: consensusproto.ErrCodes_ErrorOffset + consensusproto.ErrCodes_LogNotFound, + }, + })) + <-w.ready + assert.Equal(t, consensuserr.ErrLogNotFound, w.err) + fx.testServer.releaseStream <- nil + }) + t.Run("watcherExists error", func(t *testing.T) { + fx := newFixture(t).run(t) + defer fx.Finish() + var logId = "1" + w := &testWatcher{} + require.NoError(t, fx.Watch(logId, w)) + require.Error(t, fx.Watch(logId, w)) + st := fx.testServer.waitStream(t) + st.Recv() + fx.testServer.releaseStream <- nil + }) + t.Run("watch", func(t *testing.T) { + fx := newFixture(t).run(t) + defer fx.Finish() + var logId1 = "1" + w := &testWatcher{} + require.NoError(t, fx.Watch(logId1, w)) + st := fx.testServer.waitStream(t) + req, err := st.Recv() + require.NoError(t, err) + assert.Equal(t, []string{logId1}, req.WatchIds) + + var logId2 = "2" + w = &testWatcher{} + require.NoError(t, fx.Watch(logId2, w)) + req, err = st.Recv() + require.NoError(t, err) + assert.Equal(t, []string{logId2}, req.WatchIds) + + fx.testServer.releaseStream <- nil + }) +} + +func TestService_UnWatch(t *testing.T) { + t.Run("no watcher", func(t *testing.T) { + fx := newFixture(t).run(t) + defer fx.Finish() + require.Error(t, fx.UnWatch("1")) + }) + t.Run("success", func(t *testing.T) { + fx := newFixture(t).run(t) + defer fx.Finish() + w := &testWatcher{} + require.NoError(t, fx.Watch("1", w)) + assert.NoError(t, fx.UnWatch("1")) + }) +} + +func TestService_Init(t *testing.T) { + t.Run("reconnect on watch err", func(t *testing.T) { + fx := newFixture(t) + fx.testServer.watchErrOnce = true + fx.run(t) + defer fx.Finish() + fx.testServer.waitStream(t) + fx.testServer.releaseStream <- nil + }) + t.Run("reconnect on start", func(t *testing.T) { + fx := newFixture(t) + fx.a.MustComponent(pool.CName).(*rpctest.TestPool).WithServer(nil) + fx.run(t) + defer fx.Finish() + time.Sleep(time.Millisecond * 50) + fx.a.MustComponent(pool.CName).(*rpctest.TestPool).WithServer(fx.drpcTS) + fx.testServer.waitStream(t) + fx.testServer.releaseStream <- nil + }) +} + +func TestService_AddLog(t *testing.T) { + fx := newFixture(t).run(t) + defer fx.Finish() + assert.NoError(t, fx.AddLog(ctx, &consensusproto.RawRecordWithId{})) +} + +func TestService_AddRecord(t *testing.T) { + fx := newFixture(t).run(t) + defer fx.Finish() + rec, err := fx.AddRecord(ctx, "1", &consensusproto.RawRecord{}) + require.NoError(t, err) + assert.NotEmpty(t, rec) +} + +var ctx = context.Background() + +func newFixture(t *testing.T) *fixture { + fx := &fixture{ + Service: New(), + a: &app.App{}, + ctrl: gomock.NewController(t), + testServer: &testServer{ + stream: make(chan consensusproto.DRPCConsensus_LogWatchStream), + releaseStream: make(chan error), + }, + } + fx.nodeconf = mock_nodeconf.NewMockService(fx.ctrl) + fx.nodeconf.EXPECT().Name().Return(nodeconf.CName).AnyTimes() + fx.nodeconf.EXPECT().Init(gomock.Any()).AnyTimes() + fx.nodeconf.EXPECT().Run(gomock.Any()).AnyTimes() + fx.nodeconf.EXPECT().Close(gomock.Any()).AnyTimes() + fx.nodeconf.EXPECT().ConsensusPeers().Return([]string{"c1", "c2", "c3"}).AnyTimes() + + fx.drpcTS = rpctest.NewTestServer() + require.NoError(t, consensusproto.DRPCRegisterConsensus(fx.drpcTS.Mux, fx.testServer)) + fx.a.Register(fx.Service). + Register(&accounttest.AccountTestService{}). + Register(fx.nodeconf). + Register(rpctest.NewTestPool().WithServer(fx.drpcTS)) + + return fx +} + +type fixture struct { + Service + a *app.App + ctrl *gomock.Controller + testServer *testServer + drpcTS *rpctest.TestServer + nodeconf *mock_nodeconf.MockService +} + +func (fx *fixture) run(t *testing.T) *fixture { + require.NoError(t, fx.a.Start(ctx)) + return fx +} + +func (fx *fixture) Finish() { + assert.NoError(fx.ctrl.T, fx.a.Close(ctx)) + fx.ctrl.Finish() +} + +type testServer struct { + stream chan consensusproto.DRPCConsensus_LogWatchStream + addLog func(ctx context.Context, req *consensusproto.LogAddRequest) error + addRecord func(ctx context.Context, req *consensusproto.RecordAddRequest) error + releaseStream chan error + watchErrOnce bool +} + +func (t *testServer) LogAdd(ctx context.Context, req *consensusproto.LogAddRequest) (*consensusproto.Ok, error) { + if t.addLog != nil { + if err := t.addLog(ctx, req); err != nil { + return nil, err + } + } + return &consensusproto.Ok{}, nil +} + +func (t *testServer) RecordAdd(ctx context.Context, req *consensusproto.RecordAddRequest) (*consensusproto.RawRecordWithId, error) { + if t.addRecord != nil { + if err := t.addRecord(ctx, req); err != nil { + return nil, err + } + } + data, _ := req.Record.Marshal() + id, _ := cidutil.NewCidFromBytes(data) + return &consensusproto.RawRecordWithId{Id: id, Payload: data}, nil +} + +func (t *testServer) LogWatch(stream consensusproto.DRPCConsensus_LogWatchStream) error { + if t.watchErrOnce { + t.watchErrOnce = false + return fmt.Errorf("error") + } + t.stream <- stream + return <-t.releaseStream +} + +func (t *testServer) waitStream(test *testing.T) consensusproto.DRPCConsensus_LogWatchStream { + select { + case <-time.After(time.Second * 5): + test.Fatalf("waiteStream timeout") + case st := <-t.stream: + return st + } + return nil +} + +type testWatcher struct { + recs [][]*consensusproto.RawRecordWithId + err error + ready chan struct{} + once sync.Once +} + +func (t *testWatcher) AddConsensusRecords(recs []*consensusproto.RawRecordWithId) { + t.recs = append(t.recs, recs) + t.once.Do(func() { + close(t.ready) + }) +} + +func (t *testWatcher) AddConsensusError(err error) { + t.err = err + t.once.Do(func() { + close(t.ready) + }) +} diff --git a/consensus/consensusclient/mock_consensusclient/mock_consensusclient.go b/consensus/consensusclient/mock_consensusclient/mock_consensusclient.go new file mode 100644 index 00000000..242bd645 --- /dev/null +++ b/consensus/consensusclient/mock_consensusclient/mock_consensusclient.go @@ -0,0 +1,151 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/anyproto/any-sync/consensus/consensusclient (interfaces: Service) + +// Package mock_consensusclient is a generated GoMock package. +package mock_consensusclient + +import ( + context "context" + reflect "reflect" + + app "github.com/anyproto/any-sync/app" + consensusclient "github.com/anyproto/any-sync/consensus/consensusclient" + consensusproto "github.com/anyproto/any-sync/consensus/consensusproto" + gomock "go.uber.org/mock/gomock" +) + +// MockService is a mock of Service interface. +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService. +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance. +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// AddLog mocks base method. +func (m *MockService) AddLog(arg0 context.Context, arg1 *consensusproto.RawRecordWithId) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddLog", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddLog indicates an expected call of AddLog. +func (mr *MockServiceMockRecorder) AddLog(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddLog", reflect.TypeOf((*MockService)(nil).AddLog), arg0, arg1) +} + +// AddRecord mocks base method. +func (m *MockService) AddRecord(arg0 context.Context, arg1 string, arg2 *consensusproto.RawRecord) (*consensusproto.RawRecordWithId, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddRecord", arg0, arg1, arg2) + ret0, _ := ret[0].(*consensusproto.RawRecordWithId) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddRecord indicates an expected call of AddRecord. +func (mr *MockServiceMockRecorder) AddRecord(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRecord", reflect.TypeOf((*MockService)(nil).AddRecord), arg0, arg1, arg2) +} + +// Close mocks base method. +func (m *MockService) Close(arg0 context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockServiceMockRecorder) Close(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockService)(nil).Close), arg0) +} + +// Init mocks base method. +func (m *MockService) Init(arg0 *app.App) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockServiceMockRecorder) Init(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockService)(nil).Init), arg0) +} + +// Name mocks base method. +func (m *MockService) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockServiceMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockService)(nil).Name)) +} + +// Run mocks base method. +func (m *MockService) Run(arg0 context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Run", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Run indicates an expected call of Run. +func (mr *MockServiceMockRecorder) Run(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockService)(nil).Run), arg0) +} + +// UnWatch mocks base method. +func (m *MockService) UnWatch(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnWatch", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnWatch indicates an expected call of UnWatch. +func (mr *MockServiceMockRecorder) UnWatch(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnWatch", reflect.TypeOf((*MockService)(nil).UnWatch), arg0) +} + +// Watch mocks base method. +func (m *MockService) Watch(arg0 string, arg1 consensusclient.Watcher) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Watch", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Watch indicates an expected call of Watch. +func (mr *MockServiceMockRecorder) Watch(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockService)(nil).Watch), arg0, arg1) +} diff --git a/consensus/consensusclient/stream.go b/consensus/consensusclient/stream.go new file mode 100644 index 00000000..13348bf2 --- /dev/null +++ b/consensus/consensusclient/stream.go @@ -0,0 +1,70 @@ +package consensusclient + +import ( + "context" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/cheggaaa/mb/v3" + "sync" +) + +func runStream(rpcStream consensusproto.DRPCConsensus_LogWatchClient) *stream { + st := &stream{ + rpcStream: rpcStream, + mb: mb.New[*consensusproto.LogWatchEvent](100), + } + go st.readStream() + return st +} + +type stream struct { + rpcStream consensusproto.DRPCConsensus_LogWatchClient + mb *mb.MB[*consensusproto.LogWatchEvent] + mu sync.Mutex + err error +} + +func (s *stream) WatchIds(logIds []string) (err error) { + return s.rpcStream.Send(&consensusproto.LogWatchRequest{ + WatchIds: logIds, + }) +} + +func (s *stream) UnwatchIds(logIds []string) (err error) { + return s.rpcStream.Send(&consensusproto.LogWatchRequest{ + UnwatchIds: logIds, + }) +} + +func (s *stream) WaitLogs() []*consensusproto.LogWatchEvent { + events, _ := s.mb.Wait(context.TODO()) + return events +} + +func (s *stream) Err() error { + s.mu.Lock() + defer s.mu.Unlock() + return s.err +} + +func (s *stream) readStream() { + defer s.Close() + for { + event, err := s.rpcStream.Recv() + if err != nil { + s.mu.Lock() + s.err = err + s.mu.Unlock() + return + } + if err = s.mb.Add(s.rpcStream.Context(), event); err != nil { + return + } + } +} + +func (s *stream) Close() error { + if err := s.mb.Close(); err == nil { + return s.rpcStream.Close() + } + return nil +} diff --git a/consensus/consensusproto/consensus.go b/consensus/consensusproto/consensus.go new file mode 100644 index 00000000..deb0157a --- /dev/null +++ b/consensus/consensusproto/consensus.go @@ -0,0 +1,45 @@ +package consensusproto + +func WrapHeadUpdate(update *LogHeadUpdate, rootRecord *RawRecordWithId) *LogSyncMessage { + return &LogSyncMessage{ + Content: &LogSyncContentValue{ + Value: &LogSyncContentValue_HeadUpdate{HeadUpdate: update}, + }, + Id: rootRecord.Id, + Payload: rootRecord.Payload, + } +} + +func WrapFullRequest(request *LogFullSyncRequest, rootRecord *RawRecordWithId) *LogSyncMessage { + return &LogSyncMessage{ + Content: &LogSyncContentValue{ + Value: &LogSyncContentValue_FullSyncRequest{FullSyncRequest: request}, + }, + Id: rootRecord.Id, + Payload: rootRecord.Payload, + } +} + +func WrapFullResponse(response *LogFullSyncResponse, rootRecord *RawRecordWithId) *LogSyncMessage { + return &LogSyncMessage{ + Content: &LogSyncContentValue{ + Value: &LogSyncContentValue_FullSyncResponse{FullSyncResponse: response}, + }, + Id: rootRecord.Id, + Payload: rootRecord.Payload, + } +} + +func GetHead(msg *LogSyncMessage) (head string) { + content := msg.GetContent() + switch { + case content.GetHeadUpdate() != nil: + return content.GetHeadUpdate().Head + case content.GetFullSyncRequest() != nil: + return content.GetFullSyncRequest().Head + case content.GetFullSyncResponse() != nil: + return content.GetFullSyncResponse().Head + default: + return "" + } +} diff --git a/consensus/consensusproto/consensus.pb.go b/consensus/consensusproto/consensus.pb.go new file mode 100644 index 00000000..dbee9248 --- /dev/null +++ b/consensus/consensusproto/consensus.pb.go @@ -0,0 +1,3913 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: consensus/consensusproto/protos/consensus.proto + +package consensusproto + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type ErrCodes int32 + +const ( + ErrCodes_Unexpected ErrCodes = 0 + ErrCodes_LogExists ErrCodes = 1 + ErrCodes_LogNotFound ErrCodes = 2 + ErrCodes_RecordConflict ErrCodes = 3 + ErrCodes_Forbidden ErrCodes = 4 + ErrCodes_InvalidPayload ErrCodes = 5 + ErrCodes_ErrorOffset ErrCodes = 500 +) + +var ErrCodes_name = map[int32]string{ + 0: "Unexpected", + 1: "LogExists", + 2: "LogNotFound", + 3: "RecordConflict", + 4: "Forbidden", + 5: "InvalidPayload", + 500: "ErrorOffset", +} + +var ErrCodes_value = map[string]int32{ + "Unexpected": 0, + "LogExists": 1, + "LogNotFound": 2, + "RecordConflict": 3, + "Forbidden": 4, + "InvalidPayload": 5, + "ErrorOffset": 500, +} + +func (x ErrCodes) String() string { + return proto.EnumName(ErrCodes_name, int32(x)) +} + +func (ErrCodes) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{0} +} + +type Log struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Records []*RawRecordWithId `protobuf:"bytes,3,rep,name=records,proto3" json:"records,omitempty"` +} + +func (m *Log) Reset() { *m = Log{} } +func (m *Log) String() string { return proto.CompactTextString(m) } +func (*Log) ProtoMessage() {} +func (*Log) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{0} +} +func (m *Log) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Log) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Log.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 *Log) XXX_Merge(src proto.Message) { + xxx_messageInfo_Log.Merge(m, src) +} +func (m *Log) XXX_Size() int { + return m.Size() +} +func (m *Log) XXX_DiscardUnknown() { + xxx_messageInfo_Log.DiscardUnknown(m) +} + +var xxx_messageInfo_Log proto.InternalMessageInfo + +func (m *Log) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Log) GetRecords() []*RawRecordWithId { + if m != nil { + return m.Records + } + return nil +} + +// RawRecord is a proto message containing the payload in bytes, signature of the account who added it and signature of the acceptor +type RawRecord struct { + Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + AcceptorIdentity []byte `protobuf:"bytes,3,opt,name=acceptorIdentity,proto3" json:"acceptorIdentity,omitempty"` + AcceptorSignature []byte `protobuf:"bytes,4,opt,name=acceptorSignature,proto3" json:"acceptorSignature,omitempty"` +} + +func (m *RawRecord) Reset() { *m = RawRecord{} } +func (m *RawRecord) String() string { return proto.CompactTextString(m) } +func (*RawRecord) ProtoMessage() {} +func (*RawRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{1} +} +func (m *RawRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RawRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RawRecord.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 *RawRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_RawRecord.Merge(m, src) +} +func (m *RawRecord) XXX_Size() int { + return m.Size() +} +func (m *RawRecord) XXX_DiscardUnknown() { + xxx_messageInfo_RawRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_RawRecord proto.InternalMessageInfo + +func (m *RawRecord) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *RawRecord) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *RawRecord) GetAcceptorIdentity() []byte { + if m != nil { + return m.AcceptorIdentity + } + return nil +} + +func (m *RawRecord) GetAcceptorSignature() []byte { + if m != nil { + return m.AcceptorSignature + } + return nil +} + +// RawRecordWithId is a raw record and the id for convenience +type RawRecordWithId struct { + Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *RawRecordWithId) Reset() { *m = RawRecordWithId{} } +func (m *RawRecordWithId) String() string { return proto.CompactTextString(m) } +func (*RawRecordWithId) ProtoMessage() {} +func (*RawRecordWithId) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{2} +} +func (m *RawRecordWithId) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RawRecordWithId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RawRecordWithId.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 *RawRecordWithId) XXX_Merge(src proto.Message) { + xxx_messageInfo_RawRecordWithId.Merge(m, src) +} +func (m *RawRecordWithId) XXX_Size() int { + return m.Size() +} +func (m *RawRecordWithId) XXX_DiscardUnknown() { + xxx_messageInfo_RawRecordWithId.DiscardUnknown(m) +} + +var xxx_messageInfo_RawRecordWithId proto.InternalMessageInfo + +func (m *RawRecordWithId) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *RawRecordWithId) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +// Record is a record containing a data +type Record struct { + PrevId string `protobuf:"bytes,1,opt,name=prevId,proto3" json:"prevId,omitempty"` + Identity []byte `protobuf:"bytes,2,opt,name=identity,proto3" json:"identity,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *Record) Reset() { *m = Record{} } +func (m *Record) String() string { return proto.CompactTextString(m) } +func (*Record) ProtoMessage() {} +func (*Record) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{3} +} +func (m *Record) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Record) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Record.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 *Record) XXX_Merge(src proto.Message) { + xxx_messageInfo_Record.Merge(m, src) +} +func (m *Record) XXX_Size() int { + return m.Size() +} +func (m *Record) XXX_DiscardUnknown() { + xxx_messageInfo_Record.DiscardUnknown(m) +} + +var xxx_messageInfo_Record proto.InternalMessageInfo + +func (m *Record) GetPrevId() string { + if m != nil { + return m.PrevId + } + return "" +} + +func (m *Record) GetIdentity() []byte { + if m != nil { + return m.Identity + } + return nil +} + +func (m *Record) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *Record) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type Ok struct { +} + +func (m *Ok) Reset() { *m = Ok{} } +func (m *Ok) String() string { return proto.CompactTextString(m) } +func (*Ok) ProtoMessage() {} +func (*Ok) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{4} +} +func (m *Ok) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Ok) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Ok.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 *Ok) XXX_Merge(src proto.Message) { + xxx_messageInfo_Ok.Merge(m, src) +} +func (m *Ok) XXX_Size() int { + return m.Size() +} +func (m *Ok) XXX_DiscardUnknown() { + xxx_messageInfo_Ok.DiscardUnknown(m) +} + +var xxx_messageInfo_Ok proto.InternalMessageInfo + +type LogAddRequest struct { + // first record in the log, consensus node not sign it + Record *RawRecordWithId `protobuf:"bytes,1,opt,name=record,proto3" json:"record,omitempty"` +} + +func (m *LogAddRequest) Reset() { *m = LogAddRequest{} } +func (m *LogAddRequest) String() string { return proto.CompactTextString(m) } +func (*LogAddRequest) ProtoMessage() {} +func (*LogAddRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{5} +} +func (m *LogAddRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogAddRequest.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 *LogAddRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogAddRequest.Merge(m, src) +} +func (m *LogAddRequest) XXX_Size() int { + return m.Size() +} +func (m *LogAddRequest) XXX_DiscardUnknown() { + xxx_messageInfo_LogAddRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_LogAddRequest proto.InternalMessageInfo + +func (m *LogAddRequest) GetRecord() *RawRecordWithId { + if m != nil { + return m.Record + } + return nil +} + +type RecordAddRequest struct { + LogId string `protobuf:"bytes,1,opt,name=logId,proto3" json:"logId,omitempty"` + Record *RawRecord `protobuf:"bytes,2,opt,name=record,proto3" json:"record,omitempty"` +} + +func (m *RecordAddRequest) Reset() { *m = RecordAddRequest{} } +func (m *RecordAddRequest) String() string { return proto.CompactTextString(m) } +func (*RecordAddRequest) ProtoMessage() {} +func (*RecordAddRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{6} +} +func (m *RecordAddRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RecordAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RecordAddRequest.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 *RecordAddRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RecordAddRequest.Merge(m, src) +} +func (m *RecordAddRequest) XXX_Size() int { + return m.Size() +} +func (m *RecordAddRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RecordAddRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RecordAddRequest proto.InternalMessageInfo + +func (m *RecordAddRequest) GetLogId() string { + if m != nil { + return m.LogId + } + return "" +} + +func (m *RecordAddRequest) GetRecord() *RawRecord { + if m != nil { + return m.Record + } + return nil +} + +type LogWatchRequest struct { + WatchIds []string `protobuf:"bytes,1,rep,name=watchIds,proto3" json:"watchIds,omitempty"` + UnwatchIds []string `protobuf:"bytes,2,rep,name=unwatchIds,proto3" json:"unwatchIds,omitempty"` +} + +func (m *LogWatchRequest) Reset() { *m = LogWatchRequest{} } +func (m *LogWatchRequest) String() string { return proto.CompactTextString(m) } +func (*LogWatchRequest) ProtoMessage() {} +func (*LogWatchRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{7} +} +func (m *LogWatchRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogWatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogWatchRequest.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 *LogWatchRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogWatchRequest.Merge(m, src) +} +func (m *LogWatchRequest) XXX_Size() int { + return m.Size() +} +func (m *LogWatchRequest) XXX_DiscardUnknown() { + xxx_messageInfo_LogWatchRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_LogWatchRequest proto.InternalMessageInfo + +func (m *LogWatchRequest) GetWatchIds() []string { + if m != nil { + return m.WatchIds + } + return nil +} + +func (m *LogWatchRequest) GetUnwatchIds() []string { + if m != nil { + return m.UnwatchIds + } + return nil +} + +type LogWatchEvent struct { + LogId string `protobuf:"bytes,1,opt,name=logId,proto3" json:"logId,omitempty"` + Records []*RawRecordWithId `protobuf:"bytes,2,rep,name=records,proto3" json:"records,omitempty"` + Error *Err `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *LogWatchEvent) Reset() { *m = LogWatchEvent{} } +func (m *LogWatchEvent) String() string { return proto.CompactTextString(m) } +func (*LogWatchEvent) ProtoMessage() {} +func (*LogWatchEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{8} +} +func (m *LogWatchEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogWatchEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogWatchEvent.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 *LogWatchEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogWatchEvent.Merge(m, src) +} +func (m *LogWatchEvent) XXX_Size() int { + return m.Size() +} +func (m *LogWatchEvent) XXX_DiscardUnknown() { + xxx_messageInfo_LogWatchEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_LogWatchEvent proto.InternalMessageInfo + +func (m *LogWatchEvent) GetLogId() string { + if m != nil { + return m.LogId + } + return "" +} + +func (m *LogWatchEvent) GetRecords() []*RawRecordWithId { + if m != nil { + return m.Records + } + return nil +} + +func (m *LogWatchEvent) GetError() *Err { + if m != nil { + return m.Error + } + return nil +} + +type Err struct { + Error ErrCodes `protobuf:"varint,1,opt,name=error,proto3,enum=consensusProto.ErrCodes" json:"error,omitempty"` +} + +func (m *Err) Reset() { *m = Err{} } +func (m *Err) String() string { return proto.CompactTextString(m) } +func (*Err) ProtoMessage() {} +func (*Err) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{9} +} +func (m *Err) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Err) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Err.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 *Err) XXX_Merge(src proto.Message) { + xxx_messageInfo_Err.Merge(m, src) +} +func (m *Err) XXX_Size() int { + return m.Size() +} +func (m *Err) XXX_DiscardUnknown() { + xxx_messageInfo_Err.DiscardUnknown(m) +} + +var xxx_messageInfo_Err proto.InternalMessageInfo + +func (m *Err) GetError() ErrCodes { + if m != nil { + return m.Error + } + return ErrCodes_Unexpected +} + +// LogSyncContentValue provides different types for log sync +type LogSyncContentValue struct { + // Types that are valid to be assigned to Value: + // + // *LogSyncContentValue_HeadUpdate + // *LogSyncContentValue_FullSyncRequest + // *LogSyncContentValue_FullSyncResponse + Value isLogSyncContentValue_Value `protobuf_oneof:"value"` +} + +func (m *LogSyncContentValue) Reset() { *m = LogSyncContentValue{} } +func (m *LogSyncContentValue) String() string { return proto.CompactTextString(m) } +func (*LogSyncContentValue) ProtoMessage() {} +func (*LogSyncContentValue) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{10} +} +func (m *LogSyncContentValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogSyncContentValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogSyncContentValue.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 *LogSyncContentValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogSyncContentValue.Merge(m, src) +} +func (m *LogSyncContentValue) XXX_Size() int { + return m.Size() +} +func (m *LogSyncContentValue) XXX_DiscardUnknown() { + xxx_messageInfo_LogSyncContentValue.DiscardUnknown(m) +} + +var xxx_messageInfo_LogSyncContentValue proto.InternalMessageInfo + +type isLogSyncContentValue_Value interface { + isLogSyncContentValue_Value() + MarshalTo([]byte) (int, error) + Size() int +} + +type LogSyncContentValue_HeadUpdate struct { + HeadUpdate *LogHeadUpdate `protobuf:"bytes,1,opt,name=headUpdate,proto3,oneof" json:"headUpdate,omitempty"` +} +type LogSyncContentValue_FullSyncRequest struct { + FullSyncRequest *LogFullSyncRequest `protobuf:"bytes,2,opt,name=fullSyncRequest,proto3,oneof" json:"fullSyncRequest,omitempty"` +} +type LogSyncContentValue_FullSyncResponse struct { + FullSyncResponse *LogFullSyncResponse `protobuf:"bytes,3,opt,name=fullSyncResponse,proto3,oneof" json:"fullSyncResponse,omitempty"` +} + +func (*LogSyncContentValue_HeadUpdate) isLogSyncContentValue_Value() {} +func (*LogSyncContentValue_FullSyncRequest) isLogSyncContentValue_Value() {} +func (*LogSyncContentValue_FullSyncResponse) isLogSyncContentValue_Value() {} + +func (m *LogSyncContentValue) GetValue() isLogSyncContentValue_Value { + if m != nil { + return m.Value + } + return nil +} + +func (m *LogSyncContentValue) GetHeadUpdate() *LogHeadUpdate { + if x, ok := m.GetValue().(*LogSyncContentValue_HeadUpdate); ok { + return x.HeadUpdate + } + return nil +} + +func (m *LogSyncContentValue) GetFullSyncRequest() *LogFullSyncRequest { + if x, ok := m.GetValue().(*LogSyncContentValue_FullSyncRequest); ok { + return x.FullSyncRequest + } + return nil +} + +func (m *LogSyncContentValue) GetFullSyncResponse() *LogFullSyncResponse { + if x, ok := m.GetValue().(*LogSyncContentValue_FullSyncResponse); ok { + return x.FullSyncResponse + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*LogSyncContentValue) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*LogSyncContentValue_HeadUpdate)(nil), + (*LogSyncContentValue_FullSyncRequest)(nil), + (*LogSyncContentValue_FullSyncResponse)(nil), + } +} + +// LogSyncMessage is a message sent when we are syncing logs +type LogSyncMessage struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + Content *LogSyncContentValue `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` +} + +func (m *LogSyncMessage) Reset() { *m = LogSyncMessage{} } +func (m *LogSyncMessage) String() string { return proto.CompactTextString(m) } +func (*LogSyncMessage) ProtoMessage() {} +func (*LogSyncMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{11} +} +func (m *LogSyncMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogSyncMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogSyncMessage.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 *LogSyncMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogSyncMessage.Merge(m, src) +} +func (m *LogSyncMessage) XXX_Size() int { + return m.Size() +} +func (m *LogSyncMessage) XXX_DiscardUnknown() { + xxx_messageInfo_LogSyncMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_LogSyncMessage proto.InternalMessageInfo + +func (m *LogSyncMessage) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *LogSyncMessage) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *LogSyncMessage) GetContent() *LogSyncContentValue { + if m != nil { + return m.Content + } + return nil +} + +// LogHeadUpdate is a message sent on consensus log head update +type LogHeadUpdate struct { + Head string `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"` + Records []*RawRecordWithId `protobuf:"bytes,2,rep,name=records,proto3" json:"records,omitempty"` +} + +func (m *LogHeadUpdate) Reset() { *m = LogHeadUpdate{} } +func (m *LogHeadUpdate) String() string { return proto.CompactTextString(m) } +func (*LogHeadUpdate) ProtoMessage() {} +func (*LogHeadUpdate) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{12} +} +func (m *LogHeadUpdate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogHeadUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogHeadUpdate.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 *LogHeadUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogHeadUpdate.Merge(m, src) +} +func (m *LogHeadUpdate) XXX_Size() int { + return m.Size() +} +func (m *LogHeadUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_LogHeadUpdate.DiscardUnknown(m) +} + +var xxx_messageInfo_LogHeadUpdate proto.InternalMessageInfo + +func (m *LogHeadUpdate) GetHead() string { + if m != nil { + return m.Head + } + return "" +} + +func (m *LogHeadUpdate) GetRecords() []*RawRecordWithId { + if m != nil { + return m.Records + } + return nil +} + +// LogFullSyncRequest is a message sent when consensus log needs full sync +type LogFullSyncRequest struct { + Head string `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"` + Records []*RawRecordWithId `protobuf:"bytes,2,rep,name=records,proto3" json:"records,omitempty"` +} + +func (m *LogFullSyncRequest) Reset() { *m = LogFullSyncRequest{} } +func (m *LogFullSyncRequest) String() string { return proto.CompactTextString(m) } +func (*LogFullSyncRequest) ProtoMessage() {} +func (*LogFullSyncRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{13} +} +func (m *LogFullSyncRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogFullSyncRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogFullSyncRequest.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 *LogFullSyncRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogFullSyncRequest.Merge(m, src) +} +func (m *LogFullSyncRequest) XXX_Size() int { + return m.Size() +} +func (m *LogFullSyncRequest) XXX_DiscardUnknown() { + xxx_messageInfo_LogFullSyncRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_LogFullSyncRequest proto.InternalMessageInfo + +func (m *LogFullSyncRequest) GetHead() string { + if m != nil { + return m.Head + } + return "" +} + +func (m *LogFullSyncRequest) GetRecords() []*RawRecordWithId { + if m != nil { + return m.Records + } + return nil +} + +// LogFullSyncResponse is a message sent as a response for a specific full sync +type LogFullSyncResponse struct { + Head string `protobuf:"bytes,1,opt,name=head,proto3" json:"head,omitempty"` + Records []*RawRecordWithId `protobuf:"bytes,2,rep,name=records,proto3" json:"records,omitempty"` +} + +func (m *LogFullSyncResponse) Reset() { *m = LogFullSyncResponse{} } +func (m *LogFullSyncResponse) String() string { return proto.CompactTextString(m) } +func (*LogFullSyncResponse) ProtoMessage() {} +func (*LogFullSyncResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b8d7f1c16b400059, []int{14} +} +func (m *LogFullSyncResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogFullSyncResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogFullSyncResponse.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 *LogFullSyncResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogFullSyncResponse.Merge(m, src) +} +func (m *LogFullSyncResponse) XXX_Size() int { + return m.Size() +} +func (m *LogFullSyncResponse) XXX_DiscardUnknown() { + xxx_messageInfo_LogFullSyncResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_LogFullSyncResponse proto.InternalMessageInfo + +func (m *LogFullSyncResponse) GetHead() string { + if m != nil { + return m.Head + } + return "" +} + +func (m *LogFullSyncResponse) GetRecords() []*RawRecordWithId { + if m != nil { + return m.Records + } + return nil +} + +func init() { + proto.RegisterEnum("consensusProto.ErrCodes", ErrCodes_name, ErrCodes_value) + proto.RegisterType((*Log)(nil), "consensusProto.Log") + proto.RegisterType((*RawRecord)(nil), "consensusProto.RawRecord") + proto.RegisterType((*RawRecordWithId)(nil), "consensusProto.RawRecordWithId") + proto.RegisterType((*Record)(nil), "consensusProto.Record") + proto.RegisterType((*Ok)(nil), "consensusProto.Ok") + proto.RegisterType((*LogAddRequest)(nil), "consensusProto.LogAddRequest") + proto.RegisterType((*RecordAddRequest)(nil), "consensusProto.RecordAddRequest") + proto.RegisterType((*LogWatchRequest)(nil), "consensusProto.LogWatchRequest") + proto.RegisterType((*LogWatchEvent)(nil), "consensusProto.LogWatchEvent") + proto.RegisterType((*Err)(nil), "consensusProto.Err") + proto.RegisterType((*LogSyncContentValue)(nil), "consensusProto.LogSyncContentValue") + proto.RegisterType((*LogSyncMessage)(nil), "consensusProto.LogSyncMessage") + proto.RegisterType((*LogHeadUpdate)(nil), "consensusProto.LogHeadUpdate") + proto.RegisterType((*LogFullSyncRequest)(nil), "consensusProto.LogFullSyncRequest") + proto.RegisterType((*LogFullSyncResponse)(nil), "consensusProto.LogFullSyncResponse") +} + +func init() { + proto.RegisterFile("consensus/consensusproto/protos/consensus.proto", fileDescriptor_b8d7f1c16b400059) +} + +var fileDescriptor_b8d7f1c16b400059 = []byte{ + // 792 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x6f, 0xf3, 0x44, + 0x10, 0x8e, 0xed, 0x7c, 0x4e, 0x78, 0x13, 0x33, 0x2f, 0x42, 0x26, 0xa2, 0x69, 0x64, 0x2e, 0xa1, + 0x42, 0x29, 0x04, 0x21, 0x84, 0x2a, 0x84, 0x68, 0x94, 0x2a, 0x91, 0xd2, 0xb4, 0xb8, 0x2a, 0x95, + 0x40, 0x42, 0x72, 0xbd, 0x1b, 0xd7, 0x6a, 0xea, 0x35, 0xf6, 0x26, 0x6d, 0xae, 0x70, 0xe4, 0xc2, + 0x1f, 0xe0, 0xff, 0x70, 0xec, 0x91, 0x23, 0x6a, 0x2f, 0xfc, 0x00, 0x7e, 0x00, 0xf2, 0xfa, 0x23, + 0xa9, 0x93, 0x14, 0xd0, 0xdb, 0x4b, 0xb2, 0xf3, 0xf5, 0xcc, 0xcc, 0xb3, 0xb3, 0x63, 0xd8, 0xb7, + 0x98, 0x1b, 0x50, 0x37, 0x98, 0x05, 0xcb, 0x93, 0xe7, 0x33, 0xce, 0xf6, 0xc5, 0xef, 0x8a, 0xb6, + 0x23, 0x14, 0x58, 0x4b, 0x15, 0xa7, 0xa1, 0xac, 0x9f, 0x82, 0x32, 0x62, 0x36, 0xd6, 0x40, 0x76, + 0x88, 0x26, 0xb5, 0xa4, 0x76, 0xc5, 0x90, 0x1d, 0x82, 0x5f, 0x40, 0xc9, 0xa7, 0x16, 0xf3, 0x49, + 0xa0, 0x29, 0x2d, 0xa5, 0x5d, 0xed, 0xee, 0x76, 0x9e, 0x06, 0x76, 0x0c, 0xf3, 0xd6, 0x10, 0x1e, + 0x17, 0x0e, 0xbf, 0x1a, 0x12, 0x23, 0xf1, 0xd7, 0x7f, 0x93, 0xa0, 0x92, 0x1a, 0x51, 0x83, 0x92, + 0x67, 0x2e, 0xa6, 0xcc, 0x8c, 0xd0, 0xdf, 0x32, 0x12, 0x11, 0xdf, 0x87, 0x4a, 0xe0, 0xd8, 0xae, + 0xc9, 0x67, 0x3e, 0xd5, 0x64, 0x61, 0x5b, 0x2a, 0x70, 0x0f, 0x54, 0xd3, 0xb2, 0xa8, 0xc7, 0x99, + 0x3f, 0x24, 0xd4, 0xe5, 0x0e, 0x5f, 0x68, 0x8a, 0x70, 0x5a, 0xd3, 0xe3, 0x47, 0xf0, 0x76, 0xa2, + 0x3b, 0x4b, 0x11, 0xf3, 0xc2, 0x79, 0xdd, 0xa0, 0x1f, 0x40, 0x3d, 0x53, 0xfb, 0x33, 0x45, 0x46, + 0xbc, 0xc8, 0x09, 0x2f, 0xba, 0x0b, 0xc5, 0xb8, 0xb1, 0x77, 0xa1, 0xe8, 0xf9, 0x74, 0x3e, 0x4c, + 0x58, 0x8b, 0x25, 0x6c, 0x40, 0xd9, 0x49, 0x0a, 0x8e, 0xba, 0x4a, 0x65, 0x44, 0xc8, 0x13, 0x93, + 0x9b, 0x71, 0x23, 0xe2, 0x1c, 0xd2, 0xc0, 0x9d, 0x1b, 0x1a, 0x70, 0xf3, 0xc6, 0x13, 0x45, 0x2b, + 0xc6, 0x52, 0xa1, 0xe7, 0x41, 0x3e, 0xb9, 0xd6, 0x07, 0xf0, 0x6a, 0xc4, 0xec, 0xaf, 0x09, 0x31, + 0xe8, 0x8f, 0x33, 0x1a, 0x70, 0xfc, 0x1c, 0x8a, 0x11, 0xdd, 0x22, 0xf9, 0x7f, 0xb8, 0x9d, 0xd8, + 0x5d, 0xff, 0x1e, 0xd4, 0x48, 0xbf, 0x02, 0xf6, 0x0e, 0x14, 0xa6, 0xcc, 0x4e, 0x1b, 0x89, 0x04, + 0xfc, 0x24, 0x4d, 0x21, 0x8b, 0x14, 0xef, 0x6d, 0x4d, 0x91, 0x82, 0x1f, 0x43, 0x7d, 0xc4, 0xec, + 0x0b, 0x93, 0x5b, 0x57, 0x09, 0x76, 0x03, 0xca, 0xb7, 0xa1, 0x3c, 0x24, 0x81, 0x26, 0xb5, 0x94, + 0x76, 0xc5, 0x48, 0x65, 0x6c, 0x02, 0xcc, 0xdc, 0xd4, 0x2a, 0x0b, 0xeb, 0x8a, 0x46, 0xff, 0x45, + 0x12, 0x6d, 0x0b, 0xbc, 0xfe, 0x9c, 0xba, 0xdb, 0x2a, 0x5d, 0x99, 0x55, 0xf9, 0xff, 0xcd, 0x2a, + 0x7e, 0x08, 0x05, 0xea, 0xfb, 0xcc, 0x17, 0x37, 0x52, 0xed, 0xbe, 0xce, 0x06, 0xf6, 0x7d, 0xdf, + 0x88, 0x3c, 0xf4, 0xcf, 0x40, 0xe9, 0xfb, 0x3e, 0x76, 0x92, 0x88, 0xb0, 0x84, 0x5a, 0x57, 0xdb, + 0x10, 0xd1, 0x63, 0x84, 0x06, 0x49, 0xd8, 0x4f, 0x32, 0xbc, 0x1e, 0x31, 0xfb, 0x6c, 0xe1, 0x5a, + 0x3d, 0xe6, 0x72, 0xea, 0xf2, 0x6f, 0xcd, 0xe9, 0x8c, 0xe2, 0x57, 0x00, 0x57, 0xd4, 0x24, 0xe7, + 0x1e, 0x31, 0x39, 0x8d, 0x6f, 0x71, 0x27, 0x0b, 0x36, 0x62, 0xf6, 0x20, 0x75, 0x1a, 0xe4, 0x8c, + 0x95, 0x10, 0x1c, 0x43, 0x7d, 0x32, 0x9b, 0x4e, 0x43, 0xe0, 0x98, 0xec, 0xf8, 0xa2, 0xf4, 0x0d, + 0x28, 0x47, 0x4f, 0x3d, 0x07, 0x39, 0x23, 0x1b, 0x8c, 0xdf, 0x80, 0xba, 0x54, 0x05, 0x5e, 0x08, + 0x11, 0xb3, 0xf2, 0xc1, 0xb3, 0x80, 0x91, 0xeb, 0x20, 0x67, 0xac, 0x85, 0x1f, 0x96, 0xa0, 0x30, + 0x0f, 0x9b, 0xd5, 0x17, 0x50, 0x8b, 0x39, 0x38, 0xa6, 0x41, 0x60, 0xda, 0x74, 0x6d, 0xdf, 0xac, + 0xbc, 0x40, 0xf9, 0xe9, 0x0b, 0xfc, 0x12, 0x4a, 0x56, 0x44, 0xdc, 0x33, 0xe5, 0x64, 0xe9, 0x35, + 0x92, 0x18, 0xfd, 0x07, 0x31, 0x43, 0x4b, 0x16, 0xc3, 0x37, 0x18, 0xb2, 0x18, 0xe7, 0x16, 0xe7, + 0x37, 0x98, 0x20, 0xdd, 0x02, 0x5c, 0xe7, 0xf7, 0xa5, 0x93, 0x10, 0x31, 0x43, 0x59, 0xce, 0x5f, + 0x38, 0xcb, 0xde, 0xcf, 0x12, 0x94, 0x93, 0xf1, 0xc5, 0x1a, 0xc0, 0xb9, 0x4b, 0xef, 0x3c, 0x6a, + 0x71, 0x4a, 0xd4, 0x1c, 0xbe, 0x82, 0xca, 0x88, 0xd9, 0xfd, 0x3b, 0x27, 0xe0, 0x81, 0x2a, 0x61, + 0x1d, 0xaa, 0x23, 0x66, 0x8f, 0x19, 0x3f, 0x62, 0x33, 0x97, 0xa8, 0x32, 0x22, 0xd4, 0x22, 0xd4, + 0x1e, 0x73, 0x27, 0x53, 0xc7, 0xe2, 0xaa, 0x12, 0xc6, 0x1c, 0x31, 0xff, 0xd2, 0x21, 0x84, 0xba, + 0x6a, 0x3e, 0x74, 0x19, 0xba, 0x73, 0x73, 0xea, 0x90, 0xd3, 0xe8, 0x6e, 0xd5, 0x02, 0xaa, 0x50, + 0xed, 0x87, 0xef, 0xe4, 0x64, 0x32, 0x09, 0x28, 0x57, 0xff, 0x56, 0xba, 0x7f, 0x49, 0x50, 0xe9, + 0x25, 0x15, 0xe3, 0x01, 0x14, 0xa3, 0xcd, 0x87, 0x9b, 0x1e, 0xc7, 0x72, 0x89, 0x35, 0x30, 0x6b, + 0x3e, 0xb9, 0xc6, 0x31, 0x54, 0xd2, 0x65, 0x87, 0xad, 0x35, 0x1e, 0x32, 0x7b, 0xb0, 0xf1, 0x6f, + 0x4c, 0xe1, 0x18, 0xca, 0xc9, 0x3e, 0xc2, 0xdd, 0x0d, 0xe5, 0xac, 0x6e, 0xbe, 0xc6, 0xce, 0x36, + 0x07, 0xb1, 0xca, 0xda, 0xd2, 0xc7, 0xd2, 0x61, 0xf7, 0xf7, 0x87, 0xa6, 0x74, 0xff, 0xd0, 0x94, + 0xfe, 0x7c, 0x68, 0x4a, 0xbf, 0x3e, 0x36, 0x73, 0xf7, 0x8f, 0xcd, 0xdc, 0x1f, 0x8f, 0xcd, 0xdc, + 0x77, 0xda, 0xb6, 0xcf, 0xfa, 0x65, 0x51, 0xfc, 0x7d, 0xfa, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x3a, 0xf6, 0x39, 0x2c, 0xf9, 0x07, 0x00, 0x00, +} + +func (m *Log) 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 *Log) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Log) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Records) > 0 { + for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RawRecord) 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 *RawRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RawRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AcceptorSignature) > 0 { + i -= len(m.AcceptorSignature) + copy(dAtA[i:], m.AcceptorSignature) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.AcceptorSignature))) + i-- + dAtA[i] = 0x22 + } + if len(m.AcceptorIdentity) > 0 { + i -= len(m.AcceptorIdentity) + copy(dAtA[i:], m.AcceptorIdentity) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.AcceptorIdentity))) + i-- + dAtA[i] = 0x1a + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if len(m.Payload) > 0 { + i -= len(m.Payload) + copy(dAtA[i:], m.Payload) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Payload))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RawRecordWithId) 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 *RawRecordWithId) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RawRecordWithId) 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 = encodeVarintConsensus(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0x12 + } + if len(m.Payload) > 0 { + i -= len(m.Payload) + copy(dAtA[i:], m.Payload) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Payload))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Record) 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 *Record) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Record) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintConsensus(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x20 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0x12 + } + if len(m.PrevId) > 0 { + i -= len(m.PrevId) + copy(dAtA[i:], m.PrevId) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.PrevId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Ok) 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 *Ok) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Ok) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *LogAddRequest) 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 *LogAddRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Record != nil { + { + size, err := m.Record.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RecordAddRequest) 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 *RecordAddRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RecordAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Record != nil { + { + size, err := m.Record.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.LogId) > 0 { + i -= len(m.LogId) + copy(dAtA[i:], m.LogId) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.LogId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LogWatchRequest) 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 *LogWatchRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogWatchRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UnwatchIds) > 0 { + for iNdEx := len(m.UnwatchIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.UnwatchIds[iNdEx]) + copy(dAtA[i:], m.UnwatchIds[iNdEx]) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.UnwatchIds[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.WatchIds) > 0 { + for iNdEx := len(m.WatchIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.WatchIds[iNdEx]) + copy(dAtA[i:], m.WatchIds[iNdEx]) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.WatchIds[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *LogWatchEvent) 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 *LogWatchEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogWatchEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Error != nil { + { + size, err := m.Error.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Records) > 0 { + for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.LogId) > 0 { + i -= len(m.LogId) + copy(dAtA[i:], m.LogId) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.LogId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Err) 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 *Err) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Err) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Error != 0 { + i = encodeVarintConsensus(dAtA, i, uint64(m.Error)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *LogSyncContentValue) 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 *LogSyncContentValue) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogSyncContentValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Value != nil { + { + size := m.Value.Size() + i -= size + if _, err := m.Value.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *LogSyncContentValue_HeadUpdate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogSyncContentValue_HeadUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.HeadUpdate != nil { + { + size, err := m.HeadUpdate.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *LogSyncContentValue_FullSyncRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogSyncContentValue_FullSyncRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.FullSyncRequest != nil { + { + size, err := m.FullSyncRequest.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *LogSyncContentValue_FullSyncResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogSyncContentValue_FullSyncResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.FullSyncResponse != nil { + { + size, err := m.FullSyncResponse.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} +func (m *LogSyncMessage) 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 *LogSyncMessage) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogSyncMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Payload) > 0 { + i -= len(m.Payload) + copy(dAtA[i:], m.Payload) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Payload))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LogHeadUpdate) 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 *LogHeadUpdate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogHeadUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Records) > 0 { + for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Head) > 0 { + i -= len(m.Head) + copy(dAtA[i:], m.Head) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Head))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LogFullSyncRequest) 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 *LogFullSyncRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogFullSyncRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Records) > 0 { + for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Head) > 0 { + i -= len(m.Head) + copy(dAtA[i:], m.Head) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Head))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LogFullSyncResponse) 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 *LogFullSyncResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogFullSyncResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Records) > 0 { + for iNdEx := len(m.Records) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Records[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConsensus(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Head) > 0 { + i -= len(m.Head) + copy(dAtA[i:], m.Head) + i = encodeVarintConsensus(dAtA, i, uint64(len(m.Head))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintConsensus(dAtA []byte, offset int, v uint64) int { + offset -= sovConsensus(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Log) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if len(m.Records) > 0 { + for _, e := range m.Records { + l = e.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + } + return n +} + +func (m *RawRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Payload) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + l = len(m.AcceptorIdentity) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + l = len(m.AcceptorSignature) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} + +func (m *RawRecordWithId) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Payload) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + l = len(m.Id) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} + +func (m *Record) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PrevId) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovConsensus(uint64(m.Timestamp)) + } + return n +} + +func (m *Ok) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *LogAddRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Record != nil { + l = m.Record.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} + +func (m *RecordAddRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.LogId) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if m.Record != nil { + l = m.Record.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} + +func (m *LogWatchRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.WatchIds) > 0 { + for _, s := range m.WatchIds { + l = len(s) + n += 1 + l + sovConsensus(uint64(l)) + } + } + if len(m.UnwatchIds) > 0 { + for _, s := range m.UnwatchIds { + l = len(s) + n += 1 + l + sovConsensus(uint64(l)) + } + } + return n +} + +func (m *LogWatchEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.LogId) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if len(m.Records) > 0 { + for _, e := range m.Records { + l = e.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + } + if m.Error != nil { + l = m.Error.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} + +func (m *Err) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Error != 0 { + n += 1 + sovConsensus(uint64(m.Error)) + } + return n +} + +func (m *LogSyncContentValue) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Value != nil { + n += m.Value.Size() + } + return n +} + +func (m *LogSyncContentValue_HeadUpdate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HeadUpdate != nil { + l = m.HeadUpdate.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} +func (m *LogSyncContentValue_FullSyncRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FullSyncRequest != nil { + l = m.FullSyncRequest.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} +func (m *LogSyncContentValue_FullSyncResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FullSyncResponse != nil { + l = m.FullSyncResponse.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} +func (m *LogSyncMessage) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + l = len(m.Payload) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + return n +} + +func (m *LogHeadUpdate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Head) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if len(m.Records) > 0 { + for _, e := range m.Records { + l = e.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + } + return n +} + +func (m *LogFullSyncRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Head) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if len(m.Records) > 0 { + for _, e := range m.Records { + l = e.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + } + return n +} + +func (m *LogFullSyncResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Head) + if l > 0 { + n += 1 + l + sovConsensus(uint64(l)) + } + if len(m.Records) > 0 { + for _, e := range m.Records { + l = e.Size() + n += 1 + l + sovConsensus(uint64(l)) + } + } + return n +} + +func sovConsensus(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozConsensus(x uint64) (n int) { + return sovConsensus(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Log) 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 ErrIntOverflowConsensus + } + 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: Log: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Log: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + 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 ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Records = append(m.Records, &RawRecordWithId{}) + if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RawRecord) 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 ErrIntOverflowConsensus + } + 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: RawRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RawRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Payload = append(m.Payload[:0], dAtA[iNdEx:postIndex]...) + if m.Payload == nil { + m.Payload = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AcceptorIdentity", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AcceptorIdentity = append(m.AcceptorIdentity[:0], dAtA[iNdEx:postIndex]...) + if m.AcceptorIdentity == nil { + m.AcceptorIdentity = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AcceptorSignature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AcceptorSignature = append(m.AcceptorSignature[:0], dAtA[iNdEx:postIndex]...) + if m.AcceptorSignature == nil { + m.AcceptorSignature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RawRecordWithId) 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 ErrIntOverflowConsensus + } + 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: RawRecordWithId: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RawRecordWithId: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Payload = append(m.Payload[:0], dAtA[iNdEx:postIndex]...) + if m.Payload == nil { + m.Payload = []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 ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Record) 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 ErrIntOverflowConsensus + } + 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: Record: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Record: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PrevId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PrevId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = append(m.Identity[:0], dAtA[iNdEx:postIndex]...) + if m.Identity == nil { + m.Identity = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Ok) 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 ErrIntOverflowConsensus + } + 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: Ok: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Ok: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogAddRequest) 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 ErrIntOverflowConsensus + } + 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: LogAddRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogAddRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Record", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Record == nil { + m.Record = &RawRecordWithId{} + } + if err := m.Record.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RecordAddRequest) 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 ErrIntOverflowConsensus + } + 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: RecordAddRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RecordAddRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LogId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LogId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Record", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Record == nil { + m.Record = &RawRecord{} + } + if err := m.Record.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogWatchRequest) 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 ErrIntOverflowConsensus + } + 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: LogWatchRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogWatchRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WatchIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WatchIds = append(m.WatchIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnwatchIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UnwatchIds = append(m.UnwatchIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogWatchEvent) 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 ErrIntOverflowConsensus + } + 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: LogWatchEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogWatchEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LogId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LogId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Records = append(m.Records, &RawRecordWithId{}) + if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Error == nil { + m.Error = &Err{} + } + if err := m.Error.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Err) 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 ErrIntOverflowConsensus + } + 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: Err: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Err: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + m.Error = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Error |= ErrCodes(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogSyncContentValue) 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 ErrIntOverflowConsensus + } + 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: LogSyncContentValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogSyncContentValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HeadUpdate", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &LogHeadUpdate{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &LogSyncContentValue_HeadUpdate{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FullSyncRequest", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &LogFullSyncRequest{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &LogSyncContentValue_FullSyncRequest{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FullSyncResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &LogFullSyncResponse{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &LogSyncContentValue_FullSyncResponse{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogSyncMessage) 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 ErrIntOverflowConsensus + } + 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: LogSyncMessage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogSyncMessage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + 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 ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Payload = append(m.Payload[:0], dAtA[iNdEx:postIndex]...) + if m.Payload == nil { + m.Payload = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &LogSyncContentValue{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogHeadUpdate) 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 ErrIntOverflowConsensus + } + 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: LogHeadUpdate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogHeadUpdate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Head", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Head = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Records = append(m.Records, &RawRecordWithId{}) + if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogFullSyncRequest) 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 ErrIntOverflowConsensus + } + 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: LogFullSyncRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogFullSyncRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Head", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Head = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Records = append(m.Records, &RawRecordWithId{}) + if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogFullSyncResponse) 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 ErrIntOverflowConsensus + } + 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: LogFullSyncResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogFullSyncResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Head", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + 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 ErrInvalidLengthConsensus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Head = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Records", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConsensus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConsensus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConsensus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Records = append(m.Records, &RawRecordWithId{}) + if err := m.Records[len(m.Records)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConsensus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConsensus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipConsensus(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowConsensus + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowConsensus + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowConsensus + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthConsensus + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupConsensus + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthConsensus + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthConsensus = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowConsensus = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupConsensus = fmt.Errorf("proto: unexpected end of group") +) diff --git a/consensus/consensusproto/consensus_drpc.pb.go b/consensus/consensusproto/consensus_drpc.pb.go new file mode 100644 index 00000000..b0b00dad --- /dev/null +++ b/consensus/consensusproto/consensus_drpc.pb.go @@ -0,0 +1,232 @@ +// Code generated by protoc-gen-go-drpc. DO NOT EDIT. +// protoc-gen-go-drpc version: v0.0.33 +// source: consensus/consensusproto/protos/consensus.proto + +package consensusproto + +import ( + bytes "bytes" + context "context" + errors "errors" + jsonpb "github.com/gogo/protobuf/jsonpb" + proto "github.com/gogo/protobuf/proto" + drpc "storj.io/drpc" + drpcerr "storj.io/drpc/drpcerr" +) + +type drpcEncoding_File_consensus_consensusproto_protos_consensus_proto struct{} + +func (drpcEncoding_File_consensus_consensusproto_protos_consensus_proto) Marshal(msg drpc.Message) ([]byte, error) { + return proto.Marshal(msg.(proto.Message)) +} + +func (drpcEncoding_File_consensus_consensusproto_protos_consensus_proto) Unmarshal(buf []byte, msg drpc.Message) error { + return proto.Unmarshal(buf, msg.(proto.Message)) +} + +func (drpcEncoding_File_consensus_consensusproto_protos_consensus_proto) JSONMarshal(msg drpc.Message) ([]byte, error) { + var buf bytes.Buffer + err := new(jsonpb.Marshaler).Marshal(&buf, msg.(proto.Message)) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (drpcEncoding_File_consensus_consensusproto_protos_consensus_proto) JSONUnmarshal(buf []byte, msg drpc.Message) error { + return jsonpb.Unmarshal(bytes.NewReader(buf), msg.(proto.Message)) +} + +type DRPCConsensusClient interface { + DRPCConn() drpc.Conn + + LogAdd(ctx context.Context, in *LogAddRequest) (*Ok, error) + RecordAdd(ctx context.Context, in *RecordAddRequest) (*RawRecordWithId, error) + LogWatch(ctx context.Context) (DRPCConsensus_LogWatchClient, error) +} + +type drpcConsensusClient struct { + cc drpc.Conn +} + +func NewDRPCConsensusClient(cc drpc.Conn) DRPCConsensusClient { + return &drpcConsensusClient{cc} +} + +func (c *drpcConsensusClient) DRPCConn() drpc.Conn { return c.cc } + +func (c *drpcConsensusClient) LogAdd(ctx context.Context, in *LogAddRequest) (*Ok, error) { + out := new(Ok) + err := c.cc.Invoke(ctx, "/consensusProto.Consensus/LogAdd", drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}, in, out) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *drpcConsensusClient) RecordAdd(ctx context.Context, in *RecordAddRequest) (*RawRecordWithId, error) { + out := new(RawRecordWithId) + err := c.cc.Invoke(ctx, "/consensusProto.Consensus/RecordAdd", drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}, in, out) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *drpcConsensusClient) LogWatch(ctx context.Context) (DRPCConsensus_LogWatchClient, error) { + stream, err := c.cc.NewStream(ctx, "/consensusProto.Consensus/LogWatch", drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}) + if err != nil { + return nil, err + } + x := &drpcConsensus_LogWatchClient{stream} + return x, nil +} + +type DRPCConsensus_LogWatchClient interface { + drpc.Stream + Send(*LogWatchRequest) error + Recv() (*LogWatchEvent, error) +} + +type drpcConsensus_LogWatchClient struct { + drpc.Stream +} + +func (x *drpcConsensus_LogWatchClient) GetStream() drpc.Stream { + return x.Stream +} + +func (x *drpcConsensus_LogWatchClient) Send(m *LogWatchRequest) error { + return x.MsgSend(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}) +} + +func (x *drpcConsensus_LogWatchClient) Recv() (*LogWatchEvent, error) { + m := new(LogWatchEvent) + if err := x.MsgRecv(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}); err != nil { + return nil, err + } + return m, nil +} + +func (x *drpcConsensus_LogWatchClient) RecvMsg(m *LogWatchEvent) error { + return x.MsgRecv(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}) +} + +type DRPCConsensusServer interface { + LogAdd(context.Context, *LogAddRequest) (*Ok, error) + RecordAdd(context.Context, *RecordAddRequest) (*RawRecordWithId, error) + LogWatch(DRPCConsensus_LogWatchStream) error +} + +type DRPCConsensusUnimplementedServer struct{} + +func (s *DRPCConsensusUnimplementedServer) LogAdd(context.Context, *LogAddRequest) (*Ok, error) { + return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) +} + +func (s *DRPCConsensusUnimplementedServer) RecordAdd(context.Context, *RecordAddRequest) (*RawRecordWithId, error) { + return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) +} + +func (s *DRPCConsensusUnimplementedServer) LogWatch(DRPCConsensus_LogWatchStream) error { + return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) +} + +type DRPCConsensusDescription struct{} + +func (DRPCConsensusDescription) NumMethods() int { return 3 } + +func (DRPCConsensusDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) { + switch n { + case 0: + return "/consensusProto.Consensus/LogAdd", drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}, + func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) { + return srv.(DRPCConsensusServer). + LogAdd( + ctx, + in1.(*LogAddRequest), + ) + }, DRPCConsensusServer.LogAdd, true + case 1: + return "/consensusProto.Consensus/RecordAdd", drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}, + func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) { + return srv.(DRPCConsensusServer). + RecordAdd( + ctx, + in1.(*RecordAddRequest), + ) + }, DRPCConsensusServer.RecordAdd, true + case 2: + return "/consensusProto.Consensus/LogWatch", drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}, + func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) { + return nil, srv.(DRPCConsensusServer). + LogWatch( + &drpcConsensus_LogWatchStream{in1.(drpc.Stream)}, + ) + }, DRPCConsensusServer.LogWatch, true + default: + return "", nil, nil, nil, false + } +} + +func DRPCRegisterConsensus(mux drpc.Mux, impl DRPCConsensusServer) error { + return mux.Register(impl, DRPCConsensusDescription{}) +} + +type DRPCConsensus_LogAddStream interface { + drpc.Stream + SendAndClose(*Ok) error +} + +type drpcConsensus_LogAddStream struct { + drpc.Stream +} + +func (x *drpcConsensus_LogAddStream) SendAndClose(m *Ok) error { + if err := x.MsgSend(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}); err != nil { + return err + } + return x.CloseSend() +} + +type DRPCConsensus_RecordAddStream interface { + drpc.Stream + SendAndClose(*RawRecordWithId) error +} + +type drpcConsensus_RecordAddStream struct { + drpc.Stream +} + +func (x *drpcConsensus_RecordAddStream) SendAndClose(m *RawRecordWithId) error { + if err := x.MsgSend(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}); err != nil { + return err + } + return x.CloseSend() +} + +type DRPCConsensus_LogWatchStream interface { + drpc.Stream + Send(*LogWatchEvent) error + Recv() (*LogWatchRequest, error) +} + +type drpcConsensus_LogWatchStream struct { + drpc.Stream +} + +func (x *drpcConsensus_LogWatchStream) Send(m *LogWatchEvent) error { + return x.MsgSend(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}) +} + +func (x *drpcConsensus_LogWatchStream) Recv() (*LogWatchRequest, error) { + m := new(LogWatchRequest) + if err := x.MsgRecv(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}); err != nil { + return nil, err + } + return m, nil +} + +func (x *drpcConsensus_LogWatchStream) RecvMsg(m *LogWatchRequest) error { + return x.MsgRecv(m, drpcEncoding_File_consensus_consensusproto_protos_consensus_proto{}) +} diff --git a/consensus/consensusproto/consensuserr/errors.go b/consensus/consensusproto/consensuserr/errors.go new file mode 100644 index 00000000..4bc59139 --- /dev/null +++ b/consensus/consensusproto/consensuserr/errors.go @@ -0,0 +1,18 @@ +package consensuserr + +import ( + "fmt" + "github.com/anyproto/any-sync/consensus/consensusproto" + "github.com/anyproto/any-sync/net/rpc/rpcerr" +) + +var ( + errGroup = rpcerr.ErrGroup(consensusproto.ErrCodes_ErrorOffset) + + ErrUnexpected = errGroup.Register(fmt.Errorf("unexpected consensus error"), uint64(consensusproto.ErrCodes_Unexpected)) + ErrConflict = errGroup.Register(fmt.Errorf("records conflict"), uint64(consensusproto.ErrCodes_RecordConflict)) + ErrLogExists = errGroup.Register(fmt.Errorf("log exists"), uint64(consensusproto.ErrCodes_LogExists)) + ErrLogNotFound = errGroup.Register(fmt.Errorf("log not found"), uint64(consensusproto.ErrCodes_LogNotFound)) + ErrForbidden = errGroup.Register(fmt.Errorf("forbidden"), uint64(consensusproto.ErrCodes_Forbidden)) + ErrInvalidPayload = errGroup.Register(fmt.Errorf("invalid payload"), uint64(consensusproto.ErrCodes_InvalidPayload)) +) diff --git a/consensus/consensusproto/protos/consensus.proto b/consensus/consensusproto/protos/consensus.proto new file mode 100644 index 00000000..7af9316f --- /dev/null +++ b/consensus/consensusproto/protos/consensus.proto @@ -0,0 +1,113 @@ +syntax = "proto3"; +package consensusProto; + +option go_package = "consensus/consensusproto"; + +enum ErrCodes { + Unexpected = 0; + LogExists = 1; + LogNotFound = 2; + RecordConflict = 3; + Forbidden = 4; + InvalidPayload = 5; + ErrorOffset = 500; +} + + +message Log { + string id = 1; + repeated RawRecordWithId records = 3; +} + +// RawRecord is a proto message containing the payload in bytes, signature of the account who added it and signature of the acceptor +message RawRecord { + bytes payload = 1; + bytes signature = 2; + bytes acceptorIdentity = 3; + bytes acceptorSignature = 4; +} + +// RawRecordWithId is a raw record and the id for convenience +message RawRecordWithId { + bytes payload = 1; + string id = 2; +} + +// Record is a record containing a data +message Record { + string prevId = 1; + bytes identity = 2; + bytes data = 3; + int64 timestamp = 4; +} + + +service Consensus { + // AddLog adds new log to consensus + rpc LogAdd(LogAddRequest) returns (Ok); + // AddRecord adds new record to log + rpc RecordAdd(RecordAddRequest) returns (RawRecordWithId); + // WatchLog fetches log and subscribes for a changes + rpc LogWatch(stream LogWatchRequest) returns (stream LogWatchEvent); +} + +message Ok {} + +message LogAddRequest { + // first record in the log, consensus node not sign it + RawRecordWithId record = 1; +} + +message RecordAddRequest { + string logId = 1; + RawRecord record = 2; +} + +message LogWatchRequest { + repeated string watchIds = 1; + repeated string unwatchIds = 2; +} + +message LogWatchEvent { + string logId = 1; + repeated RawRecordWithId records = 2; + Err error = 3; +} + +message Err { + ErrCodes error = 1; +} + +// LogSyncContentValue provides different types for log sync +message LogSyncContentValue { + oneof value { + LogHeadUpdate headUpdate = 1; + LogFullSyncRequest fullSyncRequest = 2; + LogFullSyncResponse fullSyncResponse = 3; + } +} + +// LogSyncMessage is a message sent when we are syncing logs +message LogSyncMessage { + string id = 1; + bytes payload = 2; + LogSyncContentValue content = 3; +} + +// LogHeadUpdate is a message sent on consensus log head update +message LogHeadUpdate { + string head = 1; + repeated RawRecordWithId records = 2; +} + +// LogFullSyncRequest is a message sent when consensus log needs full sync +message LogFullSyncRequest { + string head = 1; + repeated RawRecordWithId records = 2; +} + +// LogFullSyncResponse is a message sent as a response for a specific full sync +message LogFullSyncResponse { + string head = 1; + repeated RawRecordWithId records = 2; +} diff --git a/coordinator/coordinatorclient/mock_coordinatorclient/mock_coordinatorclient.go b/coordinator/coordinatorclient/mock_coordinatorclient/mock_coordinatorclient.go index d2d83159..c94189e9 100644 --- a/coordinator/coordinatorclient/mock_coordinatorclient/mock_coordinatorclient.go +++ b/coordinator/coordinatorclient/mock_coordinatorclient/mock_coordinatorclient.go @@ -12,7 +12,7 @@ import ( treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto" coordinatorclient "github.com/anyproto/any-sync/coordinator/coordinatorclient" coordinatorproto "github.com/anyproto/any-sync/coordinator/coordinatorproto" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockCoordinatorClient is a mock of CoordinatorClient interface. diff --git a/go.mod b/go.mod index 116ef5df..aa746768 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/gobwas/glob v0.2.3 github.com/goccy/go-graphviz v0.1.1 github.com/gogo/protobuf v1.3.2 - github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/hashicorp/yamux v0.1.1 github.com/huandu/skiplist v1.2.0 @@ -22,30 +21,29 @@ require ( github.com/ipfs/go-ipfs-blockstore v1.3.1 github.com/ipfs/go-ipfs-chunker v0.0.6 github.com/ipfs/go-ipfs-exchange-interface v0.2.1 - github.com/ipfs/go-ipld-format v0.4.0 - github.com/ipfs/go-merkledag v0.10.0 + github.com/ipfs/go-ipld-format v0.5.0 + github.com/ipfs/go-merkledag v0.11.0 github.com/ipfs/go-unixfs v0.4.6 - github.com/libp2p/go-libp2p v0.27.5 + github.com/libp2p/go-libp2p v0.29.0 github.com/mr-tron/base58 v1.2.0 github.com/multiformats/go-multibase v0.2.0 - github.com/multiformats/go-multihash v0.2.2 - github.com/prometheus/client_golang v1.15.1 + github.com/multiformats/go-multihash v0.2.3 + github.com/prometheus/client_golang v1.16.0 github.com/stretchr/testify v1.8.4 github.com/tyler-smith/go-bip39 v1.1.0 github.com/zeebo/blake3 v0.2.3 - github.com/zeebo/errs v1.3.0 go.uber.org/atomic v1.11.0 + go.uber.org/mock v0.2.0 go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.9.0 - golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 - golang.org/x/net v0.10.0 + golang.org/x/crypto v0.11.0 + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 + golang.org/x/net v0.12.0 gopkg.in/yaml.v3 v3.0.1 storj.io/drpc v0.0.33 ) require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect @@ -55,12 +53,9 @@ require ( github.com/fogleman/gg v1.3.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/huin/goupnp v1.2.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect @@ -68,8 +63,7 @@ require ( github.com/ipfs/go-ipfs-files v0.2.0 // indirect github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect github.com/ipfs/go-ipfs-util v0.0.2 // indirect - github.com/ipfs/go-ipld-cbor v0.0.6 // indirect - github.com/ipfs/go-ipld-legacy v0.1.1 // indirect + github.com/ipfs/go-ipld-legacy v0.2.1 // indirect github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/ipfs/go-metrics-interface v0.0.1 // indirect @@ -78,43 +72,33 @@ require ( github.com/ipld/go-ipld-prime v0.20.0 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect - github.com/klauspost/compress v1.16.5 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.54 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.9.0 // indirect + github.com/multiformats/go-multiaddr v0.10.1 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo/v2 v2.9.7 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.10.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.2 // indirect - github.com/quic-go/qtls-go1-20 v0.2.2 // indirect - github.com/quic-go/quic-go v0.35.1 // indirect - github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158 // indirect github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect + github.com/zeebo/errs v1.3.0 // indirect go.opentelemetry.io/otel v1.7.0 // indirect go.opentelemetry.io/otel/trace v1.7.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/image v0.6.0 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/tools v0.9.3 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.10.0 // indirect google.golang.org/protobuf v1.30.0 // indirect lukechampine.com/blake3 v1.2.1 // indirect ) diff --git a/go.sum b/go.sum index 5f84c9c4..28c4b775 100644 --- a/go.sum +++ b/go.sum @@ -13,7 +13,6 @@ github.com/anyproto/go-slip21 v1.0.0 h1:CI7lUqTIwmPOEGVAj4jyNLoICvueh++0U2HoAi3m github.com/anyproto/go-slip21 v1.0.0/go.mod h1:gbIJt7HAdr5DuT4f2pFTKCBSUWYsm/fysHBNqgsuxT0= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= -github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -38,7 +37,6 @@ github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -47,7 +45,6 @@ github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-graphviz v0.1.1 h1:MGrsnzBxTyt7KG8FhHsFPDTGvF7UaQMmSa6A610DqPg= @@ -57,27 +54,20 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= -github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3 h1:2XF1Vzq06X+inNqgJ9tRnGuw+ZVCB3FazXODD6JE1R8= -github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= -github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= -github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= @@ -89,24 +79,18 @@ github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0Jr github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= -github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= github.com/ipfs/go-block-format v0.1.2 h1:GAjkfhVx1f4YTODS6Esrj1wt2HhrtwTnhEr+DyPUaJo= github.com/ipfs/go-block-format v0.1.2/go.mod h1:mACVcrxarQKstUU3Yf/RdwbC4DzPV6++rO2a3d+a/KE= github.com/ipfs/go-blockservice v0.5.2 h1:in9Bc+QcXwd1apOVM7Un9t8tixPKdaHQFdLSUM1Xgk8= github.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= @@ -135,21 +119,18 @@ github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9 github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipld-cbor v0.0.6 h1:pYuWHyvSpIsOOLw4Jy7NbBkCyzLDcl64Bf/LZW7eBQ0= -github.com/ipfs/go-ipld-cbor v0.0.6/go.mod h1:ssdxxaLJPXH7OjF5V4NSjBbcfh+evoR4ukuru0oPXMA= github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= -github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= -github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSghBlQ= -github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2cdcc= -github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= +github.com/ipfs/go-ipld-format v0.5.0 h1:WyEle9K96MSrvr47zZHKKcDxJ/vlpET6PSiQsAFO+Ds= +github.com/ipfs/go-ipld-format v0.5.0/go.mod h1:ImdZqJQaEouMjCvqCe0ORUS+uoBmf7Hf+EO/jh+nk3M= +github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk= +github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipfs/go-merkledag v0.10.0 h1:IUQhj/kzTZfam4e+LnaEpoiZ9vZF6ldimVlby+6OXL4= -github.com/ipfs/go-merkledag v0.10.0/go.mod h1:zkVav8KiYlmbzUzNM6kENzkdP5+qR7+2mCwxkQ6GIj8= +github.com/ipfs/go-merkledag v0.11.0 h1:DgzwK5hprESOzS4O1t/wi6JDpyVQdvm9Bs59N/jqfBY= +github.com/ipfs/go-merkledag v0.11.0/go.mod h1:Q4f/1ezvBiJV0YCIXvt51W/9/kqJGH4I1LsA7+djsM4= github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= github.com/ipfs/go-peertaskqueue v0.8.0 h1:JyNO144tfu9bx6Hpo119zvbEL9iQ760FHOiJYsUjqaU= @@ -159,7 +140,6 @@ github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2 github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= @@ -168,90 +148,73 @@ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABo github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= -github.com/libp2p/go-libp2p v0.27.5 h1:KwA7pXKXpz8hG6Cr1fMA7UkgleogcwQj0sxl5qquWRg= -github.com/libp2p/go-libp2p v0.27.5/go.mod h1:oMfQGTb9CHnrOuSM6yMmyK2lXz3qIhnkn2+oK3B1Y2g= +github.com/libp2p/go-libp2p v0.29.0 h1:QduJ2XQr/Crg4EnloueWDL0Jj86N3Ezhyyj7XH+XwHI= +github.com/libp2p/go-libp2p v0.29.0/go.mod h1:iNKL7mEnZ9wAss+03IjAwM9ZAQXfVUAPUUmOACQfQ/g= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= +github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= -github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= +github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= -github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= -github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= -github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= +github.com/multiformats/go-multiaddr v0.10.1 h1:HghtFrWyZEPrpTvgAMFJi6gFdgHfs2cb0pyfDsk+lqU= +github.com/multiformats/go-multiaddr v0.10.1/go.mod h1:jLEZsA61rwWNZQTHHnqq2HNa+4os/Hz54eqiRnsRqYQ= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= -github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= -github.com/multiformats/go-multihash v0.2.2 h1:Uu7LWs/PmWby1gkj1S1DXx3zyd3aVabA4FiMKn/2tAc= -github.com/multiformats/go-multihash v0.2.2/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -259,43 +222,31 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.10.0 h1:UkG7GPYkO4UZyLnyXjaWYcgOSONqwdBqFUT95ugmt6I= -github.com/prometheus/procfs v0.10.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= -github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= -github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.34.0 h1:OvOJ9LFjTySgwOTYUZmNoq0FzVicP8YujpV0kB7m2lU= -github.com/quic-go/quic-go v0.34.0/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= -github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo= -github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= +github.com/quic-go/quic-go v0.36.2 h1:ZX/UNQ4gvpCv2RmwdbA6lrRjF6EBm5yZ7TMoT4NQVrA= github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= -github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -303,11 +254,7 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/warpfork/go-testmark v0.11.0 h1:J6LnV8KpceDvo7spaNU4+DauH2n1x+6RaO2rJrmpQ9U= -github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158 h1:WXhVOwj2USAXB5oMDwRl3piOux2XMV9TANaYxXHdkoE= -github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -332,6 +279,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= +go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -348,12 +297,11 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -363,8 +311,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -374,8 +321,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -383,28 +330,24 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -413,28 +356,22 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= diff --git a/metric/drpc.go b/metric/drpc.go index b81ed863..23640a8c 100644 --- a/metric/drpc.go +++ b/metric/drpc.go @@ -2,8 +2,10 @@ package metric import ( "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" "storj.io/drpc" "time" + "unicode/utf8" ) type prometheusDRPC struct { @@ -14,7 +16,11 @@ type prometheusDRPC struct { func (ph *prometheusDRPC) HandleRPC(stream drpc.Stream, rpc string) (err error) { st := time.Now() defer func() { - ph.SummaryVec.WithLabelValues(rpc).Observe(time.Since(st).Seconds()) + if utf8.ValidString(rpc) { + ph.SummaryVec.WithLabelValues(rpc).Observe(time.Since(st).Seconds()) + } else { + log.WarnCtx(stream.Context(), "invalid rpc string", zap.String("rpc", rpc)) + } }() return ph.Handler.HandleRPC(stream, rpc) } diff --git a/metric/log.go b/metric/log.go index e854a82a..0fd487c2 100644 --- a/metric/log.go +++ b/metric/log.go @@ -31,6 +31,10 @@ func PeerId(val string) zap.Field { return zap.String("peerId", val) } +func PeerVersion(val string) zap.Field { + return zap.String("peerVersion", val) +} + func Identity(val string) zap.Field { return zap.String("identity", val) } @@ -61,5 +65,5 @@ func (m *metric) RequestLog(ctx context.Context, rpc string, fields ...zap.Field if ak != nil { acc = ak.Account() } - m.rpcLog.Info("", append(fields, m.appField, PeerId(peerId), Identity(acc), Method(rpc))...) + m.rpcLog.Info("", append(fields, m.appField, PeerId(peerId), Identity(acc), Method(rpc), PeerVersion(peer.CtxPeerClientVersion(ctx)))...) } diff --git a/net/config.go b/net/config.go index 333261dd..64863419 100644 --- a/net/config.go +++ b/net/config.go @@ -5,21 +5,3 @@ import "errors" var ( ErrUnableToConnect = errors.New("unable to connect") ) - -type ConfigGetter interface { - GetNet() Config -} - -type Config struct { - Server ServerConfig `yaml:"server"` - Stream StreamConfig `yaml:"stream"` -} - -type ServerConfig struct { - ListenAddrs []string `yaml:"listenAddrs"` -} - -type StreamConfig struct { - TimeoutMilliseconds int `yaml:"timeoutMilliseconds"` - MaxMsgSizeMb int `yaml:"maxMsgSizeMb"` -} diff --git a/net/peer/context.go b/net/peer/context.go index ada3bab2..856f5cff 100644 --- a/net/peer/context.go +++ b/net/peer/context.go @@ -14,6 +14,7 @@ const ( contextKeyPeerId contextKey = iota contextKeyIdentity contextKeyPeerAddr + contextKeyPeerClientVersion ) var ( @@ -50,6 +51,19 @@ func CtxWithPeerAddr(ctx context.Context, addr string) context.Context { return context.WithValue(ctx, contextKeyPeerAddr, addr) } +// CtxPeerClientVersion returns peer client version +func CtxPeerClientVersion(ctx context.Context) string { + if p, ok := ctx.Value(contextKeyPeerClientVersion).(string); ok { + return p + } + return "" +} + +// CtxWithClientVersion sets peer clientVersion to the context +func CtxWithClientVersion(ctx context.Context, addr string) context.Context { + return context.WithValue(ctx, contextKeyPeerClientVersion, addr) +} + // CtxIdentity returns identity from context func CtxIdentity(ctx context.Context) ([]byte, error) { if identity, ok := ctx.Value(contextKeyIdentity).([]byte); ok { diff --git a/net/peer/limiter.go b/net/peer/limiter.go new file mode 100644 index 00000000..4c8e1589 --- /dev/null +++ b/net/peer/limiter.go @@ -0,0 +1,18 @@ +package peer + +import ( + "time" +) + +type limiter struct { + startThreshold int + slowDownStep time.Duration +} + +func (l limiter) wait(count int) <-chan time.Time { + if count > l.startThreshold { + wait := l.slowDownStep * time.Duration(count-l.startThreshold) + return time.After(wait) + } + return nil +} diff --git a/net/peer/mock_peer/mock_peer.go b/net/peer/mock_peer/mock_peer.go new file mode 100644 index 00000000..5d6531b4 --- /dev/null +++ b/net/peer/mock_peer/mock_peer.go @@ -0,0 +1,149 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/anyproto/any-sync/net/peer (interfaces: Peer) + +// Package mock_peer is a generated GoMock package. +package mock_peer + +import ( + context "context" + reflect "reflect" + time "time" + + gomock "go.uber.org/mock/gomock" + drpc "storj.io/drpc" +) + +// MockPeer is a mock of Peer interface. +type MockPeer struct { + ctrl *gomock.Controller + recorder *MockPeerMockRecorder +} + +// MockPeerMockRecorder is the mock recorder for MockPeer. +type MockPeerMockRecorder struct { + mock *MockPeer +} + +// NewMockPeer creates a new mock instance. +func NewMockPeer(ctrl *gomock.Controller) *MockPeer { + mock := &MockPeer{ctrl: ctrl} + mock.recorder = &MockPeerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPeer) EXPECT() *MockPeerMockRecorder { + return m.recorder +} + +// AcquireDrpcConn mocks base method. +func (m *MockPeer) AcquireDrpcConn(arg0 context.Context) (drpc.Conn, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AcquireDrpcConn", arg0) + ret0, _ := ret[0].(drpc.Conn) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AcquireDrpcConn indicates an expected call of AcquireDrpcConn. +func (mr *MockPeerMockRecorder) AcquireDrpcConn(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcquireDrpcConn", reflect.TypeOf((*MockPeer)(nil).AcquireDrpcConn), arg0) +} + +// Close mocks base method. +func (m *MockPeer) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockPeerMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPeer)(nil).Close)) +} + +// Context mocks base method. +func (m *MockPeer) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context. +func (mr *MockPeerMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockPeer)(nil).Context)) +} + +// DoDrpc mocks base method. +func (m *MockPeer) DoDrpc(arg0 context.Context, arg1 func(drpc.Conn) error) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DoDrpc", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DoDrpc indicates an expected call of DoDrpc. +func (mr *MockPeerMockRecorder) DoDrpc(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DoDrpc", reflect.TypeOf((*MockPeer)(nil).DoDrpc), arg0, arg1) +} + +// Id mocks base method. +func (m *MockPeer) Id() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Id") + ret0, _ := ret[0].(string) + return ret0 +} + +// Id indicates an expected call of Id. +func (mr *MockPeerMockRecorder) Id() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockPeer)(nil).Id)) +} + +// IsClosed mocks base method. +func (m *MockPeer) IsClosed() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsClosed") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsClosed indicates an expected call of IsClosed. +func (mr *MockPeerMockRecorder) IsClosed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClosed", reflect.TypeOf((*MockPeer)(nil).IsClosed)) +} + +// ReleaseDrpcConn mocks base method. +func (m *MockPeer) ReleaseDrpcConn(arg0 drpc.Conn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReleaseDrpcConn", arg0) +} + +// ReleaseDrpcConn indicates an expected call of ReleaseDrpcConn. +func (mr *MockPeerMockRecorder) ReleaseDrpcConn(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseDrpcConn", reflect.TypeOf((*MockPeer)(nil).ReleaseDrpcConn), arg0) +} + +// TryClose mocks base method. +func (m *MockPeer) TryClose(arg0 time.Duration) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TryClose", arg0) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TryClose indicates an expected call of TryClose. +func (mr *MockPeerMockRecorder) TryClose(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TryClose", reflect.TypeOf((*MockPeer)(nil).TryClose), arg0) +} diff --git a/net/peer/peer.go b/net/peer/peer.go index 7f42260c..d0226823 100644 --- a/net/peer/peer.go +++ b/net/peer/peer.go @@ -1,26 +1,34 @@ +//go:generate mockgen -destination mock_peer/mock_peer.go github.com/anyproto/any-sync/net/peer Peer package peer import ( "context" + "io" + "net" + "sync" + "sync/atomic" + "time" + "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/app/ocache" "github.com/anyproto/any-sync/net/connutil" + "github.com/anyproto/any-sync/net/rpc" "github.com/anyproto/any-sync/net/secureservice/handshake" "github.com/anyproto/any-sync/net/secureservice/handshake/handshakeproto" "github.com/anyproto/any-sync/net/transport" "go.uber.org/zap" - "io" - "net" "storj.io/drpc" "storj.io/drpc/drpcconn" - "sync" - "time" + "storj.io/drpc/drpcmanager" + "storj.io/drpc/drpcstream" + "storj.io/drpc/drpcwire" ) var log = logger.NewNamed("common.net.peer") type connCtrl interface { ServeConn(ctx context.Context, conn net.Conn) (err error) + DrpcConfig() rpc.Config } func NewPeer(mc transport.MultiConn, ctrl connCtrl) (p Peer, err error) { @@ -29,7 +37,15 @@ func NewPeer(mc transport.MultiConn, ctrl connCtrl) (p Peer, err error) { active: map[*subConn]struct{}{}, MultiConn: mc, ctrl: ctrl, + limiter: limiter{ + // start throttling after 10 sub conns + startThreshold: 10, + slowDownStep: time.Millisecond * 100, + }, + subConnRelease: make(chan drpc.Conn), + created: time.Now(), } + pr.acceptCtx, pr.acceptCtxCancel = context.WithCancel(context.Background()) if pr.id, err = CtxPeerId(ctx); err != nil { return } @@ -63,11 +79,22 @@ type peer struct { ctrl connCtrl // drpc conn pool - inactive []*subConn - active map[*subConn]struct{} + // outgoing + inactive []*subConn + active map[*subConn]struct{} + subConnRelease chan drpc.Conn + openingWaitCount atomic.Int32 + + incomingCount atomic.Int32 + acceptCtx context.Context + + acceptCtxCancel context.CancelFunc + + limiter limiter mu sync.Mutex + created time.Time transport.MultiConn } @@ -78,7 +105,20 @@ func (p *peer) Id() string { func (p *peer) AcquireDrpcConn(ctx context.Context) (drpc.Conn, error) { p.mu.Lock() if len(p.inactive) == 0 { + wait := p.limiter.wait(len(p.active) + int(p.openingWaitCount.Load())) p.mu.Unlock() + if wait != nil { + p.openingWaitCount.Add(1) + defer p.openingWaitCount.Add(-1) + // throttle new connection opening + select { + case <-ctx.Done(): + return nil, ctx.Err() + case dconn := <-p.subConnRelease: + return dconn, nil + case <-wait: + } + } dconn, err := p.openDrpcConn(ctx) if err != nil { return nil, err @@ -89,12 +129,33 @@ func (p *peer) AcquireDrpcConn(ctx context.Context) (drpc.Conn, error) { idx := len(p.inactive) - 1 res := p.inactive[idx] p.inactive = p.inactive[:idx] + select { + case <-res.Closed(): + p.mu.Unlock() + return p.AcquireDrpcConn(ctx) + default: + } p.active[res] = struct{}{} p.mu.Unlock() return res, nil } func (p *peer) ReleaseDrpcConn(conn drpc.Conn) { + // do nothing if it's closed connection + select { + case <-conn.Closed(): + return + default: + } + + // try to send this connection to acquire if anyone is waiting for it + select { + case p.subConnRelease <- conn: + return + default: + } + + // return to pool p.mu.Lock() defer p.mu.Unlock() sc, ok := conn.(*subConn) @@ -126,8 +187,14 @@ func (p *peer) openDrpcConn(ctx context.Context) (dconn *subConn, err error) { return nil, err } tconn := connutil.NewLastUsageConn(conn) + bufSize := p.ctrl.DrpcConfig().Stream.MaxMsgSizeMb * (1 << 20) return &subConn{ - Conn: drpcconn.New(tconn), + Conn: drpcconn.NewWithOptions(tconn, drpcconn.Options{ + Manager: drpcmanager.Options{ + Reader: drpcwire.ReaderOptions{MaximumBufferSize: bufSize}, + Stream: drpcstream.Options{MaximumBufferSize: bufSize}, + }, + }), LastUsageConn: tconn, }, nil } @@ -141,12 +208,21 @@ func (p *peer) acceptLoop() { } }() for { + if wait := p.limiter.wait(int(p.incomingCount.Load())); wait != nil { + select { + case <-wait: + case <-p.acceptCtx.Done(): + return + } + } conn, err := p.Accept() if err != nil { exitErr = err return } go func() { + p.incomingCount.Add(1) + defer p.incomingCount.Add(-1) serveErr := p.serve(conn) if serveErr != io.EOF && serveErr != transport.ErrConnClosed { log.InfoCtx(p.Context(), "serve connection error", zap.Error(serveErr)) @@ -172,14 +248,14 @@ func (p *peer) serve(conn net.Conn) (err error) { } func (p *peer) TryClose(objectTTL time.Duration) (res bool, err error) { - p.gc(objectTTL) - if time.Now().Sub(p.LastUsage()) < objectTTL { - return false, nil + aliveCount := p.gc(objectTTL) + if aliveCount == 0 && p.created.Add(time.Minute).Before(time.Now()) { + return true, p.Close() } - return true, p.Close() + return false, nil } -func (p *peer) gc(ttl time.Duration) { +func (p *peer) gc(ttl time.Duration) (aliveCount int) { p.mu.Lock() defer p.mu.Unlock() minLastUsage := time.Now().Add(-ttl) @@ -210,9 +286,17 @@ func (p *peer) gc(ttl time.Duration) { select { case <-act.Closed(): delete(p.active, act) + continue default: } + if act.LastUsage().Before(minLastUsage) { + log.Warn("close active connection because no activity", zap.String("peerId", p.id), zap.String("addr", p.Addr())) + _ = act.Close() + delete(p.active, act) + continue + } } + return len(p.active) + len(p.inactive) + int(p.incomingCount.Load()) } func (p *peer) Close() (err error) { diff --git a/net/peer/peer_test.go b/net/peer/peer_test.go index ac1ff28b..a5275876 100644 --- a/net/peer/peer_test.go +++ b/net/peer/peer_test.go @@ -2,15 +2,18 @@ package peer import ( "context" + "github.com/anyproto/any-sync/net/rpc" "github.com/anyproto/any-sync/net/secureservice/handshake" "github.com/anyproto/any-sync/net/secureservice/handshake/handshakeproto" "github.com/anyproto/any-sync/net/transport/mock_transport" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "io" "net" _ "net/http/pprof" + "storj.io/drpc" + "storj.io/drpc/drpcconn" "testing" "time" ) @@ -18,32 +21,86 @@ import ( var ctx = context.Background() func TestPeer_AcquireDrpcConn(t *testing.T) { + t.Run("generic", func(t *testing.T) { + fx := newFixture(t, "p1") + defer fx.finish() + in, out := net.Pipe() + go func() { + handshake.IncomingProtoHandshake(ctx, out, defaultProtoChecker) + }() + defer out.Close() + fx.mc.EXPECT().Open(gomock.Any()).Return(in, nil) + dc, err := fx.AcquireDrpcConn(ctx) + require.NoError(t, err) + assert.NotEmpty(t, dc) + defer dc.Close() + + assert.Len(t, fx.active, 1) + assert.Len(t, fx.inactive, 0) + + fx.ReleaseDrpcConn(dc) + + assert.Len(t, fx.active, 0) + assert.Len(t, fx.inactive, 1) + + dc, err = fx.AcquireDrpcConn(ctx) + require.NoError(t, err) + assert.NotEmpty(t, dc) + assert.Len(t, fx.active, 1) + assert.Len(t, fx.inactive, 0) + }) + t.Run("closed sub conn", func(t *testing.T) { + fx := newFixture(t, "p1") + defer fx.finish() + + closedIn, _ := net.Pipe() + dc := drpcconn.New(closedIn) + fx.ReleaseDrpcConn(&subConn{Conn: dc}) + dc.Close() + + in, out := net.Pipe() + go func() { + handshake.IncomingProtoHandshake(ctx, out, defaultProtoChecker) + }() + defer out.Close() + fx.mc.EXPECT().Open(gomock.Any()).Return(in, nil) + _, err := fx.AcquireDrpcConn(ctx) + require.NoError(t, err) + }) +} + +func TestPeer_DrpcConn_OpenThrottling(t *testing.T) { fx := newFixture(t, "p1") defer fx.finish() - in, out := net.Pipe() + + acquire := func() (func(), drpc.Conn, error) { + in, out := net.Pipe() + go func() { + _, err := handshake.IncomingProtoHandshake(ctx, out, defaultProtoChecker) + require.NoError(t, err) + }() + + fx.mc.EXPECT().Open(gomock.Any()).Return(in, nil) + dconn, err := fx.AcquireDrpcConn(ctx) + return func() { out.Close() }, dconn, err + } + + var conCount = fx.limiter.startThreshold + 3 + var conns []drpc.Conn + for i := 0; i < conCount; i++ { + cc, dc, err := acquire() + require.NoError(t, err) + defer cc() + conns = append(conns, dc) + } + go func() { - handshake.IncomingProtoHandshake(ctx, out, defaultProtoChecker) + time.Sleep(fx.limiter.slowDownStep) + fx.ReleaseDrpcConn(conns[0]) + conns = conns[1:] }() - defer out.Close() - fx.mc.EXPECT().Open(gomock.Any()).Return(in, nil) - dc, err := fx.AcquireDrpcConn(ctx) + _, err := fx.AcquireDrpcConn(ctx) require.NoError(t, err) - assert.NotEmpty(t, dc) - defer dc.Close() - - assert.Len(t, fx.active, 1) - assert.Len(t, fx.inactive, 0) - - fx.ReleaseDrpcConn(dc) - - assert.Len(t, fx.active, 0) - assert.Len(t, fx.inactive, 1) - - dc, err = fx.AcquireDrpcConn(ctx) - require.NoError(t, err) - assert.NotEmpty(t, dc) - assert.Len(t, fx.active, 1) - assert.Len(t, fx.inactive, 0) } func TestPeerAccept(t *testing.T) { @@ -62,12 +119,30 @@ func TestPeerAccept(t *testing.T) { assert.NoError(t, <-outHandshakeCh) } +func TestPeer_DrpcConn_AcceptThrottling(t *testing.T) { + fx := newFixture(t, "p1") + defer fx.finish() + + var conCount = fx.limiter.startThreshold + 3 + for i := 0; i < conCount; i++ { + in, out := net.Pipe() + defer out.Close() + + var outHandshakeCh = make(chan error) + go func() { + outHandshakeCh <- handshake.OutgoingProtoHandshake(ctx, out, handshakeproto.ProtoType_DRPC) + }() + fx.acceptCh <- acceptedConn{conn: in} + cn := <-fx.testCtrl.serveConn + assert.Equal(t, in, cn) + assert.NoError(t, <-outHandshakeCh) + } +} + func TestPeer_TryClose(t *testing.T) { - t.Run("ttl", func(t *testing.T) { + t.Run("not close in first minute", func(t *testing.T) { fx := newFixture(t, "p1") defer fx.finish() - lu := time.Now() - fx.mc.EXPECT().LastUsage().Return(lu) res, err := fx.TryClose(time.Second) require.NoError(t, err) assert.False(t, res) @@ -75,8 +150,7 @@ func TestPeer_TryClose(t *testing.T) { t.Run("close", func(t *testing.T) { fx := newFixture(t, "p1") defer fx.finish() - lu := time.Now().Add(-time.Second * 2) - fx.mc.EXPECT().LastUsage().Return(lu) + fx.peer.created = fx.peer.created.Add(-time.Minute * 2) res, err := fx.TryClose(time.Second) require.NoError(t, err) assert.True(t, res) @@ -84,8 +158,7 @@ func TestPeer_TryClose(t *testing.T) { t.Run("gc", func(t *testing.T) { fx := newFixture(t, "p1") defer fx.finish() - now := time.Now() - fx.mc.EXPECT().LastUsage().Return(now.Add(time.Millisecond * 100)) + //now := time.Now() // make one inactive in, out := net.Pipe() @@ -104,6 +177,7 @@ func TestPeer_TryClose(t *testing.T) { }() defer out2.Close() fx.mc.EXPECT().Open(gomock.Any()).Return(in2, nil) + fx.mc.EXPECT().Addr().Return("") dc2, err := fx.AcquireDrpcConn(ctx) require.NoError(t, err) _ = dc2.Close() @@ -117,6 +191,18 @@ func TestPeer_TryClose(t *testing.T) { fx.mc.EXPECT().Open(gomock.Any()).Return(in3, nil) dc3, err := fx.AcquireDrpcConn(ctx) require.NoError(t, err) + + // make another active, should be removed by ttl + in4, out4 := net.Pipe() + go func() { + handshake.IncomingProtoHandshake(ctx, out4, defaultProtoChecker) + }() + defer out4.Close() + fx.mc.EXPECT().Open(gomock.Any()).Return(in4, nil) + dc4, err := fx.AcquireDrpcConn(ctx) + require.NoError(t, err) + defer dc4.Close() + fx.ReleaseDrpcConn(dc3) _ = dc3.Close() fx.ReleaseDrpcConn(dc) @@ -176,6 +262,10 @@ type testCtrl struct { closeCh chan struct{} } +func (t *testCtrl) DrpcConfig() rpc.Config { + return rpc.Config{Stream: rpc.StreamConfig{MaxMsgSizeMb: 10}} +} + func (t *testCtrl) ServeConn(ctx context.Context, conn net.Conn) (err error) { t.serveConn <- conn <-t.closeCh diff --git a/net/peerservice/peerservice.go b/net/peerservice/peerservice.go index e0691733..eda1d8c8 100644 --- a/net/peerservice/peerservice.go +++ b/net/peerservice/peerservice.go @@ -49,6 +49,7 @@ func (p *peerService) Init(a *app.App) (err error) { p.pool = a.MustComponent(pool.CName).(pool.Pool) p.server = a.MustComponent(server.CName).(server.DRPCServer) p.peerAddrs = map[string][]string{} + p.yamux.SetAccepter(p) return nil } diff --git a/net/pool/mock_pool/mock_pool.go b/net/pool/mock_pool/mock_pool.go new file mode 100644 index 00000000..7a304053 --- /dev/null +++ b/net/pool/mock_pool/mock_pool.go @@ -0,0 +1,80 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/anyproto/any-sync/net/pool (interfaces: Pool) + +// Package mock_pool is a generated GoMock package. +package mock_pool + +import ( + context "context" + reflect "reflect" + + peer "github.com/anyproto/any-sync/net/peer" + gomock "go.uber.org/mock/gomock" +) + +// MockPool is a mock of Pool interface. +type MockPool struct { + ctrl *gomock.Controller + recorder *MockPoolMockRecorder +} + +// MockPoolMockRecorder is the mock recorder for MockPool. +type MockPoolMockRecorder struct { + mock *MockPool +} + +// NewMockPool creates a new mock instance. +func NewMockPool(ctrl *gomock.Controller) *MockPool { + mock := &MockPool{ctrl: ctrl} + mock.recorder = &MockPoolMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPool) EXPECT() *MockPoolMockRecorder { + return m.recorder +} + +// AddPeer mocks base method. +func (m *MockPool) AddPeer(arg0 context.Context, arg1 peer.Peer) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddPeer", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddPeer indicates an expected call of AddPeer. +func (mr *MockPoolMockRecorder) AddPeer(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddPeer", reflect.TypeOf((*MockPool)(nil).AddPeer), arg0, arg1) +} + +// Get mocks base method. +func (m *MockPool) Get(arg0 context.Context, arg1 string) (peer.Peer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].(peer.Peer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockPoolMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockPool)(nil).Get), arg0, arg1) +} + +// GetOneOf mocks base method. +func (m *MockPool) GetOneOf(arg0 context.Context, arg1 []string) (peer.Peer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetOneOf", arg0, arg1) + ret0, _ := ret[0].(peer.Peer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetOneOf indicates an expected call of GetOneOf. +func (mr *MockPoolMockRecorder) GetOneOf(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOneOf", reflect.TypeOf((*MockPool)(nil).GetOneOf), arg0, arg1) +} diff --git a/net/pool/pool.go b/net/pool/pool.go index 37f8328e..7e936e1b 100644 --- a/net/pool/pool.go +++ b/net/pool/pool.go @@ -1,3 +1,4 @@ +//go:generate mockgen -destination mock_pool/mock_pool.go github.com/anyproto/any-sync/net/pool Pool package pool import ( diff --git a/net/pool/poolservice.go b/net/pool/poolservice.go index 2f84e5d0..0c574eb4 100644 --- a/net/pool/poolservice.go +++ b/net/pool/poolservice.go @@ -49,8 +49,8 @@ func (p *poolService) Init(a *app.App) (err error) { return p.dialer.Dial(ctx, id) }, ocache.WithLogger(log.Sugar()), - ocache.WithGCPeriod(time.Minute), - ocache.WithTTL(time.Minute*5), + ocache.WithGCPeriod(time.Minute/2), + ocache.WithTTL(time.Minute), ocache.WithPrometheus(p.metricReg, "netpool", "outgoing"), ) p.pool.incoming = ocache.New( @@ -58,8 +58,8 @@ func (p *poolService) Init(a *app.App) (err error) { return nil, ocache.ErrNotExists }, ocache.WithLogger(log.Sugar()), - ocache.WithGCPeriod(time.Minute), - ocache.WithTTL(time.Minute*5), + ocache.WithGCPeriod(time.Minute/2), + ocache.WithTTL(time.Minute), ocache.WithPrometheus(p.metricReg, "netpool", "incoming"), ) return nil diff --git a/net/rpc/debugserver/config.go b/net/rpc/debugserver/config.go new file mode 100644 index 00000000..53342ece --- /dev/null +++ b/net/rpc/debugserver/config.go @@ -0,0 +1,9 @@ +package debugserver + +type configGetter interface { + GetDebugServer() Config +} + +type Config struct { + ListenAddr string `yaml:"listenAddr"` +} diff --git a/net/rpc/debugserver/debugserver.go b/net/rpc/debugserver/debugserver.go new file mode 100644 index 00000000..4a313ea8 --- /dev/null +++ b/net/rpc/debugserver/debugserver.go @@ -0,0 +1,70 @@ +package debugserver + +import ( + "context" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/net/rpc" + "net" + "storj.io/drpc" + "storj.io/drpc/drpcmanager" + "storj.io/drpc/drpcmux" + "storj.io/drpc/drpcserver" + "storj.io/drpc/drpcstream" + "storj.io/drpc/drpcwire" +) + +const CName = "net.rpc.debugserver" + +func New() DebugServer { + return &debugServer{} +} + +type DebugServer interface { + app.ComponentRunnable + drpc.Mux +} + +type debugServer struct { + drpcServer *drpcserver.Server + *drpcmux.Mux + drpcConf rpc.Config + config Config + runCtx context.Context + runCtxCancel context.CancelFunc +} + +func (d *debugServer) Init(a *app.App) (err error) { + d.drpcConf = a.MustComponent("config").(rpc.ConfigGetter).GetDrpc() + d.config = a.MustComponent("config").(configGetter).GetDebugServer() + d.Mux = drpcmux.New() + bufSize := d.drpcConf.Stream.MaxMsgSizeMb * (1 << 20) + d.drpcServer = drpcserver.NewWithOptions(d, drpcserver.Options{Manager: drpcmanager.Options{ + Reader: drpcwire.ReaderOptions{MaximumBufferSize: bufSize}, + Stream: drpcstream.Options{MaximumBufferSize: bufSize}, + }}) + return nil +} + +func (d *debugServer) Name() (name string) { + return CName +} + +func (d *debugServer) Run(ctx context.Context) (err error) { + if d.config.ListenAddr == "" { + return + } + lis, err := net.Listen("tcp", d.config.ListenAddr) + if err != nil { + return + } + d.runCtx, d.runCtxCancel = context.WithCancel(context.Background()) + go d.drpcServer.Serve(d.runCtx, lis) + return +} + +func (d *debugServer) Close(ctx context.Context) (err error) { + if d.runCtx != nil { + d.runCtxCancel() + } + return nil +} diff --git a/net/rpc/drpcconfig.go b/net/rpc/drpcconfig.go new file mode 100644 index 00000000..551313f6 --- /dev/null +++ b/net/rpc/drpcconfig.go @@ -0,0 +1,13 @@ +package rpc + +type ConfigGetter interface { + GetDrpc() Config +} + +type Config struct { + Stream StreamConfig `yaml:"stream"` +} + +type StreamConfig struct { + MaxMsgSizeMb int `yaml:"maxMsgSizeMb"` +} diff --git a/net/rpc/rpctest/multiconntest/multiconntest.go b/net/rpc/rpctest/multiconntest/multiconntest.go new file mode 100644 index 00000000..a99d1083 --- /dev/null +++ b/net/rpc/rpctest/multiconntest/multiconntest.go @@ -0,0 +1,29 @@ +package multiconntest + +import ( + "context" + "github.com/anyproto/any-sync/net/connutil" + "github.com/anyproto/any-sync/net/transport" + yamux2 "github.com/anyproto/any-sync/net/transport/yamux" + "github.com/hashicorp/yamux" + "net" +) + +func MultiConnPair(peerServCtx, peerClientCtx context.Context) (serv, client transport.MultiConn) { + sc, cc := net.Pipe() + var servConn = make(chan transport.MultiConn, 1) + go func() { + sess, err := yamux.Server(sc, yamux.DefaultConfig()) + if err != nil { + panic(err) + } + servConn <- yamux2.NewMultiConn(peerServCtx, connutil.NewLastUsageConn(sc), "", sess) + }() + sess, err := yamux.Client(cc, yamux.DefaultConfig()) + if err != nil { + panic(err) + } + client = yamux2.NewMultiConn(peerClientCtx, connutil.NewLastUsageConn(cc), "", sess) + serv = <-servConn + return +} diff --git a/net/rpc/rpctest/peer.go b/net/rpc/rpctest/peer.go index a5fef8be..902547bb 100644 --- a/net/rpc/rpctest/peer.go +++ b/net/rpc/rpctest/peer.go @@ -2,29 +2,11 @@ package rpctest import ( "context" - "github.com/anyproto/any-sync/net/connutil" "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/net/rpc/rpctest/multiconntest" "github.com/anyproto/any-sync/net/transport" - yamux2 "github.com/anyproto/any-sync/net/transport/yamux" - "github.com/hashicorp/yamux" - "net" ) func MultiConnPair(peerIdServ, peerIdClient string) (serv, client transport.MultiConn) { - sc, cc := net.Pipe() - var servConn = make(chan transport.MultiConn, 1) - go func() { - sess, err := yamux.Server(sc, yamux.DefaultConfig()) - if err != nil { - panic(err) - } - servConn <- yamux2.NewMultiConn(peer.CtxWithPeerId(context.Background(), peerIdServ), connutil.NewLastUsageConn(sc), "", sess) - }() - sess, err := yamux.Client(cc, yamux.DefaultConfig()) - if err != nil { - panic(err) - } - client = yamux2.NewMultiConn(peer.CtxWithPeerId(context.Background(), peerIdClient), connutil.NewLastUsageConn(cc), "", sess) - serv = <-servConn - return + return multiconntest.MultiConnPair(peer.CtxWithPeerId(context.Background(), peerIdServ), peer.CtxWithPeerId(context.Background(), peerIdClient)) } diff --git a/net/rpc/rpctest/pool.go b/net/rpc/rpctest/pool.go new file mode 100644 index 00000000..fc70d16b --- /dev/null +++ b/net/rpc/rpctest/pool.go @@ -0,0 +1,76 @@ +package rpctest + +import ( + "context" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/net" + "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/net/pool" + "sync" +) + +func NewTestPool() *TestPool { + return &TestPool{peers: map[string]peer.Peer{}} +} + +type TestPool struct { + peers map[string]peer.Peer + mu sync.Mutex + ts *TestServer +} + +func (t *TestPool) Init(a *app.App) (err error) { + return nil +} + +func (t *TestPool) Name() (name string) { + return pool.CName +} + +func (t *TestPool) WithServer(ts *TestServer) *TestPool { + t.mu.Lock() + defer t.mu.Unlock() + t.ts = ts + return t +} + +func (t *TestPool) Run(ctx context.Context) (err error) { + return nil +} + +func (t *TestPool) Close(ctx context.Context) (err error) { + return nil +} + +func (t *TestPool) Get(ctx context.Context, id string) (peer.Peer, error) { + t.mu.Lock() + defer t.mu.Unlock() + if p, ok := t.peers[id]; ok { + return p, nil + } + if t.ts == nil { + return nil, net.ErrUnableToConnect + } + return t.ts.Dial(id) +} + +func (t *TestPool) GetOneOf(ctx context.Context, peerIds []string) (peer.Peer, error) { + t.mu.Lock() + defer t.mu.Unlock() + for _, peerId := range peerIds { + if p, ok := t.peers[peerId]; ok { + return p, nil + } + } + if t.ts == nil || len(peerIds) == 0 { + return nil, net.ErrUnableToConnect + } + return t.ts.Dial(peerIds[0]) +} + +func (t *TestPool) AddPeer(ctx context.Context, p peer.Peer) (err error) { + t.mu.Lock() + defer t.mu.Unlock() + t.peers[p.Id()] = p + return nil +} diff --git a/net/rpc/rpctest/server.go b/net/rpc/rpctest/server.go index 053187f4..de6bf68b 100644 --- a/net/rpc/rpctest/server.go +++ b/net/rpc/rpctest/server.go @@ -3,12 +3,25 @@ package rpctest import ( "context" "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/net/rpc" "github.com/anyproto/any-sync/net/rpc/server" "net" "storj.io/drpc/drpcmux" "storj.io/drpc/drpcserver" ) +type mockCtrl struct { +} + +func (m mockCtrl) ServeConn(ctx context.Context, conn net.Conn) (err error) { + return nil +} + +func (m mockCtrl) DrpcConfig() rpc.Config { + return rpc.Config{} +} + func NewTestServer() *TestServer { ts := &TestServer{ Mux: drpcmux.New(), @@ -22,22 +35,37 @@ type TestServer struct { *drpcserver.Server } -func (ts *TestServer) Init(a *app.App) (err error) { +func (s *TestServer) Init(a *app.App) (err error) { return nil } -func (ts *TestServer) Name() (name string) { +func (s *TestServer) Name() (name string) { return server.CName } -func (ts *TestServer) Run(ctx context.Context) (err error) { +func (s *TestServer) Run(ctx context.Context) (err error) { return nil } -func (ts *TestServer) Close(ctx context.Context) (err error) { +func (s *TestServer) Close(ctx context.Context) (err error) { return nil } func (s *TestServer) ServeConn(ctx context.Context, conn net.Conn) (err error) { return s.Server.ServeOne(ctx, conn) } + +func (s *TestServer) DrpcConfig() rpc.Config { + return rpc.Config{Stream: rpc.StreamConfig{MaxMsgSizeMb: 10}} +} + +func (s *TestServer) Dial(peerId string) (clientPeer peer.Peer, err error) { + mcS, mcC := MultiConnPair(peerId+"server", peerId) + // NewPeer runs the accept loop + _, err = peer.NewPeer(mcS, s) + if err != nil { + return + } + // and we ourselves don't call server methods on accept + return peer.NewPeer(mcC, mockCtrl{}) +} diff --git a/net/rpc/server/drpcserver.go b/net/rpc/server/drpcserver.go index 2b061515..8fc112ca 100644 --- a/net/rpc/server/drpcserver.go +++ b/net/rpc/server/drpcserver.go @@ -5,13 +5,14 @@ import ( "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/logger" "github.com/anyproto/any-sync/metric" - anyNet "github.com/anyproto/any-sync/net" + "github.com/anyproto/any-sync/net/rpc" "go.uber.org/zap" "net" "storj.io/drpc" "storj.io/drpc/drpcmanager" "storj.io/drpc/drpcmux" "storj.io/drpc/drpcserver" + "storj.io/drpc/drpcstream" "storj.io/drpc/drpcwire" ) @@ -25,6 +26,7 @@ func New() DRPCServer { type DRPCServer interface { ServeConn(ctx context.Context, conn net.Conn) (err error) + DrpcConfig() rpc.Config app.Component drpc.Mux } @@ -32,7 +34,7 @@ type DRPCServer interface { type drpcServer struct { drpcServer *drpcserver.Server *drpcmux.Mux - config anyNet.Config + config rpc.Config metric metric.Metric } @@ -43,7 +45,7 @@ func (s *drpcServer) Name() (name string) { } func (s *drpcServer) Init(a *app.App) (err error) { - s.config = a.MustComponent("config").(anyNet.ConfigGetter).GetNet() + s.config = a.MustComponent("config").(rpc.ConfigGetter).GetDrpc() s.metric, _ = a.Component(metric.CName).(metric.Metric) s.Mux = drpcmux.New() @@ -52,8 +54,10 @@ func (s *drpcServer) Init(a *app.App) (err error) { if s.metric != nil { handler = s.metric.WrapDRPCHandler(s) } + bufSize := s.config.Stream.MaxMsgSizeMb * (1 << 20) s.drpcServer = drpcserver.NewWithOptions(handler, drpcserver.Options{Manager: drpcmanager.Options{ - Reader: drpcwire.ReaderOptions{MaximumBufferSize: s.config.Stream.MaxMsgSizeMb * (1 << 20)}, + Reader: drpcwire.ReaderOptions{MaximumBufferSize: bufSize}, + Stream: drpcstream.Options{MaximumBufferSize: bufSize}, }}) return } @@ -63,3 +67,7 @@ func (s *drpcServer) ServeConn(ctx context.Context, conn net.Conn) (err error) { l.Debug("drpc serve peer") return s.drpcServer.ServeOne(ctx, conn) } + +func (s *drpcServer) DrpcConfig() rpc.Config { + return s.config +} diff --git a/net/secureservice/credential.go b/net/secureservice/credential.go index 9d992430..5a5ac7d9 100644 --- a/net/secureservice/credential.go +++ b/net/secureservice/credential.go @@ -8,9 +8,13 @@ import ( "go.uber.org/zap" ) -func newNoVerifyChecker(protoVersion uint32) handshake.CredentialChecker { +func newNoVerifyChecker(protoVersion uint32, clientVersion string) handshake.CredentialChecker { return &noVerifyChecker{ - cred: &handshakeproto.Credentials{Type: handshakeproto.CredentialsType_SkipVerify, Version: protoVersion}, + cred: &handshakeproto.Credentials{ + Type: handshakeproto.CredentialsType_SkipVerify, + Version: protoVersion, + ClientVersion: clientVersion, + }, } } @@ -22,23 +26,29 @@ func (n noVerifyChecker) MakeCredentials(remotePeerId string) *handshakeproto.Cr return n.cred } -func (n noVerifyChecker) CheckCredential(remotePeerId string, cred *handshakeproto.Credentials) (identity []byte, err error) { +func (n noVerifyChecker) CheckCredential(remotePeerId string, cred *handshakeproto.Credentials) (result handshake.Result, err error) { if cred.Version != n.cred.Version { - return nil, handshake.ErrIncompatibleVersion + err = handshake.ErrIncompatibleVersion + return } - return nil, nil + return handshake.Result{ + ProtoVersion: cred.Version, + ClientVersion: cred.ClientVersion, + }, nil } -func newPeerSignVerifier(protoVersion uint32, account *accountdata.AccountKeys) handshake.CredentialChecker { +func newPeerSignVerifier(protoVersion uint32, clientVersion string, account *accountdata.AccountKeys) handshake.CredentialChecker { return &peerSignVerifier{ - protoVersion: protoVersion, - account: account, + protoVersion: protoVersion, + clientVersion: clientVersion, + account: account, } } type peerSignVerifier struct { - protoVersion uint32 - account *accountdata.AccountKeys + protoVersion uint32 + clientVersion string + account *accountdata.AccountKeys } func (p *peerSignVerifier) MakeCredentials(remotePeerId string) *handshakeproto.Credentials { @@ -54,33 +64,43 @@ func (p *peerSignVerifier) MakeCredentials(remotePeerId string) *handshakeproto. } payload, _ := msg.Marshal() return &handshakeproto.Credentials{ - Type: handshakeproto.CredentialsType_SignedPeerIds, - Payload: payload, - Version: p.protoVersion, + Type: handshakeproto.CredentialsType_SignedPeerIds, + Payload: payload, + Version: p.protoVersion, + ClientVersion: p.clientVersion, } } -func (p *peerSignVerifier) CheckCredential(remotePeerId string, cred *handshakeproto.Credentials) (identity []byte, err error) { +func (p *peerSignVerifier) CheckCredential(remotePeerId string, cred *handshakeproto.Credentials) (result handshake.Result, err error) { if cred.Version != p.protoVersion { - return nil, handshake.ErrIncompatibleVersion + err = handshake.ErrIncompatibleVersion + return } if cred.Type != handshakeproto.CredentialsType_SignedPeerIds { - return nil, handshake.ErrSkipVerifyNotAllowed + err = handshake.ErrSkipVerifyNotAllowed + return } var msg = &handshakeproto.PayloadSignedPeerIds{} if err = msg.Unmarshal(cred.Payload); err != nil { - return nil, handshake.ErrUnexpectedPayload + err = handshake.ErrUnexpectedPayload + return } pubKey, err := crypto.UnmarshalEd25519PublicKeyProto(msg.Identity) if err != nil { - return nil, handshake.ErrInvalidCredentials + err = handshake.ErrInvalidCredentials + return } ok, err := pubKey.Verify([]byte((remotePeerId + p.account.PeerId)), msg.Sign) if err != nil { - return nil, err + return } if !ok { - return nil, handshake.ErrInvalidCredentials + err = handshake.ErrInvalidCredentials + return } - return msg.Identity, nil + return handshake.Result{ + Identity: msg.Identity, + ProtoVersion: cred.Version, + ClientVersion: cred.ClientVersion, + }, nil } diff --git a/net/secureservice/credential_test.go b/net/secureservice/credential_test.go index 50b24ece..78222c87 100644 --- a/net/secureservice/credential_test.go +++ b/net/secureservice/credential_test.go @@ -15,21 +15,21 @@ func TestPeerSignVerifier_CheckCredential(t *testing.T) { identity1, _ := a1.SignKey.GetPublic().Marshall() identity2, _ := a2.SignKey.GetPublic().Marshall() - cc1 := newPeerSignVerifier(0, a1) - cc2 := newPeerSignVerifier(0, a2) + cc1 := newPeerSignVerifier(0, "test:v1", a1) + cc2 := newPeerSignVerifier(0, "test:v1", a2) c1 := a2.PeerId c2 := a1.PeerId cr1 := cc1.MakeCredentials(c1) cr2 := cc2.MakeCredentials(c2) - id1, err := cc1.CheckCredential(c1, cr2) + res, err := cc1.CheckCredential(c1, cr2) assert.NoError(t, err) - assert.Equal(t, identity2, id1) + assert.Equal(t, identity2, res.Identity) - id2, err := cc2.CheckCredential(c2, cr1) + res2, err := cc2.CheckCredential(c2, cr1) assert.NoError(t, err) - assert.Equal(t, identity1, id2) + assert.Equal(t, identity1, res2.Identity) _, err = cc1.CheckCredential(c1, cr1) assert.EqualError(t, err, handshake.ErrInvalidCredentials.Error()) @@ -40,8 +40,8 @@ func TestIncompatibleVersion(t *testing.T) { a2 := newTestAccData(t) _, _ = a1.SignKey.GetPublic().Marshall() - cc1 := newPeerSignVerifier(0, a1) - cc2 := newPeerSignVerifier(1, a2) + cc1 := newPeerSignVerifier(0, "test:v1", a1) + cc2 := newPeerSignVerifier(1, "test:v1", a2) c1 := a2.PeerId c2 := a1.PeerId diff --git a/net/secureservice/handshake/credential.go b/net/secureservice/handshake/credential.go index 8a4760a0..4ad46c51 100644 --- a/net/secureservice/handshake/credential.go +++ b/net/secureservice/handshake/credential.go @@ -6,30 +6,31 @@ import ( "io" ) -func OutgoingHandshake(ctx context.Context, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (identity []byte, err error) { +func OutgoingHandshake(ctx context.Context, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (result Result, err error) { if ctx == nil { ctx = context.Background() } h := newHandshake() done := make(chan struct{}) var ( - resIdentity []byte - resErr error + res Result + resErr error ) go func() { defer close(done) - resIdentity, resErr = outgoingHandshake(h, conn, peerId, cc) + res, resErr = outgoingHandshake(h, conn, peerId, cc) }() select { case <-done: - return resIdentity, resErr + return res, resErr case <-ctx.Done(): _ = conn.Close() - return nil, ctx.Err() + err = ctx.Err() + return } } -func outgoingHandshake(h *handshake, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (identity []byte, err error) { +func outgoingHandshake(h *handshake, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (result Result, err error) { defer h.release() h.conn = conn localCred := cc.MakeCredentials(peerId) @@ -44,58 +45,62 @@ func outgoingHandshake(h *handshake, conn io.ReadWriteCloser, peerId string, cc } if msg.ack != nil { if msg.ack.Error == handshakeproto.Error_InvalidCredentials { - return nil, ErrPeerDeclinedCredentials + err = ErrPeerDeclinedCredentials + return } - return nil, HandshakeError{e: msg.ack.Error} + err = HandshakeError{e: msg.ack.Error} + return } - if identity, err = cc.CheckCredential(peerId, msg.cred); err != nil { + if result, err = cc.CheckCredential(peerId, msg.cred); err != nil { h.tryWriteErrAndClose(err) return } if err = h.writeAck(handshakeproto.Error_Null); err != nil { h.tryWriteErrAndClose(err) - return nil, err + return } msg, err = h.readMsg(msgTypeAck) if err != nil { h.tryWriteErrAndClose(err) - return nil, err + return } if msg.ack.Error == handshakeproto.Error_Null { - return identity, nil + return result, nil } else { _ = h.conn.Close() - return nil, HandshakeError{e: msg.ack.Error} + err = HandshakeError{e: msg.ack.Error} + return } } -func IncomingHandshake(ctx context.Context, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (identity []byte, err error) { +func IncomingHandshake(ctx context.Context, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (result Result, err error) { if ctx == nil { ctx = context.Background() } h := newHandshake() done := make(chan struct{}) var ( - resIdentity []byte - resError error + res Result + resError error ) go func() { defer close(done) - resIdentity, resError = incomingHandshake(h, conn, peerId, cc) + res, resError = incomingHandshake(h, conn, peerId, cc) }() select { case <-done: - return resIdentity, resError + return res, resError case <-ctx.Done(): _ = conn.Close() - return nil, ctx.Err() + err = ctx.Err() + return } } -func incomingHandshake(h *handshake, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (identity []byte, err error) { +func incomingHandshake(h *handshake, conn io.ReadWriteCloser, peerId string, cc CredentialChecker) (result Result, err error) { defer h.release() h.conn = conn @@ -104,30 +109,32 @@ func incomingHandshake(h *handshake, conn io.ReadWriteCloser, peerId string, cc h.tryWriteErrAndClose(err) return } - if identity, err = cc.CheckCredential(peerId, msg.cred); err != nil { + if result, err = cc.CheckCredential(peerId, msg.cred); err != nil { h.tryWriteErrAndClose(err) return } if err = h.writeCredentials(cc.MakeCredentials(peerId)); err != nil { h.tryWriteErrAndClose(err) - return nil, err + return } msg, err = h.readMsg(msgTypeAck) if err != nil { h.tryWriteErrAndClose(err) - return nil, err + return } if msg.ack.Error != handshakeproto.Error_Null { if msg.ack.Error == handshakeproto.Error_InvalidCredentials { - return nil, ErrPeerDeclinedCredentials + err = ErrPeerDeclinedCredentials + return } - return nil, HandshakeError{e: msg.ack.Error} + err = HandshakeError{e: msg.ack.Error} + return } if err = h.writeAck(handshakeproto.Error_Null); err != nil { h.tryWriteErrAndClose(err) - return nil, err + return } return } diff --git a/net/secureservice/handshake/credential_test.go b/net/secureservice/handshake/credential_test.go index 49865848..fe6687a6 100644 --- a/net/secureservice/handshake/credential_test.go +++ b/net/secureservice/handshake/credential_test.go @@ -15,15 +15,19 @@ import ( ) var noVerifyChecker = &testCredChecker{ - makeCred: &handshakeproto.Credentials{Type: handshakeproto.CredentialsType_SkipVerify}, - checkCred: func(peerId string, cred *handshakeproto.Credentials) (identity []byte, err error) { - return []byte("identity"), nil + makeCred: &handshakeproto.Credentials{Type: handshakeproto.CredentialsType_SkipVerify, ClientVersion: "test:v1.0"}, + checkCred: func(peerId string, cred *handshakeproto.Credentials) (res Result, err error) { + return Result{ + Identity: []byte("identity"), + ProtoVersion: cred.Version, + ClientVersion: cred.ClientVersion, + }, nil }, } type handshakeRes struct { - identity []byte - err error + res Result + err error } func TestOutgoingHandshake(t *testing.T) { @@ -32,7 +36,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -50,7 +54,7 @@ func TestOutgoingHandshake(t *testing.T) { // send ack require.NoError(t, h.writeAck(handshakeproto.Error_Null)) res := <-handshakeResCh - assert.NotEmpty(t, res.identity) + assert.NotEmpty(t, res.res) assert.NoError(t, res.err) }) t.Run("write cred err", func(t *testing.T) { @@ -58,7 +62,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() _ = c2.Close() res := <-handshakeResCh @@ -69,7 +73,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -85,7 +89,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -101,7 +105,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", &testCredChecker{makeCred: noVerifyChecker.makeCred, checkErr: ErrInvalidCredentials}) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -120,7 +124,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -138,7 +142,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -159,7 +163,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -172,9 +176,7 @@ func TestOutgoingHandshake(t *testing.T) { _, err = h.readMsg(msgTypeAck) require.NoError(t, err) // write cred instead ack - require.NoError(t, h.writeCredentials(noVerifyChecker.MakeCredentials(""))) - _, err = h.readMsg(msgTypeAck) - require.Error(t, err) + _ = h.writeCredentials(noVerifyChecker.MakeCredentials("")) res := <-handshakeResCh require.Error(t, res.err) }) @@ -183,7 +185,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -211,7 +213,7 @@ func TestOutgoingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := OutgoingHandshake(ctx, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -234,7 +236,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -252,7 +254,7 @@ func TestIncomingHandshake(t *testing.T) { require.NoError(t, err) assert.Equal(t, handshakeproto.Error_Null, msg.ack.Error) res := <-handshakeResCh - assert.NotEmpty(t, res.identity) + assert.NotEmpty(t, res.res) require.NoError(t, res.err) }) t.Run("write cred err", func(t *testing.T) { @@ -260,7 +262,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() _ = c2.Close() res := <-handshakeResCh @@ -271,7 +273,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -286,7 +288,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -300,7 +302,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", &testCredChecker{makeCred: noVerifyChecker.makeCred, checkErr: ErrInvalidCredentials}) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -320,7 +322,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", &testCredChecker{makeCred: noVerifyChecker.makeCred, checkErr: ErrIncompatibleVersion}) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -340,7 +342,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -350,7 +352,7 @@ func TestIncomingHandshake(t *testing.T) { _, err := h.readMsg(msgTypeCred) require.NoError(t, err) // write cred instead ack - require.NoError(t, h.writeCredentials(noVerifyChecker.MakeCredentials(""))) + _ = h.writeCredentials(noVerifyChecker.MakeCredentials("")) // expect EOF _, err = h.readMsg(msgTypeAck) require.Error(t, err) @@ -362,7 +364,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -381,7 +383,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -403,7 +405,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -425,7 +427,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -448,7 +450,7 @@ func TestIncomingHandshake(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(ctx, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -472,7 +474,7 @@ func TestNotAHandshakeMessage(t *testing.T) { var handshakeResCh = make(chan handshakeRes, 1) go func() { identity, err := IncomingHandshake(nil, c1, "", noVerifyChecker) - handshakeResCh <- handshakeRes{identity: identity, err: err} + handshakeResCh <- handshakeRes{res: identity, err: err} }() h := newHandshake() h.conn = c2 @@ -491,20 +493,20 @@ func TestEndToEnd(t *testing.T) { st := time.Now() go func() { identity, err := OutgoingHandshake(nil, c1, "", noVerifyChecker) - outResCh <- handshakeRes{identity: identity, err: err} + outResCh <- handshakeRes{res: identity, err: err} }() go func() { identity, err := IncomingHandshake(nil, c2, "", noVerifyChecker) - inResCh <- handshakeRes{identity: identity, err: err} + inResCh <- handshakeRes{res: identity, err: err} }() outRes := <-outResCh assert.NoError(t, outRes.err) - assert.NotEmpty(t, outRes.identity) + assert.NotEmpty(t, outRes.res) inRes := <-inResCh assert.NoError(t, inRes.err) - assert.NotEmpty(t, inRes.identity) + assert.NotEmpty(t, inRes.res) t.Log("dur", time.Since(st)) } @@ -548,7 +550,7 @@ func BenchmarkHandshake(b *testing.B) { type testCredChecker struct { makeCred *handshakeproto.Credentials - checkCred func(peerId string, cred *handshakeproto.Credentials) (identity []byte, err error) + checkCred func(peerId string, cred *handshakeproto.Credentials) (res Result, err error) checkErr error } @@ -556,14 +558,15 @@ func (t *testCredChecker) MakeCredentials(peerId string) *handshakeproto.Credent return t.makeCred } -func (t *testCredChecker) CheckCredential(peerId string, cred *handshakeproto.Credentials) (identity []byte, err error) { +func (t *testCredChecker) CheckCredential(peerId string, cred *handshakeproto.Credentials) (res Result, err error) { if t.checkErr != nil { - return nil, t.checkErr + err = t.checkErr + return } if t.checkCred != nil { return t.checkCred(peerId, cred) } - return nil, nil + return } func newConnPair(t require.TestingT) (sc1, sc2 *secConn) { diff --git a/net/secureservice/handshake/handshake.go b/net/secureservice/handshake/handshake.go index 42aa2a8c..8a8af38c 100644 --- a/net/secureservice/handshake/handshake.go +++ b/net/secureservice/handshake/handshake.go @@ -64,7 +64,13 @@ var handshakePool = &sync.Pool{New: func() any { type CredentialChecker interface { MakeCredentials(remotePeerId string) *handshakeproto.Credentials - CheckCredential(remotePeerId string, cred *handshakeproto.Credentials) (identity []byte, err error) + CheckCredential(remotePeerId string, cred *handshakeproto.Credentials) (result Result, err error) +} + +type Result struct { + Identity []byte + ProtoVersion uint32 + ClientVersion string } func newHandshake() *handshake { diff --git a/net/secureservice/handshake/handshakeproto/handshake.pb.go b/net/secureservice/handshake/handshakeproto/handshake.pb.go index e9d6dfcb..b632c376 100644 --- a/net/secureservice/handshake/handshakeproto/handshake.pb.go +++ b/net/secureservice/handshake/handshakeproto/handshake.pb.go @@ -115,9 +115,10 @@ func (ProtoType) EnumDescriptor() ([]byte, []int) { } type Credentials struct { - Type CredentialsType `protobuf:"varint,1,opt,name=type,proto3,enum=anyHandshake.CredentialsType" json:"type,omitempty"` - Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` - Version uint32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` + Type CredentialsType `protobuf:"varint,1,opt,name=type,proto3,enum=anyHandshake.CredentialsType" json:"type,omitempty"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + Version uint32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` + ClientVersion string `protobuf:"bytes,4,opt,name=clientVersion,proto3" json:"clientVersion,omitempty"` } func (m *Credentials) Reset() { *m = Credentials{} } @@ -174,6 +175,13 @@ func (m *Credentials) GetVersion() uint32 { return 0 } +func (m *Credentials) GetClientVersion() string { + if m != nil { + return m.ClientVersion + } + return "" +} + type PayloadSignedPeerIds struct { // account identity Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` @@ -331,35 +339,36 @@ func init() { } var fileDescriptor_60283fc75f020893 = []byte{ - // 439 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xcf, 0x6e, 0xd3, 0x40, - 0x10, 0xc6, 0xbd, 0x4d, 0xdc, 0x96, 0x21, 0x2d, 0xdb, 0x6d, 0x4a, 0x2d, 0x24, 0xac, 0x28, 0xa7, - 0x10, 0x89, 0x84, 0x7f, 0xe2, 0x1e, 0x9a, 0x22, 0x72, 0xa9, 0x22, 0x17, 0x7a, 0xe0, 0xb6, 0xf5, - 0x0e, 0xed, 0x2a, 0xcb, 0xda, 0xda, 0xdd, 0x86, 0xfa, 0x2d, 0x78, 0x14, 0x1e, 0x83, 0x63, 0x8f, - 0x1c, 0x51, 0xf2, 0x22, 0xc8, 0x9b, 0xa4, 0x71, 0x38, 0xf5, 0x62, 0xef, 0xcc, 0xfc, 0xc6, 0xdf, - 0x7c, 0xe3, 0x85, 0x81, 0x46, 0xd7, 0xb7, 0x98, 0xde, 0x18, 0xb4, 0x68, 0xa6, 0x32, 0xc5, 0xfe, - 0x35, 0xd7, 0xc2, 0x5e, 0xf3, 0x49, 0xe5, 0x94, 0x9b, 0xcc, 0x65, 0x7d, 0xff, 0xb4, 0xeb, 0x6c, - 0xcf, 0x27, 0x58, 0x83, 0xeb, 0xe2, 0xd3, 0x2a, 0xd7, 0x76, 0xf0, 0xf8, 0xc4, 0xa0, 0x40, 0xed, - 0x24, 0x57, 0x96, 0xbd, 0x86, 0xba, 0x2b, 0x72, 0x8c, 0x48, 0x8b, 0x74, 0xf6, 0xdf, 0x3c, 0xef, - 0x55, 0xd9, 0x5e, 0x05, 0xfc, 0x5c, 0xe4, 0x98, 0x78, 0x94, 0x45, 0xb0, 0x93, 0xf3, 0x42, 0x65, - 0x5c, 0x44, 0x5b, 0x2d, 0xd2, 0x69, 0x24, 0xab, 0xb0, 0xac, 0x4c, 0xd1, 0x58, 0x99, 0xe9, 0xa8, - 0xd6, 0x22, 0x9d, 0xbd, 0x64, 0x15, 0xb6, 0x3f, 0x42, 0x73, 0xbc, 0x80, 0xce, 0xe5, 0x95, 0x46, - 0x31, 0x46, 0x34, 0x23, 0x61, 0xd9, 0x33, 0xd8, 0x95, 0x5e, 0xc2, 0x15, 0x7e, 0x84, 0x46, 0x72, - 0x1f, 0x33, 0x06, 0x75, 0x2b, 0xaf, 0xf4, 0x52, 0xc4, 0x9f, 0xdb, 0xaf, 0xa0, 0x36, 0x48, 0x27, - 0xec, 0x05, 0x84, 0x68, 0x4c, 0x66, 0x96, 0x63, 0x1f, 0x6e, 0x8e, 0x7d, 0x5a, 0x96, 0x92, 0x05, - 0xd1, 0x7e, 0x0f, 0xe1, 0xd8, 0xaf, 0xe1, 0x25, 0x84, 0x7e, 0x1f, 0xcb, 0x9e, 0xe3, 0xcd, 0x1e, - 0xcf, 0x78, 0x93, 0x0b, 0xaa, 0xfb, 0x0e, 0x9e, 0xfc, 0x67, 0x9f, 0xed, 0x03, 0x9c, 0x4f, 0x64, - 0x7e, 0x81, 0x46, 0x7e, 0x2b, 0x68, 0xc0, 0x0e, 0x60, 0x6f, 0xc3, 0x0d, 0x25, 0xdd, 0x5f, 0x04, - 0x42, 0x2f, 0xcf, 0x76, 0xa1, 0x7e, 0x76, 0xa3, 0x14, 0x0d, 0xca, 0xb6, 0x2f, 0x1a, 0x6f, 0x73, - 0x4c, 0x1d, 0x0a, 0x4a, 0xd8, 0x53, 0x60, 0x23, 0x3d, 0xe5, 0x4a, 0x8a, 0x8a, 0x00, 0xdd, 0x62, - 0x47, 0x70, 0xb0, 0xe6, 0x96, 0xdb, 0xa2, 0x35, 0x16, 0x41, 0x73, 0xad, 0x7a, 0x96, 0xb9, 0x81, - 0x52, 0xd9, 0x0f, 0x14, 0xb4, 0xce, 0x9a, 0x40, 0x87, 0xc8, 0x85, 0x92, 0x1a, 0x4f, 0x6f, 0x53, - 0x44, 0x81, 0x82, 0x86, 0xec, 0x18, 0x0e, 0x47, 0x3a, 0xcd, 0xbe, 0xe7, 0xdc, 0xc9, 0x4b, 0x85, - 0x17, 0x8b, 0x3f, 0x40, 0xb7, 0xcb, 0xef, 0x57, 0x0b, 0xde, 0x31, 0xdd, 0xe9, 0x1e, 0xc1, 0xa3, - 0x7b, 0xf3, 0xe5, 0xd4, 0xc3, 0x64, 0x7c, 0x42, 0x83, 0x0f, 0xc3, 0xdf, 0xb3, 0x98, 0xdc, 0xcd, - 0x62, 0xf2, 0x77, 0x16, 0x93, 0x9f, 0xf3, 0x38, 0xb8, 0x9b, 0xc7, 0xc1, 0x9f, 0x79, 0x1c, 0x7c, - 0xed, 0x3e, 0xfc, 0x4a, 0x5e, 0x6e, 0xfb, 0xd7, 0xdb, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x53, - 0x32, 0xf7, 0x79, 0xc7, 0x02, 0x00, 0x00, + // 457 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x4d, 0x6f, 0xd3, 0x40, + 0x10, 0xf5, 0x36, 0x71, 0x3f, 0x86, 0xa4, 0x6c, 0xb7, 0x29, 0xb5, 0x90, 0xb0, 0xa2, 0x88, 0x43, + 0x88, 0x44, 0xc2, 0x97, 0xb8, 0x87, 0xa6, 0x88, 0x5c, 0xaa, 0xc8, 0x85, 0x1e, 0xb8, 0x6d, 0xbd, + 0x43, 0xbb, 0xca, 0xb2, 0xb6, 0xd6, 0xdb, 0x50, 0xff, 0x0b, 0xce, 0xfc, 0x0a, 0x7e, 0x06, 0xc7, + 0x1e, 0x39, 0xa2, 0xe4, 0x8f, 0x20, 0x6f, 0x9c, 0xc6, 0xe1, 0xc4, 0xc5, 0xde, 0x99, 0xf7, 0x66, + 0xe7, 0xbd, 0x67, 0xc3, 0x50, 0xa3, 0x1d, 0x64, 0x18, 0xdf, 0x18, 0xcc, 0xd0, 0xcc, 0x64, 0x8c, + 0x83, 0x6b, 0xae, 0x45, 0x76, 0xcd, 0xa7, 0x95, 0x53, 0x6a, 0x12, 0x9b, 0x0c, 0xdc, 0x33, 0x5b, + 0x77, 0xfb, 0xae, 0xc1, 0x1a, 0x5c, 0xe7, 0x1f, 0x56, 0xbd, 0xce, 0x0f, 0x02, 0x0f, 0x4e, 0x0c, + 0x0a, 0xd4, 0x56, 0x72, 0x95, 0xb1, 0x97, 0x50, 0xb7, 0x79, 0x8a, 0x01, 0x69, 0x93, 0xee, 0xfe, + 0xab, 0x27, 0xfd, 0x2a, 0xb9, 0x5f, 0x21, 0x7e, 0xcc, 0x53, 0x8c, 0x1c, 0x95, 0x05, 0xb0, 0x93, + 0xf2, 0x5c, 0x25, 0x5c, 0x04, 0x5b, 0x6d, 0xd2, 0x6d, 0x44, 0xab, 0xb2, 0x40, 0x66, 0x68, 0x32, + 0x99, 0xe8, 0xa0, 0xd6, 0x26, 0xdd, 0x66, 0xb4, 0x2a, 0xd9, 0x53, 0x68, 0xc6, 0x4a, 0xa2, 0xb6, + 0x17, 0x25, 0x5e, 0x6f, 0x93, 0xee, 0x5e, 0xb4, 0xd9, 0xec, 0xbc, 0x87, 0xd6, 0x64, 0x79, 0xd5, + 0xb9, 0xbc, 0xd2, 0x28, 0x26, 0x88, 0x66, 0x2c, 0x32, 0xf6, 0x18, 0x76, 0xa5, 0x13, 0x62, 0x73, + 0x27, 0xb4, 0x11, 0xdd, 0xd7, 0x8c, 0x41, 0x3d, 0x93, 0x57, 0xba, 0x94, 0xe2, 0xce, 0x9d, 0x17, + 0x50, 0x1b, 0xc6, 0x53, 0xf6, 0x0c, 0x7c, 0x34, 0x26, 0x31, 0xa5, 0xb9, 0xc3, 0x4d, 0x73, 0xa7, + 0x05, 0x14, 0x2d, 0x19, 0x9d, 0xb7, 0xe0, 0x4f, 0x5c, 0x5a, 0xcf, 0xc1, 0x77, 0xb1, 0x95, 0x33, + 0xc7, 0x9b, 0x33, 0x8e, 0xe3, 0xa2, 0x58, 0xb2, 0x7a, 0x6f, 0xe0, 0xe1, 0x3f, 0x21, 0xb1, 0x7d, + 0x80, 0xf3, 0xa9, 0x4c, 0x2f, 0xd0, 0xc8, 0x2f, 0x39, 0xf5, 0xd8, 0x01, 0x34, 0x37, 0xdc, 0x50, + 0xd2, 0xfb, 0x49, 0xc0, 0x77, 0xeb, 0xd9, 0x2e, 0xd4, 0xcf, 0x6e, 0x94, 0xa2, 0x5e, 0x31, 0xf6, + 0x49, 0xe3, 0x6d, 0x8a, 0xb1, 0x45, 0x41, 0x09, 0x7b, 0x04, 0x6c, 0xac, 0x67, 0x5c, 0x49, 0x51, + 0x59, 0x40, 0xb7, 0xd8, 0x11, 0x1c, 0xac, 0x79, 0x65, 0x5a, 0xb4, 0xc6, 0x02, 0x68, 0xad, 0xb7, + 0x9e, 0x25, 0x76, 0xa8, 0x54, 0xf2, 0x0d, 0x05, 0xad, 0xb3, 0x16, 0xd0, 0x11, 0x72, 0xa1, 0xa4, + 0xc6, 0xd3, 0xdb, 0x18, 0x51, 0xa0, 0xa0, 0x3e, 0x3b, 0x86, 0xc3, 0xb1, 0x8e, 0x93, 0xaf, 0x29, + 0xb7, 0xf2, 0x52, 0x61, 0xf9, 0x05, 0xe8, 0x76, 0x71, 0x7f, 0x15, 0x70, 0x8e, 0xe9, 0x4e, 0xef, + 0x08, 0xf6, 0xee, 0xcd, 0x17, 0xaa, 0x47, 0xd1, 0xe4, 0x84, 0x7a, 0xef, 0x46, 0xbf, 0xe6, 0x21, + 0xb9, 0x9b, 0x87, 0xe4, 0xcf, 0x3c, 0x24, 0xdf, 0x17, 0xa1, 0x77, 0xb7, 0x08, 0xbd, 0xdf, 0x8b, + 0xd0, 0xfb, 0xdc, 0xfb, 0xff, 0x3f, 0xf7, 0x72, 0xdb, 0xbd, 0x5e, 0xff, 0x0d, 0x00, 0x00, 0xff, + 0xff, 0xe2, 0xfa, 0x40, 0x67, 0xee, 0x02, 0x00, 0x00, } func (m *Credentials) Marshal() (dAtA []byte, err error) { @@ -382,6 +391,13 @@ func (m *Credentials) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ClientVersion) > 0 { + i -= len(m.ClientVersion) + copy(dAtA[i:], m.ClientVersion) + i = encodeVarintHandshake(dAtA, i, uint64(len(m.ClientVersion))) + i-- + dAtA[i] = 0x22 + } if m.Version != 0 { i = encodeVarintHandshake(dAtA, i, uint64(m.Version)) i-- @@ -522,6 +538,10 @@ func (m *Credentials) Size() (n int) { if m.Version != 0 { n += 1 + sovHandshake(uint64(m.Version)) } + l = len(m.ClientVersion) + if l > 0 { + n += 1 + l + sovHandshake(uint64(l)) + } return n } @@ -673,6 +693,38 @@ func (m *Credentials) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientVersion", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowHandshake + } + 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 ErrInvalidLengthHandshake + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthHandshake + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipHandshake(dAtA[iNdEx:]) diff --git a/net/secureservice/handshake/handshakeproto/protos/handshake.proto b/net/secureservice/handshake/handshakeproto/protos/handshake.proto index 1ea66b28..87189e24 100644 --- a/net/secureservice/handshake/handshakeproto/protos/handshake.proto +++ b/net/secureservice/handshake/handshakeproto/protos/handshake.proto @@ -39,6 +39,7 @@ message Credentials { CredentialsType type = 1; bytes payload = 2; uint32 version = 3; + string clientVersion = 4; } enum CredentialsType { diff --git a/net/secureservice/handshake/proto.go b/net/secureservice/handshake/proto.go index 45e95ab5..6cd82110 100644 --- a/net/secureservice/handshake/proto.go +++ b/net/secureservice/handshake/proto.go @@ -11,19 +11,20 @@ type ProtoChecker struct { AllowedProtoTypes []handshakeproto.ProtoType } -func OutgoingProtoHandshake(ctx context.Context, conn net.Conn, pt handshakeproto.ProtoType) (err error) { +func OutgoingProtoHandshake(ctx context.Context, conn net.Conn, pt handshakeproto.ProtoType) error { if ctx == nil { ctx = context.Background() } h := newHandshake() done := make(chan struct{}) + var err error go func() { defer close(done) err = outgoingProtoHandshake(h, conn, pt) }() select { case <-done: - return + return err case <-ctx.Done(): _ = conn.Close() return ctx.Err() @@ -54,19 +55,23 @@ func outgoingProtoHandshake(h *handshake, conn net.Conn, pt handshakeproto.Proto return HandshakeError{e: msg.ack.Error} } -func IncomingProtoHandshake(ctx context.Context, conn net.Conn, pt ProtoChecker) (protoType handshakeproto.ProtoType, err error) { +func IncomingProtoHandshake(ctx context.Context, conn net.Conn, pt ProtoChecker) (handshakeproto.ProtoType, error) { if ctx == nil { ctx = context.Background() } h := newHandshake() done := make(chan struct{}) + var ( + protoType handshakeproto.ProtoType + err error + ) go func() { defer close(done) protoType, err = incomingProtoHandshake(h, conn, pt) }() select { case <-done: - return + return protoType, err case <-ctx.Done(): _ = conn.Close() return 0, ctx.Err() diff --git a/net/secureservice/secureservice.go b/net/secureservice/secureservice.go index 1e5d3231..f5a1aa78 100644 --- a/net/secureservice/secureservice.go +++ b/net/secureservice/secureservice.go @@ -3,6 +3,9 @@ package secureservice import ( "context" "crypto/tls" + "io" + "net" + commonaccount "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app/logger" @@ -13,14 +16,18 @@ import ( "github.com/libp2p/go-libp2p/core/crypto" libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls" "go.uber.org/zap" - "io" - "net" ) const CName = "common.net.secure" var log = logger.NewNamed(CName) +const ( + // ProtoVersion 0 - first any-sync version with raw tcp connections + // ProtoVersion 1 - version with yamux over tcp and quic + ProtoVersion = 1 +) + func New() SecureService { return &secureService{} } @@ -46,6 +53,9 @@ type secureService struct { } func (s *secureService) Init(a *app.App) (err error) { + if s.protoVersion == 0 { + s.protoVersion = ProtoVersion + } account := a.MustComponent(commonaccount.CName).(commonaccount.Service) peerKey, err := account.Account().PeerKey.Raw() if err != nil { @@ -54,8 +64,8 @@ func (s *secureService) Init(a *app.App) (err error) { if s.key, err = crypto.UnmarshalEd25519PrivateKey(peerKey); err != nil { return } - s.noVerifyChecker = newNoVerifyChecker(s.protoVersion) - s.peerSignVerifier = newPeerSignVerifier(s.protoVersion, account.Account()) + s.noVerifyChecker = newNoVerifyChecker(s.protoVersion, a.VersionName()) + s.peerSignVerifier = newPeerSignVerifier(s.protoVersion, a.VersionName(), account.Account()) s.nodeconf = a.MustComponent(nodeconf.CName).(nodeconf.Service) @@ -89,13 +99,14 @@ func (s *secureService) SecureInbound(ctx context.Context, conn net.Conn) (cctx } func (s *secureService) HandshakeInbound(ctx context.Context, conn io.ReadWriteCloser, peerId string) (cctx context.Context, err error) { - identity, err := handshake.IncomingHandshake(ctx, conn, peerId, s.inboundChecker) + res, err := handshake.IncomingHandshake(ctx, conn, peerId, s.inboundChecker) if err != nil { return nil, err } cctx = context.Background() cctx = peer.CtxWithPeerId(cctx, peerId) - cctx = peer.CtxWithIdentity(cctx, identity) + cctx = peer.CtxWithIdentity(cctx, res.Identity) + cctx = peer.CtxWithClientVersion(cctx, res.ClientVersion) return } @@ -112,13 +123,14 @@ func (s *secureService) SecureOutbound(ctx context.Context, conn net.Conn) (cctx } else { checker = s.noVerifyChecker } - identity, err := handshake.OutgoingHandshake(ctx, sc, sc.RemotePeer().String(), checker) + res, err := handshake.OutgoingHandshake(ctx, sc, sc.RemotePeer().String(), checker) if err != nil { return nil, err } cctx = context.Background() cctx = peer.CtxWithPeerId(cctx, sc.RemotePeer().String()) - cctx = peer.CtxWithIdentity(cctx, identity) + cctx = peer.CtxWithIdentity(cctx, res.Identity) + cctx = peer.CtxWithClientVersion(cctx, res.ClientVersion) return cctx, nil } diff --git a/net/secureservice/secureservice_test.go b/net/secureservice/secureservice_test.go index 6aee985d..0a6675dd 100644 --- a/net/secureservice/secureservice_test.go +++ b/net/secureservice/secureservice_test.go @@ -9,9 +9,9 @@ import ( "github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/nodeconf/mock_nodeconf" "github.com/anyproto/any-sync/testutil/testnodeconf" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "net" "testing" ) @@ -57,7 +57,7 @@ func TestHandshake(t *testing.T) { func TestHandshakeIncompatibleVersion(t *testing.T) { nc := testnodeconf.GenNodeConfig(2) - fxS := newFixture(t, nc, nc.GetAccountService(0), 0) + fxS := newFixture(t, nc, nc.GetAccountService(0), 1) defer fxS.Finish(t) sc, cc := net.Pipe() @@ -72,7 +72,7 @@ func TestHandshakeIncompatibleVersion(t *testing.T) { ar.ctx, ar.err = fxS.SecureInbound(ctx, sc) resCh <- ar }() - fxC := newFixture(t, nc, nc.GetAccountService(1), 1) + fxC := newFixture(t, nc, nc.GetAccountService(1), 2) defer fxC.Finish(t) _, err := fxC.SecureOutbound(ctx, cc) require.Equal(t, handshake.ErrIncompatibleVersion, err) diff --git a/net/streampool/sendpool.go b/net/streampool/sendpool.go index 0bff0765..642aeb9a 100644 --- a/net/streampool/sendpool.go +++ b/net/streampool/sendpool.go @@ -10,7 +10,10 @@ import ( // workers - how many processes will execute tasks // maxSize - limit for queue size func NewExecPool(workers, maxSize int) *ExecPool { + ctx, cancel := context.WithCancel(context.Background()) ss := &ExecPool{ + ctx: ctx, + cancel: cancel, workers: workers, batch: mb.New[func()](maxSize), } @@ -19,6 +22,8 @@ func NewExecPool(workers, maxSize int) *ExecPool { // ExecPool needed for parallel execution of the incoming send tasks type ExecPool struct { + ctx context.Context + cancel context.CancelFunc workers int batch *mb.MB[func()] } @@ -39,7 +44,7 @@ func (ss *ExecPool) Run() { func (ss *ExecPool) sendLoop() { for { - f, err := ss.batch.WaitOne(context.Background()) + f, err := ss.batch.WaitOne(ss.ctx) if err != nil { log.Debug("close send loop", zap.Error(err)) return @@ -49,5 +54,6 @@ func (ss *ExecPool) sendLoop() { } func (ss *ExecPool) Close() (err error) { + ss.cancel() return ss.batch.Close() } diff --git a/net/streampool/stream.go b/net/streampool/stream.go index 1d59af4b..2c4c959b 100644 --- a/net/streampool/stream.go +++ b/net/streampool/stream.go @@ -2,11 +2,12 @@ package streampool import ( "context" + "sync/atomic" + "github.com/anyproto/any-sync/app/logger" "github.com/cheggaaa/mb/v3" "go.uber.org/zap" "storj.io/drpc" - "sync/atomic" ) type stream struct { @@ -22,7 +23,7 @@ type stream struct { } func (sr *stream) write(msg drpc.Message) (err error) { - return sr.queue.Add(sr.stream.Context(), msg) + return sr.queue.TryAdd(msg) } func (sr *stream) readLoop() error { diff --git a/net/streampool/streampool.go b/net/streampool/streampool.go index 50a020b3..54c513b9 100644 --- a/net/streampool/streampool.go +++ b/net/streampool/streampool.go @@ -250,6 +250,8 @@ func (s *streamPool) openStream(ctx context.Context, p peer.Peer) *openingProces close(op.ch) delete(s.opening, p.Id()) }() + // in case there was no peerId in context + ctx := peer.CtxWithPeerId(ctx, p.Id()) // open new stream and add to pool st, tags, err := s.handler.OpenStream(ctx, p) if err != nil { diff --git a/net/streampool/streampool_test.go b/net/streampool/streampool_test.go index d4a05de3..30575019 100644 --- a/net/streampool/streampool_test.go +++ b/net/streampool/streampool_test.go @@ -2,18 +2,19 @@ package streampool import ( "fmt" + "sort" + "sync" + "sync/atomic" + "testing" + "time" + "github.com/anyproto/any-sync/net/peer" "github.com/anyproto/any-sync/net/rpc/rpctest" "github.com/anyproto/any-sync/net/streampool/testservice" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" - "sort" "storj.io/drpc" - "sync" - "sync/atomic" - "testing" - "time" ) var ctx = context.Background() diff --git a/net/transport/mock_transport/mock_transport.go b/net/transport/mock_transport/mock_transport.go index 43f5572c..ddc13ea4 100644 --- a/net/transport/mock_transport/mock_transport.go +++ b/net/transport/mock_transport/mock_transport.go @@ -8,10 +8,9 @@ import ( context "context" net "net" reflect "reflect" - time "time" transport "github.com/anyproto/any-sync/net/transport" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockTransport is a mock of Transport interface. @@ -158,20 +157,6 @@ func (mr *MockMultiConnMockRecorder) IsClosed() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClosed", reflect.TypeOf((*MockMultiConn)(nil).IsClosed)) } -// LastUsage mocks base method. -func (m *MockMultiConn) LastUsage() time.Time { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LastUsage") - ret0, _ := ret[0].(time.Time) - return ret0 -} - -// LastUsage indicates an expected call of LastUsage. -func (mr *MockMultiConnMockRecorder) LastUsage() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastUsage", reflect.TypeOf((*MockMultiConn)(nil).LastUsage)) -} - // Open mocks base method. func (m *MockMultiConn) Open(arg0 context.Context) (net.Conn, error) { m.ctrl.T.Helper() diff --git a/net/transport/transport.go b/net/transport/transport.go index 2dab5348..9d36435a 100644 --- a/net/transport/transport.go +++ b/net/transport/transport.go @@ -5,7 +5,6 @@ import ( "context" "errors" "net" - "time" ) var ( @@ -29,8 +28,6 @@ type MultiConn interface { Accept() (conn net.Conn, err error) // Open opens new sub connection Open(ctx context.Context) (conn net.Conn, err error) - // LastUsage returns the time of the last connection activity - LastUsage() time.Time // Addr returns remote peer address Addr() string // IsClosed returns true when connection is closed diff --git a/net/transport/yamux/config.go b/net/transport/yamux/config.go index 2afce5fd..48ea710a 100644 --- a/net/transport/yamux/config.go +++ b/net/transport/yamux/config.go @@ -5,7 +5,8 @@ type configGetter interface { } type Config struct { - ListenAddrs []string `yaml:"listenAddrs"` - WriteTimeoutSec int `yaml:"writeTimeoutSec"` - DialTimeoutSec int `yaml:"dialTimeoutSec"` + ListenAddrs []string `yaml:"listenAddrs"` + WriteTimeoutSec int `yaml:"writeTimeoutSec"` + DialTimeoutSec int `yaml:"dialTimeoutSec"` + KeepAlivePeriodSec int `yaml:"keepAlivePeriodSec"` } diff --git a/net/transport/yamux/conn.go b/net/transport/yamux/conn.go index c752d22d..6a473796 100644 --- a/net/transport/yamux/conn.go +++ b/net/transport/yamux/conn.go @@ -3,13 +3,16 @@ package yamux import ( "context" "github.com/anyproto/any-sync/net/connutil" + "github.com/anyproto/any-sync/net/peer" "github.com/anyproto/any-sync/net/transport" "github.com/hashicorp/yamux" + "io" "net" "time" ) func NewMultiConn(cctx context.Context, luConn *connutil.LastUsageConn, addr string, sess *yamux.Session) transport.MultiConn { + cctx = peer.CtxWithPeerAddr(cctx, sess.RemoteAddr().String()) return &yamuxConn{ ctx: cctx, luConn: luConn, @@ -46,7 +49,7 @@ func (y *yamuxConn) Addr() string { func (y *yamuxConn) Accept() (conn net.Conn, err error) { if conn, err = y.Session.Accept(); err != nil { - if err == yamux.ErrSessionShutdown { + if err == yamux.ErrSessionShutdown || err == io.EOF { err = transport.ErrConnClosed } return diff --git a/net/transport/yamux/yamux.go b/net/transport/yamux/yamux.go index 18558604..1aa8420f 100644 --- a/net/transport/yamux/yamux.go +++ b/net/transport/yamux/yamux.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/yamux" "go.uber.org/zap" "net" + "sync" "time" ) @@ -25,6 +26,7 @@ func New() Yamux { // Yamux implements transport.Transport with tcp+yamux type Yamux interface { transport.Transport + AddListener(lis net.Listener) app.ComponentRunnable } @@ -37,15 +39,31 @@ type yamuxTransport struct { listCtx context.Context listCtxCancel context.CancelFunc yamuxConf *yamux.Config + mu sync.Mutex } func (y *yamuxTransport) Init(a *app.App) (err error) { y.secure = a.MustComponent(secureservice.CName).(secureservice.SecureService) y.conf = a.MustComponent("config").(configGetter).GetYamux() + if y.conf.DialTimeoutSec <= 0 { + y.conf.DialTimeoutSec = 10 + } + if y.conf.WriteTimeoutSec <= 0 { + y.conf.WriteTimeoutSec = 10 + } + y.yamuxConf = yamux.DefaultConfig() - y.yamuxConf.EnableKeepAlive = false + if y.conf.KeepAlivePeriodSec < 0 { + y.yamuxConf.EnableKeepAlive = false + } else { + y.yamuxConf.EnableKeepAlive = true + if y.conf.KeepAlivePeriodSec != 0 { + y.yamuxConf.KeepAliveInterval = time.Duration(y.conf.KeepAlivePeriodSec) * time.Second + } + } y.yamuxConf.StreamOpenTimeout = time.Duration(y.conf.DialTimeoutSec) * time.Second y.yamuxConf.ConnectionWriteTimeout = time.Duration(y.conf.WriteTimeoutSec) * time.Second + y.listCtx, y.listCtxCancel = context.WithCancel(context.Background()) return } @@ -57,6 +75,8 @@ func (y *yamuxTransport) Run(ctx context.Context) (err error) { if y.accepter == nil { return fmt.Errorf("can't run service without accepter") } + y.mu.Lock() + defer y.mu.Unlock() for _, listAddr := range y.conf.ListenAddrs { list, err := net.Listen("tcp", listAddr) if err != nil { @@ -64,7 +84,6 @@ func (y *yamuxTransport) Run(ctx context.Context) (err error) { } y.listeners = append(y.listeners, list) } - y.listCtx, y.listCtxCancel = context.WithCancel(context.Background()) for _, list := range y.listeners { go y.acceptLoop(y.listCtx, list) } @@ -75,6 +94,13 @@ func (y *yamuxTransport) SetAccepter(accepter transport.Accepter) { y.accepter = accepter } +func (y *yamuxTransport) AddListener(lis net.Listener) { + y.mu.Lock() + defer y.mu.Unlock() + y.listeners = append(y.listeners, lis) + go y.acceptLoop(y.listCtx, lis) +} + func (y *yamuxTransport) Dial(ctx context.Context, addr string) (mc transport.MultiConn, err error) { dialTimeout := time.Duration(y.conf.DialTimeoutSec) * time.Second conn, err := net.DialTimeout("tcp", addr, dialTimeout) @@ -88,7 +114,7 @@ func (y *yamuxTransport) Dial(ctx context.Context, addr string) (mc transport.Mu _ = conn.Close() return nil, err } - luc := connutil.NewLastUsageConn(conn) + luc := connutil.NewLastUsageConn(connutil.NewTimeout(conn, time.Duration(y.conf.WriteTimeoutSec)*time.Second)) sess, err := yamux.Client(luc, y.yamuxConf) if err != nil { return @@ -134,7 +160,7 @@ func (y *yamuxTransport) accept(conn net.Conn) { log.Warn("incoming connection handshake error", zap.Error(err)) return } - luc := connutil.NewLastUsageConn(conn) + luc := connutil.NewLastUsageConn(connutil.NewTimeout(conn, time.Duration(y.conf.WriteTimeoutSec)*time.Second)) sess, err := yamux.Server(luc, y.yamuxConf) if err != nil { log.Warn("incoming connection yamux session error", zap.Error(err)) diff --git a/net/transport/yamux/yamux_test.go b/net/transport/yamux/yamux_test.go index 02e1c322..1fd6ba3a 100644 --- a/net/transport/yamux/yamux_test.go +++ b/net/transport/yamux/yamux_test.go @@ -10,9 +10,9 @@ import ( "github.com/anyproto/any-sync/nodeconf/mock_nodeconf" "github.com/anyproto/any-sync/testutil/accounttest" "github.com/anyproto/any-sync/testutil/testnodeconf" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "io" "net" "sync" @@ -30,8 +30,12 @@ func TestYamuxTransport_Dial(t *testing.T) { mcC, err := fxC.Dial(ctx, fxS.addr) require.NoError(t, err) - require.Len(t, fxS.accepter.mcs, 1) - mcS := <-fxS.accepter.mcs + var mcS transport.MultiConn + select { + case mcS = <-fxS.accepter.mcs: + case <-time.After(time.Second * 5): + require.True(t, false, "timeout") + } var ( sData string @@ -69,11 +73,11 @@ func TestYamuxTransport_Dial(t *testing.T) { // no deadline - 69100 rps // common write deadline - 66700 rps // subconn write deadline - 67100 rps -func TestWriteBench(t *testing.T) { +func TestWriteBenchReuse(t *testing.T) { t.Skip() var ( numSubConn = 10 - numWrites = 100000 + numWrites = 10000 ) fxS := newFixture(t) @@ -124,6 +128,63 @@ func TestWriteBench(t *testing.T) { t.Logf("%.2f req per sec", float64(numWrites*numSubConn)/dur.Seconds()) } +func TestWriteBenchNew(t *testing.T) { + t.Skip() + var ( + numSubConn = 10 + numWrites = 10000 + ) + + fxS := newFixture(t) + defer fxS.finish(t) + fxC := newFixture(t) + defer fxC.finish(t) + + mcC, err := fxC.Dial(ctx, fxS.addr) + require.NoError(t, err) + mcS := <-fxS.accepter.mcs + + go func() { + for i := 0; i < numSubConn; i++ { + require.NoError(t, err) + go func() { + var b = make([]byte, 1024) + for { + conn, _ := mcS.Accept() + n, _ := conn.Read(b) + if n > 0 { + conn.Write(b[:n]) + } else { + _ = conn.Close() + break + } + conn.Close() + } + }() + } + }() + + var wg sync.WaitGroup + wg.Add(numSubConn) + st := time.Now() + for i := 0; i < numSubConn; i++ { + go func() { + defer wg.Done() + for j := 0; j < numWrites; j++ { + sc, err := mcC.Open(ctx) + require.NoError(t, err) + var b = []byte("some data some data some data some data some data some data some data some data some data") + sc.Write(b) + sc.Read(b) + sc.Close() + } + }() + } + wg.Wait() + dur := time.Since(st) + t.Logf("%.2f req per sec", float64(numWrites*numSubConn)/dur.Seconds()) +} + type fixture struct { *yamuxTransport a *app.App diff --git a/nodeconf/mock_nodeconf/mock_nodeconf.go b/nodeconf/mock_nodeconf/mock_nodeconf.go index 353bf921..9b4c7d58 100644 --- a/nodeconf/mock_nodeconf/mock_nodeconf.go +++ b/nodeconf/mock_nodeconf/mock_nodeconf.go @@ -11,7 +11,7 @@ import ( app "github.com/anyproto/any-sync/app" nodeconf "github.com/anyproto/any-sync/nodeconf" chash "github.com/anyproto/go-chash" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockService is a mock of Service interface. diff --git a/util/crypto/ed25519_test.go b/util/crypto/ed25519_test.go index 92565723..6b571699 100644 --- a/util/crypto/ed25519_test.go +++ b/util/crypto/ed25519_test.go @@ -2,8 +2,9 @@ package crypto import ( "crypto/rand" - "github.com/stretchr/testify/require" "testing" + + "github.com/stretchr/testify/require" ) func Test_EncryptDecrypt(t *testing.T) { diff --git a/util/periodicsync/mock_periodicsync/mock_periodicsync.go b/util/periodicsync/mock_periodicsync/mock_periodicsync.go index d34d6162..909b32e1 100644 --- a/util/periodicsync/mock_periodicsync/mock_periodicsync.go +++ b/util/periodicsync/mock_periodicsync/mock_periodicsync.go @@ -7,7 +7,7 @@ package mock_periodicsync import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockPeriodicSync is a mock of PeriodicSync interface. diff --git a/util/periodicsync/periodicsync_test.go b/util/periodicsync/periodicsync_test.go index 35dd848c..f45a940b 100644 --- a/util/periodicsync/periodicsync_test.go +++ b/util/periodicsync/periodicsync_test.go @@ -3,8 +3,8 @@ package periodicsync import ( "context" "github.com/anyproto/any-sync/app/logger" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "testing" "time" ) diff --git a/util/strkey/strkey_test.go b/util/strkey/strkey_test.go index 07fc2a64..d33f49d8 100644 --- a/util/strkey/strkey_test.go +++ b/util/strkey/strkey_test.go @@ -1,9 +1,10 @@ package strkey import ( + "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func TestDecode(t *testing.T) {