diff --git a/.mockery.yaml b/.mockery.yaml index bdffa3a15..8978fb726 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -165,3 +165,9 @@ packages: github.com/anyproto/anytype-heart/core/block/object/idresolver: interfaces: Resolver: + github.com/anyproto/anytype-heart/space/spacecore/clientserver: + interfaces: + ClientServer: + github.com/anyproto/anytype-heart/core/syncstatus/p2p: + interfaces: + StatusUpdateSender: \ No newline at end of file diff --git a/core/anytype/bootstrap.go b/core/anytype/bootstrap.go index 479ddae8b..81f954361 100644 --- a/core/anytype/bootstrap.go +++ b/core/anytype/bootstrap.go @@ -73,6 +73,7 @@ import ( "github.com/anyproto/anytype-heart/core/recordsbatcher" "github.com/anyproto/anytype-heart/core/subscription" "github.com/anyproto/anytype-heart/core/syncstatus" + "github.com/anyproto/anytype-heart/core/syncstatus/p2p" "github.com/anyproto/anytype-heart/core/wallet" "github.com/anyproto/anytype-heart/metrics" "github.com/anyproto/anytype-heart/pkg/lib/core" @@ -228,6 +229,7 @@ func Bootstrap(a *app.App, components ...app.Component) { Register(virtualspaceservice.New()). Register(spacecore.New()). Register(idresolver.New()). + Register(p2p.NewObservers()). Register(localdiscovery.New()). Register(peermanager.New()). Register(typeprovider.New()). diff --git a/core/syncstatus/p2p/mock_p2p/mock_StatusUpdateSender.go b/core/syncstatus/p2p/mock_p2p/mock_StatusUpdateSender.go new file mode 100644 index 000000000..db3fa37cc --- /dev/null +++ b/core/syncstatus/p2p/mock_p2p/mock_StatusUpdateSender.go @@ -0,0 +1,288 @@ +// Code generated by mockery. DO NOT EDIT. + +package mock_p2p + +import ( + context "context" + + app "github.com/anyproto/any-sync/app" + + mock "github.com/stretchr/testify/mock" + + peerstatus "github.com/anyproto/any-sync/commonspace/peerstatus" +) + +// MockStatusUpdateSender is an autogenerated mock type for the StatusUpdateSender type +type MockStatusUpdateSender struct { + mock.Mock +} + +type MockStatusUpdateSender_Expecter struct { + mock *mock.Mock +} + +func (_m *MockStatusUpdateSender) EXPECT() *MockStatusUpdateSender_Expecter { + return &MockStatusUpdateSender_Expecter{mock: &_m.Mock} +} + +// Close provides a mock function with given fields: ctx +func (_m *MockStatusUpdateSender) Close(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockStatusUpdateSender_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type MockStatusUpdateSender_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockStatusUpdateSender_Expecter) Close(ctx interface{}) *MockStatusUpdateSender_Close_Call { + return &MockStatusUpdateSender_Close_Call{Call: _e.mock.On("Close", ctx)} +} + +func (_c *MockStatusUpdateSender_Close_Call) Run(run func(ctx context.Context)) *MockStatusUpdateSender_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockStatusUpdateSender_Close_Call) Return(err error) *MockStatusUpdateSender_Close_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockStatusUpdateSender_Close_Call) RunAndReturn(run func(context.Context) error) *MockStatusUpdateSender_Close_Call { + _c.Call.Return(run) + return _c +} + +// Init provides a mock function with given fields: a +func (_m *MockStatusUpdateSender) Init(a *app.App) error { + ret := _m.Called(a) + + if len(ret) == 0 { + panic("no return value specified for Init") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*app.App) error); ok { + r0 = rf(a) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockStatusUpdateSender_Init_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Init' +type MockStatusUpdateSender_Init_Call struct { + *mock.Call +} + +// Init is a helper method to define mock.On call +// - a *app.App +func (_e *MockStatusUpdateSender_Expecter) Init(a interface{}) *MockStatusUpdateSender_Init_Call { + return &MockStatusUpdateSender_Init_Call{Call: _e.mock.On("Init", a)} +} + +func (_c *MockStatusUpdateSender_Init_Call) Run(run func(a *app.App)) *MockStatusUpdateSender_Init_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*app.App)) + }) + return _c +} + +func (_c *MockStatusUpdateSender_Init_Call) Return(err error) *MockStatusUpdateSender_Init_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockStatusUpdateSender_Init_Call) RunAndReturn(run func(*app.App) error) *MockStatusUpdateSender_Init_Call { + _c.Call.Return(run) + return _c +} + +// Name provides a mock function with given fields: +func (_m *MockStatusUpdateSender) Name() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Name") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockStatusUpdateSender_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' +type MockStatusUpdateSender_Name_Call struct { + *mock.Call +} + +// Name is a helper method to define mock.On call +func (_e *MockStatusUpdateSender_Expecter) Name() *MockStatusUpdateSender_Name_Call { + return &MockStatusUpdateSender_Name_Call{Call: _e.mock.On("Name")} +} + +func (_c *MockStatusUpdateSender_Name_Call) Run(run func()) *MockStatusUpdateSender_Name_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockStatusUpdateSender_Name_Call) Return(name string) *MockStatusUpdateSender_Name_Call { + _c.Call.Return(name) + return _c +} + +func (_c *MockStatusUpdateSender_Name_Call) RunAndReturn(run func() string) *MockStatusUpdateSender_Name_Call { + _c.Call.Return(run) + return _c +} + +// Run provides a mock function with given fields: ctx +func (_m *MockStatusUpdateSender) Run(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Run") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockStatusUpdateSender_Run_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Run' +type MockStatusUpdateSender_Run_Call struct { + *mock.Call +} + +// Run is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockStatusUpdateSender_Expecter) Run(ctx interface{}) *MockStatusUpdateSender_Run_Call { + return &MockStatusUpdateSender_Run_Call{Call: _e.mock.On("Run", ctx)} +} + +func (_c *MockStatusUpdateSender_Run_Call) Run(run func(ctx context.Context)) *MockStatusUpdateSender_Run_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockStatusUpdateSender_Run_Call) Return(err error) *MockStatusUpdateSender_Run_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockStatusUpdateSender_Run_Call) RunAndReturn(run func(context.Context) error) *MockStatusUpdateSender_Run_Call { + _c.Call.Return(run) + return _c +} + +// SendNewStatus provides a mock function with given fields: status +func (_m *MockStatusUpdateSender) SendNewStatus(status peerstatus.Status) { + _m.Called(status) +} + +// MockStatusUpdateSender_SendNewStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendNewStatus' +type MockStatusUpdateSender_SendNewStatus_Call struct { + *mock.Call +} + +// SendNewStatus is a helper method to define mock.On call +// - status peerstatus.Status +func (_e *MockStatusUpdateSender_Expecter) SendNewStatus(status interface{}) *MockStatusUpdateSender_SendNewStatus_Call { + return &MockStatusUpdateSender_SendNewStatus_Call{Call: _e.mock.On("SendNewStatus", status)} +} + +func (_c *MockStatusUpdateSender_SendNewStatus_Call) Run(run func(status peerstatus.Status)) *MockStatusUpdateSender_SendNewStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(peerstatus.Status)) + }) + return _c +} + +func (_c *MockStatusUpdateSender_SendNewStatus_Call) Return() *MockStatusUpdateSender_SendNewStatus_Call { + _c.Call.Return() + return _c +} + +func (_c *MockStatusUpdateSender_SendNewStatus_Call) RunAndReturn(run func(peerstatus.Status)) *MockStatusUpdateSender_SendNewStatus_Call { + _c.Call.Return(run) + return _c +} + +// SendPeerUpdate provides a mock function with given fields: +func (_m *MockStatusUpdateSender) SendPeerUpdate() { + _m.Called() +} + +// MockStatusUpdateSender_SendPeerUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendPeerUpdate' +type MockStatusUpdateSender_SendPeerUpdate_Call struct { + *mock.Call +} + +// SendPeerUpdate is a helper method to define mock.On call +func (_e *MockStatusUpdateSender_Expecter) SendPeerUpdate() *MockStatusUpdateSender_SendPeerUpdate_Call { + return &MockStatusUpdateSender_SendPeerUpdate_Call{Call: _e.mock.On("SendPeerUpdate")} +} + +func (_c *MockStatusUpdateSender_SendPeerUpdate_Call) Run(run func()) *MockStatusUpdateSender_SendPeerUpdate_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockStatusUpdateSender_SendPeerUpdate_Call) Return() *MockStatusUpdateSender_SendPeerUpdate_Call { + _c.Call.Return() + return _c +} + +func (_c *MockStatusUpdateSender_SendPeerUpdate_Call) RunAndReturn(run func()) *MockStatusUpdateSender_SendPeerUpdate_Call { + _c.Call.Return(run) + return _c +} + +// NewMockStatusUpdateSender creates a new instance of MockStatusUpdateSender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockStatusUpdateSender(t interface { + mock.TestingT + Cleanup(func()) +}) *MockStatusUpdateSender { + mock := &MockStatusUpdateSender{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/syncstatus/p2p/observer_test.go b/core/syncstatus/p2p/observer_test.go new file mode 100644 index 000000000..3517033bf --- /dev/null +++ b/core/syncstatus/p2p/observer_test.go @@ -0,0 +1,170 @@ +package p2p + +import ( + "context" + "testing" + + "github.com/anyproto/any-sync/commonspace/peerstatus" + "github.com/stretchr/testify/assert" + + "github.com/anyproto/anytype-heart/pb" +) + +func TestObservers_BroadcastStatus(t *testing.T) { + t.Run("TestObservers_BroadcastStatus on registered observers", func(t *testing.T) { + // given + observers := NewObservers() + status := newFixture(t, "spaceId1", pb.EventP2PStatus_NotConnected) + err := status.Run(context.Background()) + assert.Nil(t, err) + observers.AddObserver("spaceId1", status) + + status2 := newFixture(t, "spaceId2", pb.EventP2PStatus_NotConnected) + err = status2.Run(context.Background()) + assert.Nil(t, err) + observers.AddObserver("spaceId2", status2) + + // when + status.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId1", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + status2.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId2", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + observers.BroadcastStatus(peerstatus.Connected) + for status.StatusUpdateSender.(*p2pStatus).status != peerstatus.Connected { + } + + for status2.StatusUpdateSender.(*p2pStatus).status != peerstatus.Connected { + } + + // then + assert.Equal(t, peerstatus.Connected, status.StatusUpdateSender.(*p2pStatus).status) + assert.Equal(t, peerstatus.Connected, status2.StatusUpdateSender.(*p2pStatus).status) + + err = status.Close(context.Background()) + assert.Nil(t, err) + err = status2.Close(context.Background()) + assert.Nil(t, err) + }) +} + +func TestObservers_BroadcastPeerUpdate(t *testing.T) { + t.Run("BroadcastPeerUpdate on registered observers", func(t *testing.T) { + // given + observers := NewObservers() + status := newFixture(t, "spaceId1", pb.EventP2PStatus_NotConnected) + err := status.Run(context.Background()) + assert.Nil(t, err) + observers.AddObserver("spaceId1", status) + + status2 := newFixture(t, "spaceId2", pb.EventP2PStatus_NotConnected) + err = status2.Run(context.Background()) + assert.Nil(t, err) + observers.AddObserver("spaceId2", status2) + + // when + status.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId1", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + status2.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId2", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + status.store.UpdateLocalPeer("peerId", []string{"spaceId1"}) + status2.store.UpdateLocalPeer("peerId", []string{"spaceId2"}) + observers.BroadcastPeerUpdate() + for status.StatusUpdateSender.(*p2pStatus).status != peerstatus.Connected { + } + + for status2.StatusUpdateSender.(*p2pStatus).status != peerstatus.Connected { + } + + // then + assert.Equal(t, peerstatus.Connected, status.StatusUpdateSender.(*p2pStatus).status) + assert.Equal(t, peerstatus.Connected, status2.StatusUpdateSender.(*p2pStatus).status) + + err = status.Close(context.Background()) + assert.Nil(t, err) + err = status2.Close(context.Background()) + assert.Nil(t, err) + }) +} + +func TestObservers_SendPeerUpdate(t *testing.T) { + t.Run("BroadcastPeerUpdate on registered observers", func(t *testing.T) { + // given + observers := NewObservers() + status := newFixture(t, "spaceId1", pb.EventP2PStatus_NotConnected) + err := status.Run(context.Background()) + assert.Nil(t, err) + observers.AddObserver("spaceId1", status) + + status2 := newFixture(t, "spaceId2", pb.EventP2PStatus_NotConnected) + err = status2.Run(context.Background()) + assert.Nil(t, err) + observers.AddObserver("spaceId2", status2) + + // when + status.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId1", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + status.store.UpdateLocalPeer("peerId", []string{"spaceId1"}) + observers.SendPeerUpdate([]string{"spaceId1"}) + for status.StatusUpdateSender.(*p2pStatus).status != peerstatus.Connected { + } + + // then + assert.Equal(t, peerstatus.Connected, status.StatusUpdateSender.(*p2pStatus).status) + assert.Equal(t, peerstatus.NotConnected, status2.StatusUpdateSender.(*p2pStatus).status) + + err = status.Close(context.Background()) + assert.Nil(t, err) + err = status2.Close(context.Background()) + assert.Nil(t, err) + }) +} diff --git a/core/syncstatus/p2p/observers.go b/core/syncstatus/p2p/observers.go new file mode 100644 index 000000000..479eaa8dd --- /dev/null +++ b/core/syncstatus/p2p/observers.go @@ -0,0 +1,62 @@ +package p2p + +import ( + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/peerstatus" + + "github.com/anyproto/anytype-heart/pkg/lib/logging" +) + +var log = logging.Logger(observerServiceName) + +const observerServiceName = "core.syncstatus.p2p.observers" + +type ObserverComponent interface { + app.Component + AddObserver(spaceId string, observer StatusUpdateSender) + BroadcastStatus(status peerstatus.Status) + SendPeerUpdate(spaceIds []string) + BroadcastPeerUpdate() +} + +type Observers struct { + observer map[string]StatusUpdateSender +} + +func (o *Observers) SendPeerUpdate(spaceIds []string) { + for _, spaceId := range spaceIds { + if observer, ok := o.observer[spaceId]; ok { + observer.SendPeerUpdate() + return + } + log.Errorf("observer not registered for space %s", spaceIds) + } +} + +func (o *Observers) BroadcastStatus(status peerstatus.Status) { + for _, observer := range o.observer { + observer.SendNewStatus(status) + } +} + +func (o *Observers) BroadcastPeerUpdate() { + for _, observer := range o.observer { + observer.SendPeerUpdate() + } +} + +func (o *Observers) Init(a *app.App) (err error) { + return +} + +func (o *Observers) Name() (name string) { + return observerServiceName +} + +func (o *Observers) AddObserver(spaceId string, observer StatusUpdateSender) { + o.observer[spaceId] = observer +} + +func NewObservers() *Observers { + return &Observers{observer: make(map[string]StatusUpdateSender, 0)} +} diff --git a/core/syncstatus/p2p/status.go b/core/syncstatus/p2p/status.go new file mode 100644 index 000000000..cae879752 --- /dev/null +++ b/core/syncstatus/p2p/status.go @@ -0,0 +1,147 @@ +package p2p + +import ( + "context" + "sync" + "time" + + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/peerstatus" + + "github.com/anyproto/anytype-heart/core/event" + "github.com/anyproto/anytype-heart/pb" + "github.com/anyproto/anytype-heart/space/spacecore/peerstore" +) + +const CName = "core.syncstatus.p2p" + +type StatusUpdateSender interface { + app.ComponentRunnable + SendPeerUpdate() + SendNewStatus(status peerstatus.Status) +} + +type p2pStatus struct { + spaceId string + peerStore peerstore.PeerStore + eventSender event.Sender + contextCancel context.CancelFunc + ctx context.Context + + sync.Mutex + status peerstatus.Status + + forceCheckSpace chan struct{} + updateStatus chan peerstatus.Status +} + +func NewP2PStatus(spaceId string) StatusUpdateSender { + return &p2pStatus{forceCheckSpace: make(chan struct{}), updateStatus: make(chan peerstatus.Status), spaceId: spaceId} +} + +func (p *p2pStatus) Init(a *app.App) (err error) { + p.peerStore = app.MustComponent[peerstore.PeerStore](a) + p.eventSender = app.MustComponent[event.Sender](a) + observerComponent := app.MustComponent[ObserverComponent](a) + observerComponent.AddObserver(p.spaceId, p) + return +} + +func (p *p2pStatus) Name() (name string) { + return CName +} + +func (p *p2pStatus) Run(ctx context.Context) (err error) { + p.ctx, p.contextCancel = context.WithCancel(context.Background()) + go p.checkP2PDevices() + return nil +} + +func (p *p2pStatus) Close(ctx context.Context) (err error) { + if p.contextCancel != nil { + p.contextCancel() + } + return +} + +func (p *p2pStatus) SendPeerUpdate() { + p.forceCheckSpace <- struct{}{} +} + +func (p *p2pStatus) SendNewStatus(status peerstatus.Status) { + p.updateStatus <- status +} + +func (p *p2pStatus) checkP2PDevices() { + timer := time.NewTimer(10 * time.Second) + defer timer.Stop() + p.updateSpaceP2PStatus() + for { + select { + case <-p.ctx.Done(): + return + case <-timer.C: + p.updateSpaceP2PStatus() + case <-p.forceCheckSpace: + p.updateSpaceP2PStatus() + case newStatus := <-p.updateStatus: + p.sendNewStatus(newStatus) + } + } +} + +func (p *p2pStatus) updateSpaceP2PStatus() { + p.Lock() + defer p.Unlock() + peerIds := p.peerStore.LocalPeerIds(p.spaceId) + if p.status != peerstatus.Unknown { + // avoiding sending of redundant event + if p.status == peerstatus.Connected && len(peerIds) == 0 { + p.sendEvent(p.spaceId, pb.EventP2PStatus_NotConnected) + p.status = peerstatus.NotConnected + } + if (p.status == peerstatus.NotConnected || p.status == peerstatus.NotPossible) && len(peerIds) > 0 { + p.sendEvent(p.spaceId, pb.EventP2PStatus_Connected) + p.status = peerstatus.Connected + } + } else { + if len(peerIds) > 0 { + p.sendEvent(p.spaceId, pb.EventP2PStatus_Connected) + p.status = peerstatus.Connected + } else { + p.sendEvent(p.spaceId, pb.EventP2PStatus_NotConnected) + p.status = peerstatus.NotConnected + } + } +} + +func (p *p2pStatus) sendNewStatus(status peerstatus.Status) { + var pbStatus pb.EventP2PStatusStatus + switch status { + case peerstatus.Connected: + pbStatus = pb.EventP2PStatus_Connected + case peerstatus.NotConnected: + pbStatus = pb.EventP2PStatus_NotConnected + case peerstatus.NotPossible: + pbStatus = pb.EventP2PStatus_NotPossible + } + p.Lock() + p.status = status + p.Unlock() + p.sendEvent(p.spaceId, pbStatus) +} + +func (p *p2pStatus) sendEvent(spaceId string, status pb.EventP2PStatusStatus) { + p.eventSender.Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: spaceId, + Status: status, + }, + }, + }, + }, + }) +} diff --git a/core/syncstatus/p2p/status_test.go b/core/syncstatus/p2p/status_test.go new file mode 100644 index 000000000..41a8b24bc --- /dev/null +++ b/core/syncstatus/p2p/status_test.go @@ -0,0 +1,353 @@ +package p2p + +import ( + "context" + "testing" + "time" + + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/peerstatus" + "github.com/anyproto/any-sync/nodeconf/mock_nodeconf" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + "github.com/anyproto/anytype-heart/core/event/mock_event" + "github.com/anyproto/anytype-heart/pb" + "github.com/anyproto/anytype-heart/space/spacecore/peerstore" + "github.com/anyproto/anytype-heart/tests/testutil" +) + +type fixture struct { + StatusUpdateSender + sender *mock_event.MockSender + service *mock_nodeconf.MockService + store peerstore.PeerStore +} + +func TestP2PStatus_Init(t *testing.T) { + t.Run("success", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_NotConnected) + + // when + err := f.Run(context.Background()) + assert.Nil(t, err) + + // then + err = f.Close(context.Background()) + assert.Nil(t, err) + }) +} + +func TestP2pStatus_SendNewStatus(t *testing.T) { + t.Run("send NotPossible status", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_NotConnected) + err := f.Run(context.Background()) + assert.Nil(t, err) + + // when + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_NotPossible, + }, + }, + }, + }, + }) + f.SendNewStatus(peerstatus.NotPossible) + + // then + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.NotPossible, status.status) + err = f.Close(context.Background()) + assert.Nil(t, err) + }) + + t.Run("send NotConnected status", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_NotConnected) + err := f.Run(context.Background()) + assert.Nil(t, err) + + // when + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_NotConnected, + }, + }, + }, + }, + }) + f.SendNewStatus(peerstatus.NotConnected) + + // then + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.NotConnected, status.status) + + err = f.Close(context.Background()) + assert.Nil(t, err) + }) + t.Run("send NotConnected status", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_NotConnected) + err := f.Run(context.Background()) + assert.Nil(t, err) + + // when + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_NotConnected, + }, + }, + }, + }, + }) + f.SendNewStatus(peerstatus.NotConnected) + + // then + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.NotConnected, status.status) + err = f.Close(context.Background()) + assert.Nil(t, err) + }) + t.Run("send Connected status", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_NotConnected) + err := f.Run(context.Background()) + assert.Nil(t, err) + + // when + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + f.SendNewStatus(peerstatus.Connected) + + // then + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.Connected, status.status) + err = f.Close(context.Background()) + assert.Nil(t, err) + }) +} + +func TestP2pStatus_SendPeerUpdate(t *testing.T) { + t.Run("send Connected status, because we have peers", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_Connected) + f.store.UpdateLocalPeer("peerId", []string{"spaceId"}) + + // when + err := f.Run(context.Background()) + assert.Nil(t, err) + f.SendPeerUpdate() + + // then + err = f.Close(context.Background()) + assert.Nil(t, err) + + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.Connected, status.status) + }) + t.Run("send NotConnected status, because we have peer were disconnected", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_Connected) + f.store.UpdateLocalPeer("peerId", []string{"spaceId"}) + + // when + err := f.Run(context.Background()) + assert.Nil(t, err) + + for f.StatusUpdateSender.(*p2pStatus).status != peerstatus.Connected { + } + + f.store.RemoveLocalPeer("peerId") + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_NotConnected, + }, + }, + }, + }, + }) + f.SendPeerUpdate() + err = waitForStatus(f.StatusUpdateSender.(*p2pStatus), peerstatus.NotConnected) + assert.Nil(t, err) + + // then + err = f.Close(context.Background()) + assert.Nil(t, err) + + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.NotConnected, status.status) + }) + t.Run("connection was not possible, but after a while starts working", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_NotConnected) + + // when + err := f.Run(context.Background()) + assert.Nil(t, err) + + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_NotPossible, + }, + }, + }, + }, + }) + f.SendNewStatus(peerstatus.NotPossible) + err = waitForStatus(f.StatusUpdateSender.(*p2pStatus), peerstatus.NotPossible) + assert.Nil(t, err) + + f.store.UpdateLocalPeer("peerId", []string{"spaceId"}) + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + f.SendPeerUpdate() + err = waitForStatus(f.StatusUpdateSender.(*p2pStatus), peerstatus.Connected) + assert.Nil(t, err) + + // then + err = f.Close(context.Background()) + assert.Nil(t, err) + + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.Connected, status.status) + }) + t.Run("no peers were connected, but after a while one is connected", func(t *testing.T) { + // given + f := newFixture(t, "spaceId", pb.EventP2PStatus_NotConnected) + + // when + err := f.Run(context.Background()) + assert.Nil(t, err) + + err = waitForStatus(f.StatusUpdateSender.(*p2pStatus), peerstatus.NotConnected) + assert.Nil(t, err) + + f.store.UpdateLocalPeer("peerId", []string{"spaceId"}) + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: "spaceId", + Status: pb.EventP2PStatus_Connected, + }, + }, + }, + }, + }) + f.SendPeerUpdate() + err = waitForStatus(f.StatusUpdateSender.(*p2pStatus), peerstatus.Connected) + assert.Nil(t, err) + + // then + err = f.Close(context.Background()) + assert.Nil(t, err) + + status := f.StatusUpdateSender.(*p2pStatus) + assert.NotNil(t, status) + assert.Equal(t, peerstatus.Connected, status.status) + }) +} + +func newFixture(t *testing.T, spaceId string, initialStatus pb.EventP2PStatusStatus) *fixture { + ctrl := gomock.NewController(t) + a := &app.App{} + ctx := context.Background() + sender := mock_event.NewMockSender(t) + service := mock_nodeconf.NewMockService(ctrl) + service.EXPECT().Name().Return("common.nodeconf").AnyTimes() + store := peerstore.New() + a.Register(testutil.PrepareMock(ctx, a, sender)). + Register(service). + Register(NewObservers()). + Register(store) + err := store.Init(a) + assert.Nil(t, err) + status := NewP2PStatus(spaceId) + f := &fixture{ + StatusUpdateSender: status, + sender: sender, + service: service, + store: store, + } + err = f.Init(a) + assert.Nil(t, err) + f.sender.EXPECT().Broadcast(&pb.Event{ + Messages: []*pb.EventMessage{ + { + Value: &pb.EventMessageValueOfP2PStatusUpdate{ + P2PStatusUpdate: &pb.EventP2PStatusUpdate{ + SpaceId: spaceId, + Status: initialStatus, + }, + }, + }, + }, + }).Maybe() + return f +} + +func waitForStatus(statusSender *p2pStatus, expectedStatus peerstatus.Status) error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if statusSender.status == expectedStatus { + return nil + } + } + } +} diff --git a/docs/proto.md b/docs/proto.md index 915c91069..dfaaa8d86 100644 --- a/docs/proto.md +++ b/docs/proto.md @@ -24637,6 +24637,7 @@ Removes document from subscription | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | +| spaceId | [string](#string) | | | | status | [Event.P2PStatus.Status](#anytype-Event-P2PStatus-Status) | | | @@ -25037,8 +25038,8 @@ Precondition: user A and user B opened the same block | Name | Number | Description | | ---- | ------ | ----------- | -| Possible | 0 | | -| Connected | 1 | | +| Connected | 0 | | +| NotPossible | 1 | | | NotConnected | 2 | | diff --git a/go.mod b/go.mod index 1c78edb89..b1772e885 100644 --- a/go.mod +++ b/go.mod @@ -231,7 +231,7 @@ require ( github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/pseudomuto/protokit v0.2.1 // indirect - github.com/quic-go/quic-go v0.43.0 // indirect + github.com/quic-go/quic-go v0.43.1 // indirect github.com/rs/cors v1.7.0 // indirect github.com/rs/zerolog v1.29.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect @@ -294,3 +294,5 @@ replace github.com/araddon/dateparse => github.com/mehanizm/dateparse v0.0.0-202 replace github.com/multiformats/go-multiaddr => github.com/anyproto/go-multiaddr v0.8.1-0.20221213144344-0b6b93adaec4 replace github.com/gogo/protobuf => github.com/anyproto/protobuf v1.3.3-0.20240201225420-6e325cf0ac38 + +replace github.com/anyproto/any-sync => ./../any-sync diff --git a/go.sum b/go.sum index a0a5d4899..d21b3264f 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,6 @@ github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxB github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/anyproto/any-sync v0.4.14 h1:8AbckYHcy8gRpP/+kULDt2/l3jbaz3Uu5A2mBGTsSGo= -github.com/anyproto/any-sync v0.4.14/go.mod h1:NvsYUYn9HnPC6tnx+VKicGQf78pN3GZFQ28CoDAN27c= github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580 h1:Ba80IlCCxkZ9H1GF+7vFu/TSpPvbpDCxXJ5ogc4euYc= github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580/go.mod h1:T/uWAYxrXdaXw64ihI++9RMbKTCpKd/yE9+saARew7k= github.com/anyproto/go-chash v0.1.0 h1:I9meTPjXFRfXZHRJzjOHC/XF7Q5vzysKkiT/grsogXY= @@ -1257,8 +1255,8 @@ github.com/pseudomuto/protokit v0.2.1 h1:kCYpE3thoR6Esm0CUvd5xbrDTOZPvQPTDeyXpZf github.com/pseudomuto/protokit v0.2.1/go.mod h1:gt7N5Rz2flBzYafvaxyIxMZC0TTF5jDZfRnw25hAAyo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/quic-go v0.43.0 h1:sjtsTKWX0dsHpuMJvLxGqoQdtgJnbAPWY+W+5vjYW/g= -github.com/quic-go/quic-go v0.43.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= +github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= diff --git a/net/addrs/interface.go b/net/addrs/interface.go index 8afdbc7cb..1ef8b9912 100644 --- a/net/addrs/interface.go +++ b/net/addrs/interface.go @@ -5,6 +5,7 @@ package addrs import ( "net" + "strings" "github.com/anyproto/anytype-heart/util/slice" ) @@ -26,7 +27,7 @@ func GetInterfacesAddrs() (iAddrs InterfacesAddrs, err error) { if err != nil { return } - iAddrs.Addrs = addrs + iAddrs.Addrs = slice.Filter(addrs, func(addr net.Addr) bool { return !strings.HasPrefix(addr.String(), "127.0.0.1") }) ifaces, err := net.Interfaces() if err != nil { return @@ -34,7 +35,7 @@ func GetInterfacesAddrs() (iAddrs InterfacesAddrs, err error) { iAddrs.Interfaces = ifaces iAddrs.Interfaces = slice.Filter(iAddrs.Interfaces, func(iface net.Interface) bool { - return iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagMulticast != 0 + return iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagMulticast != 0 && iface.Flags&net.FlagLoopback == 0 }) return } diff --git a/pb/events.pb.go b/pb/events.pb.go index ae299c284..b49688eb3 100644 --- a/pb/events.pb.go +++ b/pb/events.pb.go @@ -98,20 +98,20 @@ func (EventStatusThreadSyncStatus) EnumDescriptor() ([]byte, []int) { type EventP2PStatusStatus int32 const ( - EventP2PStatus_Possible EventP2PStatusStatus = 0 - EventP2PStatus_Connected EventP2PStatusStatus = 1 + EventP2PStatus_Connected EventP2PStatusStatus = 0 + EventP2PStatus_NotPossible EventP2PStatusStatus = 1 EventP2PStatus_NotConnected EventP2PStatusStatus = 2 ) var EventP2PStatusStatus_name = map[int32]string{ - 0: "Possible", - 1: "Connected", + 0: "Connected", + 1: "NotPossible", 2: "NotConnected", } var EventP2PStatusStatus_value = map[string]int32{ - "Possible": 0, - "Connected": 1, + "Connected": 0, + "NotPossible": 1, "NotConnected": 2, } @@ -10794,7 +10794,8 @@ func (m *EventP2PStatus) XXX_DiscardUnknown() { var xxx_messageInfo_EventP2PStatus proto.InternalMessageInfo type EventP2PStatusUpdate struct { - Status EventP2PStatusStatus `protobuf:"varint,1,opt,name=status,proto3,enum=anytype.EventP2PStatusStatus" json:"status,omitempty"` + SpaceId string `protobuf:"bytes,1,opt,name=spaceId,proto3" json:"spaceId,omitempty"` + Status EventP2PStatusStatus `protobuf:"varint,2,opt,name=status,proto3,enum=anytype.EventP2PStatusStatus" json:"status,omitempty"` } func (m *EventP2PStatusUpdate) Reset() { *m = EventP2PStatusUpdate{} } @@ -10830,11 +10831,18 @@ func (m *EventP2PStatusUpdate) XXX_DiscardUnknown() { var xxx_messageInfo_EventP2PStatusUpdate proto.InternalMessageInfo +func (m *EventP2PStatusUpdate) GetSpaceId() string { + if m != nil { + return m.SpaceId + } + return "" +} + func (m *EventP2PStatusUpdate) GetStatus() EventP2PStatusStatus { if m != nil { return m.Status } - return EventP2PStatus_Possible + return EventP2PStatus_Connected } type ResponseEvent struct { @@ -11264,356 +11272,356 @@ func init() { func init() { proto.RegisterFile("pb/protos/events.proto", fileDescriptor_a966342d378ae5f5) } var fileDescriptor_a966342d378ae5f5 = []byte{ - // 5569 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7c, 0x49, 0x8c, 0x1c, 0xc9, - 0x75, 0x76, 0xd7, 0x5e, 0xf5, 0x9a, 0xdd, 0x2c, 0x06, 0x39, 0x9c, 0x9c, 0x64, 0x4f, 0x0f, 0xa7, - 0xb9, 0x6a, 0x86, 0x53, 0xe4, 0x70, 0x6b, 0x8a, 0xc3, 0x21, 0xd9, 0x1b, 0xa7, 0x8b, 0x4b, 0xb3, - 0xff, 0x68, 0x92, 0x1a, 0x8d, 0x04, 0x41, 0xd9, 0x99, 0xd1, 0xd5, 0x29, 0x56, 0x67, 0x96, 0x32, - 0xb3, 0x9a, 0x6c, 0xe9, 0xb7, 0x6c, 0x78, 0x81, 0x2e, 0x36, 0xec, 0x93, 0x6c, 0xf8, 0x66, 0xc3, - 0x06, 0x7c, 0x30, 0x0c, 0x0b, 0xbe, 0xe8, 0x64, 0x18, 0x30, 0x0c, 0x78, 0x3b, 0xc8, 0x3e, 0x18, - 0xbe, 0x49, 0x98, 0xb9, 0xf8, 0x62, 0xc0, 0x0b, 0x60, 0xf8, 0x64, 0x18, 0xb1, 0x64, 0x66, 0x44, - 0x2e, 0x95, 0x55, 0x9a, 0x91, 0x17, 0x78, 0x4e, 0xdd, 0xf1, 0xe2, 0x7d, 0x5f, 0x6c, 0x2f, 0x5e, - 0x44, 0xbc, 0x8c, 0x28, 0x38, 0x3e, 0xd8, 0xbe, 0x38, 0xf0, 0xdc, 0xc0, 0xf5, 0x2f, 0x92, 0x7d, - 0xe2, 0x04, 0x7e, 0x87, 0xa5, 0x50, 0xc3, 0x70, 0x0e, 0x82, 0x83, 0x01, 0xd1, 0x4f, 0x0f, 0x9e, - 0xf7, 0x2e, 0xf6, 0xed, 0xed, 0x8b, 0x83, 0xed, 0x8b, 0x7b, 0xae, 0x45, 0xfa, 0xa1, 0x3a, 0x4b, - 0x08, 0x75, 0x7d, 0xae, 0xe7, 0xba, 0xbd, 0x3e, 0xe1, 0x79, 0xdb, 0xc3, 0x9d, 0x8b, 0x7e, 0xe0, - 0x0d, 0xcd, 0x80, 0xe7, 0x2e, 0xfc, 0xdd, 0xf7, 0x4b, 0x50, 0x5b, 0xa3, 0xf4, 0xe8, 0x32, 0x34, - 0xf7, 0x88, 0xef, 0x1b, 0x3d, 0xe2, 0x6b, 0xa5, 0x93, 0x95, 0xf3, 0xd3, 0x97, 0x8f, 0x77, 0x44, - 0x51, 0x1d, 0xa6, 0xd1, 0x79, 0xc4, 0xb3, 0x71, 0xa4, 0x87, 0xe6, 0xa0, 0x65, 0xba, 0x4e, 0x40, - 0x5e, 0x06, 0x5d, 0x4b, 0x2b, 0x9f, 0x2c, 0x9d, 0x6f, 0xe1, 0x58, 0x80, 0xae, 0x42, 0xcb, 0x76, - 0xec, 0xc0, 0x36, 0x02, 0xd7, 0xd3, 0x2a, 0x27, 0x4b, 0x0a, 0x25, 0xab, 0x64, 0x67, 0xc9, 0x34, - 0xdd, 0xa1, 0x13, 0xe0, 0x58, 0x11, 0x69, 0xd0, 0x08, 0x3c, 0xc3, 0x24, 0x5d, 0x4b, 0xab, 0x32, - 0xc6, 0x30, 0xa9, 0xff, 0xed, 0x05, 0x68, 0x88, 0x3a, 0xa0, 0x3b, 0x30, 0x6d, 0x70, 0xec, 0xd6, - 0xae, 0xfb, 0x42, 0x2b, 0x31, 0xf6, 0x13, 0x89, 0x0a, 0x0b, 0xf6, 0x0e, 0x55, 0x59, 0x9f, 0xc2, - 0x32, 0x02, 0x75, 0x61, 0x56, 0x24, 0x57, 0x49, 0x60, 0xd8, 0x7d, 0x5f, 0xfb, 0x0b, 0x4e, 0x32, - 0x9f, 0x43, 0x22, 0xd4, 0xd6, 0xa7, 0x70, 0x02, 0x88, 0xbe, 0x0c, 0x47, 0x85, 0x64, 0xc5, 0x75, - 0x76, 0xec, 0xde, 0xd3, 0x81, 0x65, 0x04, 0x44, 0xfb, 0x4b, 0xce, 0x77, 0x3a, 0x87, 0x8f, 0xeb, - 0x76, 0xb8, 0xf2, 0xfa, 0x14, 0xce, 0xe2, 0x40, 0xf7, 0x60, 0x46, 0x88, 0x05, 0xe9, 0x5f, 0x71, - 0xd2, 0xd7, 0x73, 0x48, 0x23, 0x36, 0x15, 0x86, 0xbe, 0x02, 0xc7, 0x84, 0xe0, 0xa1, 0xed, 0x3c, - 0x5f, 0xd9, 0x35, 0xfa, 0x7d, 0xe2, 0xf4, 0x88, 0xf6, 0xd7, 0xa3, 0xeb, 0xa8, 0x28, 0xaf, 0x4f, - 0xe1, 0x4c, 0x12, 0xf4, 0x18, 0xda, 0xee, 0xf6, 0x37, 0x88, 0x19, 0x76, 0xc8, 0x16, 0x09, 0xb4, - 0x36, 0xe3, 0x7d, 0x33, 0xc1, 0xfb, 0x98, 0xa9, 0x85, 0x5d, 0xd9, 0xd9, 0x22, 0xc1, 0xfa, 0x14, - 0x4e, 0x81, 0xd1, 0x53, 0x40, 0x8a, 0x6c, 0x69, 0x8f, 0x38, 0x96, 0x76, 0x99, 0x51, 0x9e, 0x1a, - 0x4d, 0xc9, 0x54, 0xd7, 0xa7, 0x70, 0x06, 0x41, 0x8a, 0xf6, 0xa9, 0xe3, 0x93, 0x40, 0xbb, 0x32, - 0x0e, 0x2d, 0x53, 0x4d, 0xd1, 0x32, 0x29, 0xed, 0x5b, 0x2e, 0xc5, 0xa4, 0x6f, 0x04, 0xb6, 0xeb, - 0x88, 0xfa, 0x5e, 0x65, 0xc4, 0x67, 0xb2, 0x89, 0x23, 0xdd, 0xa8, 0xc6, 0x99, 0x24, 0xe8, 0x6b, - 0xf0, 0x4a, 0x42, 0x8e, 0xc9, 0x9e, 0xbb, 0x4f, 0xb4, 0x6b, 0x8c, 0xfd, 0x6c, 0x11, 0x3b, 0xd7, - 0x5e, 0x9f, 0xc2, 0xd9, 0x34, 0x68, 0x19, 0x0e, 0x85, 0x19, 0x8c, 0xf6, 0x3a, 0xa3, 0x9d, 0xcb, - 0xa3, 0x15, 0x64, 0x0a, 0x46, 0xae, 0xa3, 0x1f, 0x78, 0xb6, 0xc9, 0xf8, 0xa9, 0x11, 0x2c, 0x8e, - 0xae, 0x63, 0xac, 0x2c, 0x2c, 0x21, 0x9b, 0x06, 0x61, 0x38, 0xec, 0x0f, 0xb7, 0x7d, 0xd3, 0xb3, - 0x07, 0x54, 0xb6, 0x64, 0x59, 0xda, 0xad, 0x51, 0xcc, 0x5b, 0x92, 0x72, 0x67, 0xc9, 0xa2, 0x9d, - 0x9b, 0x24, 0x40, 0x5f, 0x01, 0x24, 0x8b, 0x44, 0xeb, 0xdf, 0x67, 0xb4, 0x5f, 0x18, 0x83, 0x36, - 0xea, 0x8a, 0x0c, 0x1a, 0x64, 0xc0, 0x31, 0x59, 0xba, 0xe9, 0xfa, 0x36, 0xfd, 0xab, 0xdd, 0x66, - 0xf4, 0x6f, 0x8f, 0x41, 0x1f, 0x42, 0xa8, 0x5d, 0x64, 0x51, 0x25, 0x8b, 0x58, 0xa1, 0xb3, 0x92, - 0x78, 0xbe, 0x76, 0x67, 0xec, 0x22, 0x42, 0x48, 0xb2, 0x88, 0x50, 0x9e, 0xec, 0xa2, 0x0f, 0x3c, - 0x77, 0x38, 0xf0, 0xb5, 0xbb, 0x63, 0x77, 0x11, 0x07, 0x24, 0xbb, 0x88, 0x4b, 0xd1, 0x75, 0x68, - 0x6e, 0xf7, 0x5d, 0xf3, 0x39, 0x1d, 0xcc, 0x32, 0xa3, 0xd4, 0x12, 0x94, 0xcb, 0x34, 0x5b, 0x0c, - 0x5f, 0xa4, 0x4b, 0xfd, 0x3e, 0xfb, 0x7f, 0x95, 0xf4, 0x49, 0x40, 0xc4, 0xaa, 0x72, 0x22, 0x13, - 0xca, 0x55, 0xa8, 0xdf, 0x97, 0x10, 0x68, 0x15, 0xa6, 0x77, 0xec, 0x3e, 0xf1, 0x9f, 0x0e, 0xfa, - 0xae, 0xc1, 0x97, 0x98, 0xe9, 0xcb, 0x27, 0x33, 0x09, 0xee, 0xc5, 0x7a, 0x94, 0x45, 0x82, 0xa1, - 0xdb, 0xd0, 0xda, 0x33, 0xbc, 0xe7, 0x7e, 0xd7, 0xd9, 0x71, 0xb5, 0x5a, 0xe6, 0xba, 0xc1, 0x39, - 0x1e, 0x85, 0x5a, 0xeb, 0x53, 0x38, 0x86, 0xd0, 0xd5, 0x87, 0x55, 0x6a, 0x8b, 0x04, 0xf7, 0x6c, - 0xd2, 0xb7, 0x7c, 0xad, 0xce, 0x48, 0xde, 0xc8, 0x24, 0xd9, 0x22, 0x41, 0x87, 0xab, 0xd1, 0xd5, - 0x47, 0x05, 0xa2, 0x0f, 0xe1, 0x68, 0x28, 0x59, 0xd9, 0xb5, 0xfb, 0x96, 0x47, 0x9c, 0xae, 0xe5, - 0x6b, 0x8d, 0x4c, 0xc7, 0x1e, 0xf3, 0x49, 0xba, 0x74, 0xf1, 0xc9, 0xa0, 0xa0, 0x8e, 0x2d, 0x14, - 0xcb, 0x53, 0x52, 0x6b, 0x66, 0x3a, 0xb6, 0x98, 0x5a, 0x56, 0xa6, 0xd6, 0x95, 0x45, 0x82, 0x2c, - 0x78, 0x35, 0x94, 0x2f, 0x1b, 0xe6, 0xf3, 0x9e, 0xe7, 0x0e, 0x1d, 0x6b, 0xc5, 0xed, 0xbb, 0x9e, - 0xd6, 0x62, 0xfc, 0xe7, 0x73, 0xf9, 0x13, 0xfa, 0xeb, 0x53, 0x38, 0x8f, 0x0a, 0xad, 0xc0, 0xa1, - 0x30, 0xeb, 0x09, 0x79, 0x19, 0x68, 0x90, 0xb9, 0x7a, 0xc6, 0xd4, 0x54, 0x89, 0xfa, 0x37, 0x19, - 0x24, 0x93, 0x50, 0x93, 0xd0, 0xa6, 0x0b, 0x48, 0xa8, 0x92, 0x4c, 0x42, 0xd3, 0x32, 0x09, 0x5d, - 0x3d, 0xb5, 0x99, 0x02, 0x12, 0xaa, 0x24, 0x93, 0xd0, 0x34, 0x5d, 0x69, 0xa3, 0x96, 0xba, 0xee, - 0x73, 0x6a, 0x4f, 0xda, 0x6c, 0xe6, 0x4a, 0x2b, 0xf5, 0x96, 0x50, 0xa4, 0x2b, 0x6d, 0x12, 0x4c, - 0xf7, 0x17, 0xa1, 0x6c, 0xa9, 0x6f, 0xf7, 0x1c, 0xed, 0xf0, 0x08, 0x5b, 0xa6, 0x6c, 0x4c, 0x8b, - 0xee, 0x2f, 0x14, 0x18, 0xba, 0x2b, 0xa6, 0xe5, 0x16, 0x09, 0x56, 0xed, 0x7d, 0xed, 0x48, 0xe6, - 0x2a, 0x12, 0xb3, 0xac, 0xda, 0xfb, 0xd1, 0xbc, 0xe4, 0x10, 0xb9, 0x69, 0xe1, 0x1a, 0xa5, 0xbd, - 0x52, 0xd0, 0xb4, 0x50, 0x51, 0x6e, 0x5a, 0x28, 0x93, 0x9b, 0xf6, 0xd0, 0x08, 0xc8, 0x4b, 0xed, - 0xb5, 0x82, 0xa6, 0x31, 0x2d, 0xb9, 0x69, 0x4c, 0x40, 0x57, 0xb7, 0x50, 0xf0, 0x8c, 0x78, 0x81, - 0x6d, 0x1a, 0x7d, 0xde, 0x55, 0xa7, 0x33, 0xd7, 0xa0, 0x98, 0x4f, 0xd1, 0xa6, 0xab, 0x5b, 0x26, - 0x8d, 0xdc, 0xf0, 0x27, 0xc6, 0x76, 0x9f, 0x60, 0xf7, 0x85, 0x76, 0xa6, 0xa0, 0xe1, 0xa1, 0xa2, - 0xdc, 0xf0, 0x50, 0x26, 0xfb, 0x96, 0x2f, 0xd9, 0x56, 0x8f, 0x04, 0xda, 0xf9, 0x02, 0xdf, 0xc2, - 0xd5, 0x64, 0xdf, 0xc2, 0x25, 0x91, 0x07, 0x58, 0x35, 0x02, 0x63, 0xdf, 0x26, 0x2f, 0x9e, 0xd9, - 0xe4, 0x05, 0x5d, 0xd8, 0x8f, 0x8e, 0xf0, 0x00, 0xa1, 0x6e, 0x47, 0x28, 0x47, 0x1e, 0x20, 0x41, - 0x12, 0x79, 0x00, 0x59, 0x2e, 0xdc, 0xfa, 0xb1, 0x11, 0x1e, 0x40, 0xe1, 0x8f, 0x7c, 0x7c, 0x1e, - 0x15, 0x32, 0xe0, 0x78, 0x2a, 0xeb, 0xb1, 0x67, 0x11, 0x4f, 0x7b, 0x9d, 0x15, 0x72, 0xae, 0xb8, - 0x10, 0xa6, 0xbe, 0x3e, 0x85, 0x73, 0x88, 0x52, 0x45, 0x6c, 0xb9, 0x43, 0xcf, 0x24, 0xb4, 0x9f, - 0x4e, 0x8d, 0x53, 0x44, 0xa4, 0x9e, 0x2a, 0x22, 0xca, 0x41, 0xfb, 0xf0, 0x7a, 0x94, 0x43, 0x0b, - 0x66, 0xab, 0x28, 0x2b, 0x5d, 0x9c, 0x0b, 0xce, 0xb2, 0x92, 0x3a, 0xa3, 0x4b, 0x4a, 0xa2, 0xd6, - 0xa7, 0xf0, 0x68, 0x5a, 0x74, 0x00, 0xf3, 0x8a, 0x02, 0x5f, 0xe7, 0xe5, 0x82, 0xcf, 0xb1, 0x82, - 0x2f, 0x8e, 0x2e, 0x38, 0x05, 0x5b, 0x9f, 0xc2, 0x05, 0xc4, 0x68, 0x00, 0x27, 0x94, 0xce, 0x08, - 0x27, 0xb6, 0x30, 0x91, 0xff, 0xcf, 0xca, 0xbd, 0x30, 0xba, 0x5c, 0x15, 0xb3, 0x3e, 0x85, 0x47, - 0x51, 0xa2, 0x1e, 0x68, 0x99, 0xd9, 0x74, 0x24, 0xbf, 0x9d, 0xb9, 0xed, 0xc9, 0x29, 0x8e, 0x8f, - 0x65, 0x2e, 0x59, 0xa6, 0xe5, 0x8b, 0xee, 0xfc, 0x99, 0x71, 0x2d, 0x3f, 0xea, 0xc7, 0x3c, 0x2a, - 0x65, 0xec, 0x68, 0xd6, 0x13, 0xc3, 0xeb, 0x91, 0x80, 0x77, 0x74, 0xd7, 0xa2, 0x8d, 0xfa, 0xce, - 0x38, 0x63, 0x97, 0x82, 0x29, 0x63, 0x97, 0x49, 0x8c, 0x7c, 0x98, 0x53, 0x34, 0xba, 0xfe, 0x8a, - 0xdb, 0xef, 0x13, 0x33, 0xec, 0xcd, 0x9f, 0x65, 0x05, 0xbf, 0x33, 0xba, 0xe0, 0x04, 0x68, 0x7d, - 0x0a, 0x8f, 0x24, 0x4d, 0xb5, 0xf7, 0x71, 0xdf, 0x4a, 0xd8, 0x8c, 0x36, 0x96, 0xad, 0x26, 0x61, - 0xa9, 0xf6, 0xa6, 0x34, 0x52, 0xb6, 0x2a, 0x69, 0xd0, 0xe6, 0xbe, 0x3a, 0x8e, 0xad, 0xaa, 0x98, - 0x94, 0xad, 0xaa, 0xd9, 0x74, 0x75, 0x1b, 0xfa, 0xc4, 0x63, 0x1c, 0xf7, 0x5d, 0xdb, 0xd1, 0xde, - 0xc8, 0x5c, 0xdd, 0x9e, 0xfa, 0xc4, 0x13, 0x05, 0x51, 0x2d, 0xba, 0xba, 0x29, 0x30, 0x85, 0xe7, - 0x21, 0xd9, 0x09, 0xb4, 0x93, 0x45, 0x3c, 0x54, 0x4b, 0xe1, 0xa1, 0x02, 0xba, 0x52, 0x44, 0x82, - 0x2d, 0x42, 0x47, 0x05, 0x1b, 0x4e, 0x8f, 0x68, 0x6f, 0x66, 0xae, 0x14, 0x12, 0x9d, 0xa4, 0x4c, - 0x57, 0x8a, 0x2c, 0x12, 0x7a, 0x70, 0x8f, 0xe4, 0x74, 0x47, 0xc6, 0xa9, 0x17, 0x32, 0x0f, 0xee, - 0x12, 0x75, 0xa4, 0x4a, 0xcf, 0x20, 0x69, 0x02, 0xf4, 0x05, 0xa8, 0x0e, 0x6c, 0xa7, 0xa7, 0x59, - 0x8c, 0xe8, 0x68, 0x82, 0x68, 0xd3, 0x76, 0x7a, 0xeb, 0x53, 0x98, 0xa9, 0xa0, 0x5b, 0x00, 0x03, - 0xcf, 0x35, 0x89, 0xef, 0x6f, 0x90, 0x17, 0x1a, 0x61, 0x00, 0x3d, 0x09, 0xe0, 0x0a, 0x9d, 0x0d, - 0x42, 0xd7, 0x65, 0x49, 0x1f, 0xad, 0xc1, 0x8c, 0x48, 0x89, 0x59, 0xbe, 0x93, 0xb9, 0xf9, 0x0b, - 0x09, 0xe2, 0x20, 0x8e, 0x82, 0xa2, 0x67, 0x1f, 0x21, 0x58, 0x75, 0x1d, 0xa2, 0xf5, 0x32, 0xcf, - 0x3e, 0x21, 0x09, 0x55, 0xa1, 0x7b, 0x2c, 0x09, 0x41, 0x0f, 0xfb, 0xc1, 0xae, 0x47, 0x0c, 0x6b, - 0x2b, 0x30, 0x82, 0xa1, 0xaf, 0x39, 0x99, 0xdb, 0x34, 0x9e, 0xd9, 0x79, 0xc2, 0x34, 0xe9, 0x16, - 0x54, 0xc6, 0xa0, 0x0d, 0x68, 0xd3, 0x83, 0xd0, 0x43, 0x7b, 0xcf, 0x0e, 0x30, 0x31, 0xcc, 0x5d, - 0x62, 0x69, 0x6e, 0xe6, 0x21, 0x8a, 0x6e, 0x7b, 0x3b, 0xb2, 0x1e, 0xdd, 0xad, 0x24, 0xb1, 0x68, - 0x1d, 0x66, 0xa9, 0x6c, 0x6b, 0x60, 0x98, 0xe4, 0xa9, 0x6f, 0xf4, 0x88, 0x36, 0xc8, 0xb4, 0x40, - 0xc6, 0x16, 0x6b, 0xd1, 0xcd, 0x8a, 0x8a, 0x0b, 0x99, 0x1e, 0xba, 0xa6, 0xd1, 0xe7, 0x4c, 0xdf, - 0xcc, 0x67, 0x8a, 0xb5, 0x42, 0xa6, 0x58, 0xa2, 0xb4, 0x91, 0xf7, 0xbd, 0xa5, 0xed, 0x17, 0xb4, - 0x51, 0xe8, 0x29, 0x6d, 0x14, 0x32, 0xca, 0xe7, 0xb8, 0x81, 0xbd, 0x63, 0x9b, 0x62, 0xfe, 0x3a, - 0x96, 0xe6, 0x65, 0xf2, 0x6d, 0x48, 0x6a, 0x9d, 0x2d, 0x1e, 0x18, 0x4a, 0x61, 0xd1, 0x13, 0x40, - 0xb2, 0x4c, 0x18, 0x95, 0xcf, 0x18, 0x17, 0x46, 0x31, 0x46, 0x96, 0x95, 0x81, 0xa7, 0xb5, 0x1c, - 0x18, 0x07, 0xf4, 0x78, 0xbb, 0xec, 0xb9, 0x86, 0x65, 0x1a, 0x7e, 0xa0, 0x05, 0x99, 0xb5, 0xdc, - 0xe4, 0x6a, 0x9d, 0x48, 0x8f, 0xd6, 0x32, 0x89, 0xa5, 0x7c, 0x7b, 0x64, 0x6f, 0x9b, 0x78, 0xfe, - 0xae, 0x3d, 0x10, 0x75, 0x1c, 0x66, 0xf2, 0x3d, 0x8a, 0xd4, 0xe2, 0x1a, 0xa6, 0xb0, 0xe8, 0x01, - 0x1c, 0x1e, 0x5c, 0x1e, 0x70, 0x33, 0x14, 0x74, 0x2f, 0x32, 0x37, 0xb6, 0x9b, 0x97, 0x37, 0x85, - 0x0d, 0x47, 0x6c, 0x49, 0xe4, 0x72, 0x03, 0x6a, 0xfb, 0x46, 0x7f, 0x48, 0xf4, 0xef, 0xd7, 0xa0, - 0x21, 0xc2, 0x9d, 0xfa, 0x06, 0x54, 0x59, 0x6c, 0xf8, 0x18, 0xd4, 0x6c, 0xc7, 0x22, 0x2f, 0x59, - 0x58, 0xb9, 0x86, 0x79, 0x02, 0x5d, 0x82, 0x86, 0x08, 0x7f, 0x8a, 0x88, 0x45, 0x5e, 0x30, 0x3b, - 0x54, 0xd3, 0x3f, 0x82, 0x46, 0x18, 0x23, 0x9e, 0x83, 0xd6, 0xc0, 0x73, 0xa9, 0x65, 0x74, 0x2d, - 0x46, 0xdb, 0xc2, 0xb1, 0x00, 0xbd, 0x0b, 0x0d, 0x4b, 0x44, 0xa1, 0x39, 0xf5, 0xab, 0x1d, 0x1e, - 0xb6, 0xef, 0x84, 0x61, 0xfb, 0xce, 0x16, 0x0b, 0xdb, 0xe3, 0x50, 0x4f, 0xff, 0xb9, 0x12, 0xd4, - 0x79, 0xa8, 0x58, 0xdf, 0x87, 0xba, 0xe8, 0xa2, 0x6b, 0x50, 0x37, 0x99, 0x4c, 0x4b, 0x86, 0x89, - 0x95, 0x1a, 0x8a, 0xd8, 0x33, 0x16, 0xca, 0x14, 0xe6, 0x73, 0x8f, 0x50, 0x1e, 0x09, 0xe3, 0x3d, - 0x88, 0x85, 0xf2, 0x7f, 0x5b, 0xb9, 0xff, 0x51, 0x82, 0x19, 0x35, 0x02, 0x3d, 0x07, 0x2d, 0x33, - 0x8a, 0x69, 0x8b, 0xde, 0x35, 0xa5, 0xf8, 0x34, 0x98, 0x7d, 0x9b, 0x38, 0x01, 0x8b, 0xd6, 0x94, - 0x33, 0x37, 0x01, 0x99, 0x11, 0xef, 0xce, 0x4a, 0x04, 0xc3, 0x12, 0x85, 0xfe, 0x1d, 0x80, 0x38, - 0x07, 0x9d, 0x8c, 0xdc, 0xf2, 0x86, 0xb1, 0x17, 0x16, 0x2f, 0x8b, 0x24, 0x8d, 0x4d, 0x23, 0xd8, - 0x15, 0x1f, 0x4a, 0x64, 0x11, 0xba, 0x00, 0x47, 0x7c, 0xbb, 0xe7, 0x18, 0xc1, 0xd0, 0x23, 0xcf, - 0x88, 0x67, 0xef, 0xd8, 0xc4, 0x62, 0xc1, 0xad, 0x26, 0x4e, 0x67, 0xe8, 0xff, 0xd4, 0x84, 0x3a, - 0xdf, 0x6e, 0xe9, 0xff, 0x56, 0x8e, 0x6c, 0x4c, 0xff, 0xd3, 0x12, 0xd4, 0x78, 0xd4, 0x78, 0x16, - 0xca, 0x76, 0x68, 0x66, 0x65, 0xdb, 0x42, 0xf7, 0x64, 0xfb, 0xaa, 0x64, 0xec, 0x45, 0xb2, 0xa2, - 0xe8, 0x9d, 0x07, 0xe4, 0xe0, 0x19, 0x9d, 0x23, 0x91, 0xd1, 0xa1, 0xe3, 0x50, 0xf7, 0x87, 0xdb, - 0x5d, 0xcb, 0xd7, 0x2a, 0x27, 0x2b, 0xe7, 0x5b, 0x58, 0xa4, 0xf4, 0xfb, 0xd0, 0x0c, 0x95, 0x51, - 0x1b, 0x2a, 0xcf, 0xc9, 0x81, 0x28, 0x9c, 0xfe, 0x8b, 0x2e, 0x88, 0xb9, 0x16, 0x4d, 0x9b, 0xa4, - 0x6d, 0xf3, 0x52, 0xc4, 0x84, 0xfc, 0x3a, 0x54, 0xe8, 0x06, 0x27, 0xd9, 0x84, 0xc9, 0xa7, 0x48, - 0x6e, 0x6d, 0x57, 0xa0, 0xc6, 0x23, 0xf7, 0xc9, 0x32, 0x10, 0x54, 0x9f, 0x93, 0x03, 0xde, 0x47, - 0x2d, 0xcc, 0xfe, 0xcf, 0x25, 0xf9, 0x93, 0x0a, 0x1c, 0x92, 0xc3, 0x9d, 0xfa, 0x1a, 0x54, 0x96, - 0xac, 0x74, 0xd7, 0x6b, 0xd0, 0x30, 0x76, 0x02, 0xe2, 0x45, 0x1f, 0xc8, 0xc2, 0x24, 0xf5, 0x32, - 0x8c, 0x8b, 0x8d, 0x73, 0x0b, 0xf3, 0x84, 0xde, 0x81, 0xba, 0x88, 0x22, 0x27, 0x99, 0x22, 0xfd, - 0xb2, 0xac, 0x7f, 0x1f, 0x9a, 0x51, 0x50, 0xf8, 0xd3, 0x96, 0xed, 0x41, 0x33, 0x8a, 0xfe, 0x1e, - 0x83, 0x5a, 0xe0, 0x06, 0x46, 0x9f, 0xd1, 0x55, 0x30, 0x4f, 0xd0, 0x89, 0xe6, 0x90, 0x97, 0xc1, - 0x4a, 0xe4, 0x05, 0x2b, 0x38, 0x16, 0x70, 0x27, 0x47, 0xf6, 0x79, 0x6e, 0x85, 0xe7, 0x46, 0x82, - 0xb8, 0xcc, 0xaa, 0x5c, 0xe6, 0x01, 0xd4, 0x45, 0x48, 0x38, 0xca, 0x2f, 0x49, 0xf9, 0x68, 0x09, - 0x6a, 0x3d, 0x9a, 0x2f, 0x46, 0xfd, 0xed, 0x84, 0x8b, 0xe0, 0x3b, 0xbd, 0x15, 0xd7, 0x09, 0xa8, - 0x19, 0xab, 0x27, 0x5d, 0xcc, 0x91, 0x74, 0x08, 0x3d, 0x1e, 0xdf, 0xe7, 0x33, 0x4a, 0xa4, 0xf4, - 0xdf, 0x2d, 0x41, 0x2b, 0xfa, 0x1e, 0xa2, 0x7f, 0x94, 0x37, 0x79, 0x96, 0x60, 0xc6, 0x13, 0x5a, - 0xd4, 0x3b, 0x84, 0x53, 0xe8, 0x44, 0xa2, 0x26, 0x58, 0xd2, 0xc1, 0x2a, 0x42, 0xbf, 0x95, 0x3b, - 0xa8, 0x0b, 0x70, 0x28, 0x54, 0x7d, 0x10, 0x9b, 0x9e, 0x22, 0xd3, 0xf5, 0x08, 0xdd, 0x86, 0x8a, - 0x6d, 0xf1, 0xcf, 0xb3, 0x2d, 0x4c, 0xff, 0xd5, 0x77, 0xe0, 0x90, 0x1c, 0x56, 0xd5, 0x9f, 0x65, - 0xcf, 0x9e, 0x3b, 0xb4, 0x18, 0x29, 0x84, 0x5b, 0x4e, 0xec, 0x1d, 0xc3, 0x26, 0xc4, 0x2a, 0x58, - 0x01, 0xe8, 0xbf, 0x69, 0x42, 0x8d, 0xf5, 0xb5, 0x7e, 0x85, 0xdb, 0xf9, 0x05, 0xa8, 0xb3, 0xf3, - 0x49, 0xf8, 0xb1, 0xf8, 0x58, 0xd6, 0xc0, 0x60, 0xa1, 0xa3, 0xaf, 0xc0, 0xb4, 0x14, 0x4d, 0xa7, - 0x86, 0xc9, 0x32, 0xa2, 0xc1, 0x0e, 0x93, 0x48, 0x87, 0x26, 0x5d, 0x13, 0x85, 0x9f, 0xa4, 0xcd, - 0x8c, 0xd2, 0xfa, 0x69, 0xa8, 0x8b, 0xf3, 0x96, 0x2e, 0xbe, 0x1e, 0x74, 0xa3, 0xce, 0x88, 0xd2, - 0xfa, 0x57, 0xa1, 0x15, 0x05, 0xdd, 0xd1, 0x63, 0x38, 0x24, 0x82, 0xee, 0xfc, 0xcc, 0x40, 0x95, - 0x67, 0x0b, 0x8c, 0x88, 0x1e, 0x10, 0x58, 0xdc, 0xbe, 0xf3, 0xe4, 0x60, 0x40, 0xb0, 0x42, 0xa0, - 0xff, 0xd2, 0x79, 0xd6, 0xc1, 0xfa, 0x00, 0x9a, 0x51, 0xa4, 0x31, 0xd9, 0xd9, 0x8b, 0xdc, 0x03, - 0x96, 0x0b, 0xc3, 0xe4, 0x1c, 0x4f, 0xfd, 0x2c, 0x73, 0x94, 0xfa, 0x09, 0xa8, 0x3c, 0x20, 0x07, - 0x74, 0x22, 0x70, 0x7f, 0x29, 0x26, 0x02, 0xf7, 0x8b, 0x5d, 0xa8, 0x8b, 0x88, 0x7f, 0xb2, 0xbc, - 0x8b, 0x50, 0xdf, 0xe1, 0x1f, 0x11, 0x0a, 0x3c, 0xa3, 0x50, 0xd3, 0xef, 0xc0, 0xb4, 0x1c, 0xe7, - 0x4f, 0xf2, 0x9d, 0x84, 0x69, 0x53, 0xfa, 0x92, 0xc0, 0x87, 0x41, 0x16, 0xe9, 0x44, 0xb5, 0xba, - 0x14, 0xc3, 0x5a, 0xa6, 0xb9, 0xbd, 0x99, 0xd9, 0xed, 0x23, 0x8c, 0xee, 0x01, 0x1c, 0x4e, 0x06, - 0xf4, 0x93, 0x25, 0x9d, 0x87, 0xc3, 0xdb, 0x89, 0xcf, 0x07, 0xdc, 0xd5, 0x25, 0xc5, 0x7a, 0x17, - 0x6a, 0x3c, 0xe0, 0x9a, 0xa4, 0xb8, 0x04, 0x35, 0x83, 0x05, 0x74, 0x29, 0x70, 0x56, 0x3a, 0xd6, - 0xc9, 0xb5, 0x64, 0x50, 0xcc, 0x15, 0x75, 0x1b, 0x66, 0xd4, 0x18, 0x6e, 0x92, 0x72, 0x1d, 0x66, - 0xf6, 0x95, 0x58, 0x31, 0xa7, 0x5e, 0xc8, 0xa4, 0x56, 0xa8, 0xb0, 0x0a, 0xd4, 0x7f, 0xbe, 0x0e, - 0x55, 0xf6, 0x11, 0x22, 0x59, 0xc4, 0x75, 0xa8, 0x06, 0xe4, 0x65, 0xb8, 0x15, 0x5d, 0x18, 0xf9, - 0x45, 0x83, 0x9f, 0x84, 0x99, 0x3e, 0xfa, 0x22, 0xd4, 0xfc, 0xe0, 0xa0, 0x1f, 0x7e, 0x3a, 0x3b, - 0x35, 0x1a, 0xb8, 0x45, 0x55, 0x31, 0x47, 0x50, 0x28, 0x9b, 0x0b, 0xe2, 0xa3, 0x59, 0x01, 0x94, - 0x4d, 0x42, 0xcc, 0x11, 0xe8, 0x0e, 0x34, 0xcc, 0x5d, 0x62, 0x3e, 0x27, 0x96, 0xf8, 0x5a, 0x76, - 0x66, 0x34, 0x78, 0x85, 0x2b, 0xe3, 0x10, 0x45, 0xcb, 0x36, 0xd9, 0xe8, 0xd6, 0xc7, 0x29, 0x9b, - 0x8d, 0x38, 0xe6, 0x08, 0xb4, 0x06, 0x2d, 0xdb, 0x74, 0x9d, 0xb5, 0x3d, 0xf7, 0x1b, 0xb6, 0xf8, - 0x2c, 0x76, 0x6e, 0x34, 0xbc, 0x1b, 0xaa, 0xe3, 0x18, 0x19, 0xd2, 0x74, 0xf7, 0xe8, 0xc9, 0xb2, - 0x39, 0x2e, 0x0d, 0x53, 0xc7, 0x31, 0x52, 0x9f, 0x13, 0xe3, 0x99, 0x3d, 0xc9, 0xef, 0x41, 0x8d, - 0x75, 0x39, 0x7a, 0x5f, 0xce, 0x9e, 0x95, 0x4a, 0xca, 0xf5, 0x58, 0x62, 0xa8, 0x22, 0x1e, 0xd6, - 0xff, 0x2a, 0xcf, 0xf4, 0x38, 0x3c, 0x62, 0xdc, 0x38, 0xcf, 0x1b, 0xd0, 0x10, 0x43, 0xa1, 0x56, - 0xb8, 0x19, 0x2a, 0xbc, 0x0e, 0x35, 0x3e, 0x31, 0xb3, 0xdb, 0xf3, 0x26, 0xb4, 0xa2, 0xce, 0x1c, - 0xad, 0xc2, 0x7a, 0x27, 0x47, 0xe5, 0xbb, 0x65, 0xa8, 0xf1, 0x8f, 0x31, 0x69, 0x57, 0x2b, 0xcf, - 0x82, 0x53, 0xa3, 0xbf, 0xed, 0xc8, 0xd3, 0xe0, 0x1e, 0x3b, 0x8f, 0xd1, 0xfd, 0x77, 0x74, 0x37, - 0xe9, 0x7c, 0x01, 0x7a, 0x33, 0xd4, 0xc7, 0x31, 0xb4, 0x60, 0x38, 0x1f, 0x43, 0x2b, 0x42, 0xa1, - 0x65, 0x75, 0x48, 0x2f, 0x8c, 0x1c, 0x8a, 0x64, 0x91, 0x82, 0xf0, 0x7b, 0x25, 0xa8, 0xac, 0xda, - 0xfb, 0xa9, 0x7e, 0xb8, 0x11, 0xce, 0xea, 0x22, 0x77, 0xb0, 0x6a, 0xef, 0x2b, 0x93, 0x5a, 0x5f, - 0x0b, 0x2d, 0xee, 0x96, 0x5a, 0xbd, 0xb3, 0xa3, 0x37, 0x5a, 0x31, 0x0d, 0xaf, 0xd8, 0xaf, 0x36, - 0xa0, 0xca, 0xbe, 0x73, 0x66, 0xf9, 0xa9, 0x83, 0x41, 0x71, 0xc5, 0x58, 0x24, 0x85, 0x2d, 0xb8, - 0x4c, 0x9f, 0xfb, 0x29, 0x23, 0x28, 0xf6, 0x53, 0x3c, 0x30, 0x44, 0x55, 0x31, 0x47, 0xd0, 0x22, - 0xf7, 0xec, 0x3d, 0x22, 0xdc, 0x54, 0x41, 0x91, 0x8f, 0xec, 0x3d, 0x82, 0x99, 0x3e, 0xc5, 0xed, - 0x1a, 0xfe, 0xae, 0xf0, 0x50, 0x05, 0xb8, 0x75, 0xc3, 0xdf, 0xc5, 0x4c, 0x9f, 0xe2, 0x1c, 0x7a, - 0xf2, 0xab, 0x8f, 0x83, 0xa3, 0x07, 0x42, 0xcc, 0xf4, 0x29, 0xce, 0xb7, 0xbf, 0x45, 0x84, 0x4f, - 0x2a, 0xc0, 0x6d, 0xd9, 0xdf, 0x22, 0x98, 0xe9, 0xc7, 0x2e, 0xbc, 0x39, 0x5e, 0xd7, 0x48, 0x2e, - 0xfc, 0x09, 0xcc, 0x06, 0x4a, 0xb4, 0x5e, 0x7c, 0x6c, 0xbf, 0x50, 0x30, 0x2e, 0x0a, 0x06, 0x27, - 0x38, 0xe8, 0x24, 0x60, 0xe7, 0xdc, 0xec, 0x49, 0xf0, 0x3a, 0xd4, 0xbe, 0x64, 0x5b, 0xc1, 0xae, - 0x9a, 0x5d, 0x53, 0x5c, 0x1e, 0x1d, 0xb6, 0x89, 0x5c, 0x9e, 0x3c, 0xea, 0x9c, 0x67, 0x15, 0xaa, - 0xd4, 0x7c, 0x26, 0xb3, 0xe3, 0xd8, 0xea, 0x3e, 0x95, 0x03, 0x96, 0x3b, 0x9a, 0xf3, 0xcc, 0x41, - 0x95, 0x5a, 0x48, 0x4e, 0x97, 0xcc, 0x41, 0x95, 0xda, 0x5d, 0x7e, 0x2e, 0x1d, 0x6d, 0x35, 0xb7, - 0x12, 0xe6, 0x9e, 0x85, 0x59, 0x75, 0x38, 0x72, 0x58, 0xfe, 0xb8, 0x01, 0x55, 0x76, 0x69, 0x20, - 0x39, 0x23, 0xff, 0x1f, 0xcc, 0xf0, 0xf1, 0x5b, 0x16, 0x5b, 0xf0, 0x72, 0xe6, 0x9d, 0x21, 0xf5, - 0x2a, 0x82, 0x30, 0x01, 0x01, 0xc1, 0x2a, 0xc3, 0xf8, 0x9b, 0x0a, 0x46, 0xa5, 0x58, 0xe4, 0xad, - 0x68, 0xf3, 0x5a, 0x2d, 0xb8, 0xb1, 0xc2, 0xb0, 0x7c, 0x0b, 0x1c, 0xee, 0x64, 0xd1, 0x32, 0x34, - 0xe9, 0xd2, 0x4a, 0xbb, 0x4b, 0x4c, 0xdb, 0xb3, 0xa3, 0xf1, 0x5d, 0xa1, 0x8d, 0x23, 0x1c, 0x5d, - 0xd8, 0x4d, 0xc3, 0xb3, 0x58, 0xad, 0xc4, 0x1c, 0x3e, 0x37, 0x9a, 0x64, 0x25, 0x54, 0xc7, 0x31, - 0x12, 0x3d, 0x80, 0x69, 0x8b, 0x44, 0xe1, 0x00, 0x31, 0xa9, 0xbf, 0x30, 0x9a, 0x68, 0x35, 0x06, - 0x60, 0x19, 0x4d, 0xeb, 0x14, 0x1e, 0x01, 0xfd, 0xc2, 0xcd, 0x06, 0xa3, 0x8a, 0x2f, 0xf6, 0xc5, - 0x48, 0xfd, 0x0c, 0xcc, 0x28, 0xe3, 0xf6, 0x99, 0xee, 0x3a, 0xe4, 0xb1, 0xe4, 0x3c, 0x8b, 0xd1, - 0x11, 0xe5, 0x1d, 0x75, 0xdb, 0x91, 0x7b, 0x22, 0x11, 0xc0, 0x87, 0xd0, 0x0c, 0x07, 0x06, 0xdd, - 0x55, 0xeb, 0xf0, 0x56, 0x71, 0x1d, 0xa2, 0x31, 0x15, 0x6c, 0x1b, 0xd0, 0x8a, 0x46, 0x08, 0x2d, - 0xa9, 0x74, 0x6f, 0x17, 0xd3, 0xc5, 0xa3, 0x2b, 0xf8, 0x30, 0x4c, 0x4b, 0x03, 0x85, 0x56, 0x54, - 0xc6, 0x77, 0x8a, 0x19, 0xe5, 0x61, 0x8e, 0x77, 0x3d, 0xd1, 0x88, 0xc9, 0xa3, 0x52, 0x89, 0x47, - 0xe5, 0x0f, 0x1b, 0xd0, 0x8c, 0x2e, 0xea, 0x64, 0x9c, 0x31, 0x87, 0x5e, 0xbf, 0xf0, 0x8c, 0x19, - 0xe2, 0x3b, 0x4f, 0xbd, 0x3e, 0xa6, 0x08, 0x3a, 0xc4, 0x81, 0x1d, 0x44, 0x53, 0xf5, 0x5c, 0x31, - 0xf4, 0x09, 0x55, 0xc7, 0x1c, 0x85, 0x1e, 0xab, 0x56, 0x5e, 0x1d, 0xf1, 0x21, 0x57, 0x21, 0xc9, - 0xb5, 0xf4, 0x2e, 0xb4, 0x6c, 0xba, 0xf5, 0x5b, 0x8f, 0x57, 0xde, 0xb7, 0x8b, 0xe9, 0xba, 0x21, - 0x04, 0xc7, 0x68, 0x5a, 0xb7, 0x1d, 0x63, 0x9f, 0xce, 0x6b, 0x46, 0x56, 0x1f, 0xb7, 0x6e, 0xf7, - 0x62, 0x10, 0x96, 0x19, 0xd0, 0x4d, 0xb1, 0x77, 0x69, 0x14, 0x78, 0x96, 0xb8, 0xab, 0xe2, 0xfd, - 0xcb, 0x87, 0xa9, 0x95, 0x96, 0x4f, 0xe3, 0x4b, 0x63, 0xb0, 0x8c, 0x5c, 0x6d, 0xe9, 0x08, 0xf2, - 0x9d, 0x51, 0x6b, 0xdc, 0x11, 0x94, 0x77, 0x47, 0xfa, 0x09, 0xa8, 0x3c, 0xf5, 0xfa, 0xf9, 0x6b, - 0x35, 0x1b, 0xee, 0x9c, 0xec, 0x53, 0xea, 0x4c, 0xc8, 0xdf, 0xd0, 0x47, 0x63, 0x92, 0xcb, 0x23, - 0x75, 0x7a, 0x8e, 0xd2, 0xfb, 0x62, 0x41, 0xbf, 0xa6, 0xce, 0xb7, 0x37, 0x12, 0xf3, 0x8d, 0xce, - 0xb0, 0x4d, 0x8f, 0xf0, 0xbb, 0x0a, 0xd2, 0x4a, 0x3e, 0xee, 0x3a, 0x79, 0x3f, 0xdc, 0x7f, 0x4c, - 0xe4, 0x29, 0x92, 0x7d, 0xcb, 0xb9, 0x7e, 0xb1, 0x04, 0xcd, 0xe8, 0x1e, 0x56, 0x3a, 0x08, 0xdf, - 0xb4, 0xfd, 0x75, 0x62, 0x58, 0xc4, 0x13, 0xf3, 0xf6, 0xad, 0xc2, 0x0b, 0x5e, 0x9d, 0xae, 0x40, - 0xe0, 0x08, 0xab, 0x9f, 0x84, 0x66, 0x28, 0xcd, 0x39, 0x94, 0xfd, 0xb8, 0x0c, 0x75, 0x71, 0x83, - 0x2b, 0x59, 0x89, 0xdb, 0x50, 0xef, 0x1b, 0x07, 0xee, 0x30, 0x3c, 0x32, 0x9d, 0x2d, 0xb8, 0x14, - 0xd6, 0x79, 0xc8, 0xb4, 0xb1, 0x40, 0xa1, 0xf7, 0xa0, 0xd6, 0xb7, 0xf7, 0xec, 0x40, 0xb8, 0x8f, - 0x33, 0x85, 0x70, 0xf6, 0xad, 0x97, 0x63, 0x68, 0xe1, 0xec, 0xe2, 0x46, 0x78, 0xed, 0xb6, 0xb0, - 0xf0, 0x67, 0x4c, 0x1b, 0x0b, 0x94, 0x7e, 0x1f, 0xea, 0xbc, 0x3a, 0x93, 0x2d, 0x12, 0x6a, 0x4b, - 0x62, 0x4b, 0x67, 0x75, 0xcb, 0xd9, 0x95, 0xce, 0x43, 0x9d, 0x17, 0x9e, 0x63, 0x35, 0x3f, 0x7a, - 0x8d, 0x9d, 0x77, 0xfa, 0xfa, 0xc3, 0xf8, 0x1b, 0xdf, 0xa7, 0xff, 0x64, 0xa1, 0x3f, 0x81, 0xc3, - 0xab, 0x46, 0x60, 0x6c, 0x1b, 0x3e, 0xc1, 0xc4, 0x74, 0x3d, 0x2b, 0x93, 0xd5, 0xe3, 0x59, 0x22, - 0x10, 0x9d, 0xcf, 0x2a, 0xf4, 0x3e, 0x0f, 0x1d, 0xfe, 0xcf, 0x09, 0x1d, 0xfe, 0x51, 0x35, 0x27, - 0x9e, 0x37, 0x4e, 0x24, 0x83, 0x1a, 0x5c, 0x2a, 0xa0, 0x77, 0x53, 0xdd, 0x7b, 0x9f, 0x2e, 0x40, - 0x2a, 0x9b, 0xef, 0x9b, 0x6a, 0x44, 0xaf, 0x08, 0xab, 0x84, 0xf4, 0xee, 0x26, 0x43, 0x7a, 0x67, - 0x0b, 0xd0, 0xa9, 0x98, 0xde, 0x4d, 0x35, 0xa6, 0x57, 0x54, 0xba, 0x1c, 0xd4, 0xfb, 0x3f, 0x16, - 0x46, 0xfb, 0xf5, 0x9c, 0xb0, 0xcf, 0x17, 0xd5, 0xb0, 0xcf, 0x08, 0xab, 0xf9, 0x69, 0xc5, 0x7d, - 0x7e, 0xa3, 0x9e, 0x13, 0xf7, 0x59, 0x54, 0xe2, 0x3e, 0x23, 0x6a, 0x96, 0x0c, 0xfc, 0xdc, 0x54, - 0x03, 0x3f, 0xa7, 0x0b, 0x90, 0x4a, 0xe4, 0x67, 0x51, 0x89, 0xfc, 0x14, 0x15, 0x2a, 0x85, 0x7e, - 0x16, 0x95, 0xd0, 0x4f, 0x11, 0x50, 0x8a, 0xfd, 0x2c, 0x2a, 0xb1, 0x9f, 0x22, 0xa0, 0x14, 0xfc, - 0x59, 0x54, 0x82, 0x3f, 0x45, 0x40, 0x29, 0xfa, 0x73, 0x53, 0x8d, 0xfe, 0x14, 0xf7, 0x8f, 0x34, - 0xe8, 0x9f, 0x07, 0x6a, 0xfe, 0x0b, 0x03, 0x35, 0xbf, 0x52, 0xc9, 0x09, 0xc0, 0xe0, 0xec, 0x00, - 0xcc, 0x85, 0xfc, 0x91, 0x2c, 0x8e, 0xc0, 0x8c, 0xbf, 0x0a, 0xa4, 0x43, 0x30, 0xef, 0x27, 0x42, - 0x30, 0x67, 0x0a, 0xc0, 0x6a, 0x0c, 0xe6, 0x7f, 0x4d, 0x90, 0xe1, 0xf7, 0xeb, 0x23, 0xce, 0xd3, - 0x37, 0xe4, 0xf3, 0xf4, 0x88, 0x95, 0x2c, 0x7d, 0xa0, 0xbe, 0xad, 0x1e, 0xa8, 0xcf, 0x8f, 0x81, - 0x55, 0x4e, 0xd4, 0x9b, 0x59, 0x27, 0xea, 0xce, 0x18, 0x2c, 0xb9, 0x47, 0xea, 0xfb, 0xe9, 0x23, - 0xf5, 0x85, 0x31, 0xf8, 0x32, 0xcf, 0xd4, 0x9b, 0x59, 0x67, 0xea, 0x71, 0x6a, 0x97, 0x7b, 0xa8, - 0x7e, 0x4f, 0x39, 0x54, 0x9f, 0x1b, 0xa7, 0xbb, 0xe2, 0xc5, 0xe1, 0xcb, 0x39, 0xa7, 0xea, 0x77, - 0xc7, 0xa1, 0x19, 0x1d, 0xc4, 0xfe, 0xfc, 0x5c, 0xac, 0x16, 0xf3, 0x7b, 0x6f, 0x40, 0x33, 0xbc, - 0x4f, 0xa3, 0x7f, 0x13, 0x1a, 0xe1, 0xb3, 0x9d, 0xe4, 0xcc, 0x39, 0x1e, 0x1d, 0xea, 0xf8, 0xee, - 0x59, 0xa4, 0xd0, 0x6d, 0xa8, 0xd2, 0xff, 0xc4, 0xb4, 0x78, 0x6b, 0xbc, 0x7b, 0x3b, 0xb4, 0x10, - 0xcc, 0x70, 0xfa, 0xbf, 0x1e, 0x03, 0x90, 0x5e, 0x33, 0x8c, 0x5b, 0xec, 0x07, 0xd4, 0x99, 0xf5, - 0x03, 0xe2, 0xb1, 0xfb, 0x5a, 0x85, 0xb7, 0xfd, 0xe3, 0x12, 0xa8, 0xb5, 0x04, 0xc4, 0xc3, 0x02, - 0x8e, 0x1e, 0x41, 0x33, 0x0c, 0xa4, 0x6a, 0x55, 0x46, 0xf5, 0xee, 0xd8, 0x54, 0x61, 0x68, 0x0f, - 0x47, 0x14, 0x68, 0x09, 0xaa, 0xbe, 0xeb, 0x05, 0x5a, 0x8d, 0x51, 0xbd, 0x33, 0x36, 0xd5, 0x96, - 0xeb, 0x05, 0x98, 0x41, 0x79, 0xd3, 0xa4, 0xc7, 0xa2, 0x93, 0x34, 0x4d, 0xf1, 0xd8, 0xff, 0x52, - 0x89, 0x7c, 0xe8, 0x8a, 0x98, 0x8d, 0xdc, 0x86, 0x2e, 0x8e, 0x3f, 0x4a, 0xf2, 0xac, 0x44, 0x62, - 0x13, 0xc4, 0x47, 0x82, 0xef, 0x6f, 0xde, 0x82, 0xb6, 0xe9, 0xee, 0x13, 0x0f, 0xc7, 0x37, 0x99, - 0xc4, 0x65, 0xb3, 0x94, 0x1c, 0xe9, 0xd0, 0xdc, 0xb5, 0x2d, 0xd2, 0x35, 0x85, 0xff, 0x6b, 0xe2, - 0x28, 0x8d, 0x1e, 0x40, 0x93, 0xc5, 0xd8, 0xc3, 0x08, 0xff, 0x64, 0x95, 0xe4, 0xa1, 0xfe, 0x90, - 0x80, 0x16, 0xc4, 0x0a, 0xbf, 0x67, 0x07, 0xac, 0x0f, 0x9b, 0x38, 0x4a, 0xd3, 0x0a, 0xb3, 0xeb, - 0x62, 0x72, 0x85, 0x1b, 0xbc, 0xc2, 0x49, 0x39, 0xba, 0x0a, 0xaf, 0x30, 0x59, 0xe2, 0x88, 0xc9, - 0x43, 0xf5, 0x4d, 0x9c, 0x9d, 0xc9, 0xae, 0xc7, 0x19, 0x3d, 0x7e, 0x35, 0x9c, 0x05, 0xef, 0x6a, - 0x38, 0x16, 0xa0, 0x0b, 0x70, 0xc4, 0x22, 0x3b, 0xc6, 0xb0, 0x1f, 0x3c, 0x21, 0x7b, 0x83, 0xbe, - 0x11, 0x90, 0xae, 0xc5, 0xde, 0xab, 0xb6, 0x70, 0x3a, 0x03, 0x5d, 0x82, 0xa3, 0x42, 0xc8, 0xa7, - 0x31, 0x1d, 0x8d, 0xae, 0xc5, 0x9e, 0x6f, 0xb6, 0x70, 0x56, 0x96, 0xfe, 0xa3, 0x2a, 0x1d, 0x74, - 0x66, 0xda, 0x1f, 0x40, 0xc5, 0xb0, 0x2c, 0xb1, 0x6c, 0x5e, 0x99, 0x70, 0x82, 0x88, 0x27, 0xd9, - 0x94, 0x01, 0x6d, 0x46, 0x37, 0xeb, 0xf8, 0xc2, 0x79, 0x7d, 0x52, 0xae, 0xe8, 0x19, 0xbd, 0xe0, - 0xa1, 0x8c, 0x43, 0x7e, 0xb7, 0xbb, 0xf2, 0x93, 0x31, 0x46, 0x57, 0xbe, 0x05, 0x0f, 0xba, 0x0f, - 0x55, 0x56, 0x43, 0xbe, 0xb0, 0x5e, 0x9d, 0x94, 0xef, 0x11, 0xaf, 0x1f, 0xe3, 0xd0, 0x4d, 0x7e, - 0xf7, 0x4d, 0xba, 0x57, 0x59, 0x52, 0xef, 0x55, 0x2e, 0x43, 0xcd, 0x0e, 0xc8, 0x5e, 0xfa, 0x9a, - 0xed, 0x48, 0x53, 0x15, 0x9e, 0x87, 0x43, 0x47, 0x5e, 0xf7, 0xfb, 0x28, 0xba, 0x72, 0x9d, 0xf4, - 0x87, 0x77, 0xa1, 0x4a, 0xe1, 0xa9, 0xbd, 0xe4, 0x38, 0x05, 0x33, 0xa4, 0x7e, 0x19, 0xaa, 0xb4, - 0xb1, 0x23, 0x5a, 0x27, 0xea, 0x53, 0x8e, 0xea, 0xb3, 0x3c, 0x0d, 0x2d, 0x77, 0x40, 0x3c, 0x36, - 0x31, 0xf4, 0x7f, 0xac, 0x4a, 0x97, 0xe2, 0xba, 0xb2, 0x8d, 0x5d, 0x9b, 0xd8, 0x73, 0xca, 0x56, - 0x86, 0x13, 0x56, 0x76, 0x63, 0x72, 0xb6, 0x94, 0x9d, 0xe1, 0x84, 0x9d, 0xfd, 0x04, 0x9c, 0x29, - 0x4b, 0x7b, 0xa8, 0x58, 0xda, 0xf5, 0xc9, 0x19, 0x15, 0x5b, 0x23, 0x45, 0xb6, 0xb6, 0xaa, 0xda, - 0x5a, 0x67, 0xbc, 0x21, 0x8f, 0x96, 0xa6, 0x31, 0xac, 0xed, 0xab, 0xb9, 0xd6, 0xb6, 0xac, 0x58, - 0xdb, 0xa4, 0x45, 0x7f, 0x46, 0xf6, 0xf6, 0x37, 0x55, 0xa8, 0xd2, 0xe5, 0x11, 0xad, 0xc9, 0xb6, - 0xf6, 0xee, 0x44, 0x4b, 0xab, 0x6c, 0x67, 0x1b, 0x09, 0x3b, 0xbb, 0x3a, 0x19, 0x53, 0xca, 0xc6, - 0x36, 0x12, 0x36, 0x36, 0x21, 0x5f, 0xca, 0xbe, 0xd6, 0x15, 0xfb, 0xba, 0x3c, 0x19, 0x9b, 0x62, - 0x5b, 0x46, 0x91, 0x6d, 0xdd, 0x55, 0x6d, 0x6b, 0xcc, 0xdd, 0x1b, 0xdb, 0xab, 0x8c, 0x61, 0x57, - 0x1f, 0xe6, 0xda, 0xd5, 0x6d, 0xc5, 0xae, 0x26, 0x29, 0xf6, 0x33, 0xb2, 0xa9, 0xab, 0x7c, 0xd3, - 0x29, 0xee, 0x19, 0x8f, 0xb9, 0xe9, 0xd4, 0xaf, 0x41, 0x2b, 0x7e, 0x0e, 0x9e, 0x71, 0x0b, 0x9f, - 0xab, 0x85, 0xa5, 0x86, 0x49, 0xfd, 0x0a, 0xb4, 0xe2, 0x27, 0xde, 0x19, 0x65, 0xf9, 0x2c, 0x53, - 0xa0, 0x44, 0x4a, 0x5f, 0x83, 0x23, 0xe9, 0x07, 0xa8, 0x19, 0x71, 0x78, 0xe9, 0x0a, 0x79, 0xf8, - 0xe2, 0x44, 0x12, 0xe9, 0x2f, 0x60, 0x36, 0xf1, 0xa4, 0x74, 0x62, 0x0e, 0x74, 0x45, 0xda, 0x22, - 0x57, 0xc4, 0x19, 0x3c, 0xfb, 0x52, 0x7c, 0xbc, 0x11, 0xd6, 0x57, 0x61, 0xb6, 0xa0, 0xf2, 0xe3, - 0xdc, 0x89, 0xff, 0x3a, 0x4c, 0x8f, 0xaa, 0xfb, 0x67, 0x70, 0x67, 0x3f, 0x80, 0x76, 0xea, 0x39, - 0x7c, 0xb2, 0x98, 0x4d, 0x80, 0x5e, 0xa4, 0x23, 0x8c, 0xf6, 0xd2, 0x04, 0x2f, 0x14, 0x18, 0x0e, - 0x4b, 0x1c, 0xfa, 0xef, 0x94, 0xe0, 0x48, 0xfa, 0x2d, 0xfc, 0xb8, 0x87, 0x1f, 0x0d, 0x1a, 0x8c, - 0x2b, 0x7a, 0xd8, 0x11, 0x26, 0xd1, 0x23, 0x38, 0xe4, 0xf7, 0x6d, 0x93, 0xac, 0xec, 0x1a, 0x4e, - 0x8f, 0xf8, 0xe2, 0x44, 0x53, 0xf0, 0x9e, 0x7d, 0x2b, 0x46, 0x60, 0x05, 0xae, 0xbf, 0x80, 0x69, - 0x29, 0x13, 0xdd, 0x82, 0xb2, 0x3b, 0x48, 0xdd, 0x6b, 0xcc, 0xe7, 0x7c, 0x1c, 0xce, 0x37, 0x5c, - 0x76, 0x07, 0xe9, 0x29, 0x29, 0x4f, 0xdf, 0x8a, 0x32, 0x7d, 0xf5, 0x07, 0x70, 0x24, 0xfd, 0xdc, - 0x3c, 0xd9, 0x3d, 0x67, 0x53, 0x51, 0x02, 0xde, 0x4d, 0xc9, 0x23, 0xff, 0x22, 0x1c, 0x4e, 0x3e, - 0x22, 0xcf, 0x78, 0x74, 0x13, 0xbf, 0x5d, 0x0a, 0xc3, 0xf5, 0x0b, 0xbf, 0x5c, 0x82, 0x59, 0xb5, - 0x21, 0xe8, 0x38, 0x20, 0x55, 0xb2, 0xe1, 0x3a, 0xa4, 0x3d, 0x85, 0x5e, 0x81, 0x23, 0xaa, 0x7c, - 0xc9, 0xb2, 0xda, 0xa5, 0xb4, 0x3a, 0x75, 0x5b, 0xed, 0x32, 0xd2, 0xe0, 0x58, 0xa2, 0x87, 0x98, - 0x13, 0x6d, 0x57, 0xd0, 0x6b, 0xf0, 0x4a, 0x32, 0x67, 0xd0, 0x37, 0x4c, 0xd2, 0xae, 0xea, 0xff, - 0x5c, 0x86, 0xea, 0x53, 0x9f, 0x78, 0xfa, 0x3f, 0x94, 0xc3, 0x57, 0x1a, 0x37, 0xa0, 0xca, 0xde, - 0x77, 0x4b, 0x8f, 0x16, 0x4b, 0x89, 0x47, 0x8b, 0xca, 0xc3, 0xb7, 0xf8, 0xd1, 0xe2, 0x0d, 0xa8, - 0xb2, 0x17, 0xdd, 0x93, 0x23, 0x7f, 0xa1, 0x04, 0xad, 0xf8, 0x75, 0xf5, 0xc4, 0x78, 0xf9, 0x55, - 0x48, 0x59, 0x7d, 0x15, 0xf2, 0x16, 0xd4, 0x3c, 0xf6, 0x7e, 0x83, 0x7b, 0x99, 0xe4, 0x5b, 0x13, - 0x56, 0x20, 0xe6, 0x2a, 0x3a, 0x81, 0x69, 0xf9, 0xed, 0xf8, 0xe4, 0xd5, 0x38, 0x2d, 0x7e, 0x38, - 0xa6, 0x6b, 0xf9, 0x4b, 0x9e, 0x67, 0x1c, 0x08, 0xc3, 0x54, 0x85, 0xfa, 0x1c, 0x54, 0x37, 0x6d, - 0xa7, 0x97, 0xfd, 0x56, 0x54, 0xff, 0x41, 0x09, 0x1a, 0xe2, 0xf2, 0xae, 0xbe, 0x08, 0x95, 0x0d, - 0xf2, 0x82, 0x56, 0x44, 0x5c, 0x1b, 0x4e, 0x55, 0xe4, 0x11, 0x6b, 0x85, 0xd0, 0xc7, 0xa1, 0x9a, - 0x7e, 0x33, 0x5a, 0x26, 0x27, 0xc7, 0xde, 0x80, 0x2a, 0x7b, 0xf2, 0x3d, 0x39, 0xf2, 0xb7, 0x9a, - 0x50, 0xe7, 0x0f, 0x2e, 0xf5, 0xef, 0x35, 0xa1, 0xce, 0x9f, 0x81, 0xa3, 0xdb, 0xd0, 0xf0, 0x87, - 0x7b, 0x7b, 0x86, 0x77, 0xa0, 0x65, 0xff, 0x64, 0xa0, 0xf2, 0x6a, 0xbc, 0xb3, 0xc5, 0x75, 0x71, - 0x08, 0x42, 0xd7, 0xa0, 0x6a, 0x1a, 0x3b, 0x24, 0xf5, 0x39, 0x37, 0x0b, 0xbc, 0x62, 0xec, 0x10, - 0xcc, 0xd4, 0xd1, 0x5d, 0x68, 0x8a, 0x61, 0xf1, 0x45, 0x3c, 0x67, 0x74, 0xb9, 0xe1, 0x60, 0x46, - 0x28, 0xfd, 0x3e, 0x34, 0x44, 0x65, 0xd0, 0x9d, 0xe8, 0xb9, 0x69, 0x32, 0xf2, 0x9c, 0xd9, 0x84, - 0x03, 0xc7, 0x4c, 0x3c, 0x3c, 0xfd, 0xb3, 0x32, 0x54, 0x69, 0xe5, 0x3e, 0x35, 0x13, 0x9a, 0x07, - 0xe8, 0x1b, 0x7e, 0xb0, 0x39, 0xec, 0xf7, 0x89, 0x25, 0x1e, 0xd2, 0x49, 0x12, 0x74, 0x1e, 0x0e, - 0xf3, 0x94, 0xbf, 0xbb, 0x35, 0x34, 0x4d, 0x12, 0xbd, 0x06, 0x4d, 0x8a, 0xd1, 0x12, 0xd4, 0xd8, - 0x0f, 0x93, 0x89, 0x5d, 0xe1, 0xdb, 0x85, 0x3d, 0xdb, 0xd9, 0xb4, 0x1d, 0x51, 0x1b, 0x8e, 0xd4, - 0x5d, 0x68, 0x45, 0x32, 0x3a, 0x09, 0x07, 0xb6, 0xe3, 0xd8, 0x4e, 0x4f, 0x58, 0x74, 0x98, 0xa4, - 0x8b, 0x0e, 0xfd, 0x57, 0xd4, 0xb7, 0x86, 0x45, 0x8a, 0xca, 0x77, 0x0c, 0xbb, 0x2f, 0xaa, 0x58, - 0xc3, 0x22, 0x45, 0x99, 0x86, 0xe2, 0xf1, 0x7c, 0x95, 0x35, 0x30, 0x4c, 0xea, 0x1f, 0x97, 0xa2, - 0x37, 0xd7, 0x59, 0x6f, 0x30, 0x53, 0xb1, 0xa4, 0x39, 0x39, 0xa0, 0xcd, 0x17, 0x04, 0x29, 0x44, - 0x7d, 0x1c, 0xea, 0xae, 0xd3, 0xb7, 0x1d, 0x22, 0x62, 0x47, 0x22, 0x95, 0xe8, 0xe3, 0x5a, 0xaa, - 0x8f, 0x45, 0xfe, 0x9a, 0x65, 0xd3, 0x2a, 0xd6, 0xe3, 0x7c, 0x2e, 0x41, 0xef, 0x43, 0xc3, 0x22, - 0xfb, 0xb6, 0x49, 0x7c, 0xad, 0xc1, 0x4c, 0xef, 0xd4, 0xc8, 0xbe, 0x5d, 0x65, 0xba, 0x38, 0xc4, - 0xe8, 0x01, 0xd4, 0xb9, 0x28, 0x6a, 0x52, 0x49, 0x6a, 0x52, 0x5c, 0xe9, 0xf2, 0x88, 0x4a, 0x57, - 0x0a, 0x2a, 0x5d, 0x4d, 0x56, 0x7a, 0xc1, 0x02, 0x88, 0xcd, 0x0d, 0x4d, 0x43, 0xe3, 0xa9, 0xf3, - 0xdc, 0x71, 0x5f, 0x38, 0xed, 0x29, 0x9a, 0x78, 0xbc, 0xb3, 0x43, 0x4b, 0x69, 0x97, 0x68, 0x82, - 0xea, 0xd9, 0x4e, 0xaf, 0x5d, 0x46, 0x00, 0x75, 0x9a, 0x20, 0x56, 0xbb, 0x42, 0xff, 0xbf, 0xc7, - 0xc6, 0xaf, 0x5d, 0x45, 0xaf, 0xc2, 0xd1, 0xae, 0x63, 0xba, 0x7b, 0x03, 0x23, 0xb0, 0xb7, 0xfb, - 0xe4, 0x19, 0xf1, 0x7c, 0xdb, 0x75, 0xda, 0x35, 0xfd, 0xdf, 0x4b, 0xfc, 0xab, 0xaf, 0x7e, 0x17, - 0x0e, 0x29, 0xbf, 0xe6, 0xa0, 0x41, 0xc3, 0x1f, 0xf0, 0x1f, 0x6f, 0x15, 0xfb, 0x6e, 0x91, 0x64, - 0x56, 0xc2, 0x5f, 0xbf, 0x8b, 0x2d, 0x0b, 0x4f, 0xe9, 0xf7, 0x00, 0xa4, 0xdf, 0x70, 0x98, 0x07, - 0xd8, 0x3e, 0x08, 0x88, 0xcf, 0x7f, 0xbf, 0x81, 0x52, 0x54, 0xb1, 0x24, 0x91, 0xf9, 0xcb, 0x0a, - 0xbf, 0x7e, 0x1d, 0x40, 0xfa, 0x05, 0x07, 0x3a, 0x7f, 0x68, 0x6a, 0x39, 0x49, 0x96, 0x14, 0xeb, - 0x1d, 0xd1, 0x82, 0xf0, 0xb7, 0x1a, 0xc2, 0x1a, 0xf0, 0x28, 0x9d, 0x5c, 0x03, 0x26, 0xd1, 0xd7, - 0x00, 0xe2, 0x9f, 0x2b, 0xd0, 0x17, 0x23, 0x17, 0xfd, 0x0e, 0x54, 0x2d, 0x23, 0x30, 0x84, 0x77, - 0x7c, 0x2d, 0xb1, 0x42, 0xc5, 0x10, 0xcc, 0xd4, 0xf4, 0xdf, 0x2e, 0xc1, 0x21, 0xf9, 0xa7, 0x19, - 0xf4, 0x0f, 0xa0, 0xca, 0x7e, 0xdb, 0xe1, 0x0e, 0x1c, 0x92, 0x7f, 0x9b, 0x21, 0xf5, 0xcb, 0xb6, - 0x9c, 0x4f, 0x86, 0x62, 0x05, 0xa0, 0x77, 0xa3, 0x2a, 0x7d, 0x6a, 0xaa, 0x4b, 0xd0, 0x10, 0x3f, - 0xf5, 0xa0, 0x9f, 0x81, 0x56, 0xfc, 0xcb, 0x0e, 0xd4, 0x47, 0x70, 0x79, 0x38, 0xca, 0x22, 0xa9, - 0x7f, 0xb7, 0x04, 0xad, 0xe8, 0xe7, 0x17, 0xf4, 0xa5, 0xa8, 0x2a, 0x8b, 0x09, 0x87, 0x99, 0xff, - 0x93, 0x0d, 0xaa, 0xa3, 0x5c, 0x58, 0x0c, 0x17, 0x23, 0x74, 0x88, 0x3d, 0x74, 0xf6, 0xa9, 0x21, - 0xb6, 0xa7, 0xd0, 0x0c, 0xb4, 0x56, 0x5c, 0xc7, 0x21, 0x66, 0x40, 0xe8, 0x76, 0xaa, 0xcd, 0x7a, - 0x33, 0x96, 0x94, 0x17, 0xbe, 0x0d, 0x33, 0x98, 0xf8, 0x03, 0xd7, 0xf1, 0xc9, 0x4f, 0xeb, 0xf7, - 0x8d, 0x73, 0x7f, 0xa9, 0x78, 0xe1, 0x07, 0x15, 0xa8, 0xb1, 0xe5, 0x55, 0xff, 0x83, 0x4a, 0xb4, - 0x11, 0xc8, 0xb8, 0x7c, 0x15, 0x5f, 0x91, 0x98, 0x95, 0xc6, 0x45, 0x59, 0x98, 0xe5, 0x38, 0xfb, - 0x65, 0xf9, 0x6a, 0xc4, 0xac, 0xf4, 0xc3, 0x2d, 0x2a, 0x42, 0xb9, 0x12, 0xf1, 0x1e, 0x34, 0x07, - 0x9e, 0xdb, 0xf3, 0xe8, 0x0e, 0xa0, 0x9a, 0xf8, 0xb9, 0x0c, 0x15, 0xb6, 0x29, 0xd4, 0x70, 0x04, - 0xd0, 0x37, 0xa0, 0x19, 0x4a, 0x73, 0x1e, 0x84, 0x23, 0xa8, 0x5a, 0xae, 0xf0, 0x62, 0x15, 0xcc, - 0xfe, 0xa7, 0xfd, 0x22, 0x7a, 0x30, 0xdc, 0xbd, 0x8b, 0xe4, 0xc2, 0xd7, 0xc4, 0xa7, 0xab, 0x19, - 0x68, 0xad, 0x7a, 0xee, 0x80, 0x3d, 0x09, 0x6e, 0x4f, 0x51, 0x9f, 0xd3, 0xdd, 0x1b, 0xb8, 0x5e, - 0xd0, 0x2e, 0xd1, 0xff, 0xd7, 0x5e, 0xb2, 0xff, 0xcb, 0x74, 0xc8, 0xb7, 0x8c, 0x7d, 0x42, 0xd5, - 0xda, 0x15, 0x84, 0xe8, 0xc1, 0x91, 0x85, 0xeb, 0xc5, 0xda, 0xd1, 0xae, 0x52, 0xa2, 0x47, 0x76, - 0x8f, 0xef, 0x87, 0xdb, 0xb5, 0x85, 0xa5, 0xf0, 0x8a, 0x42, 0x13, 0xaa, 0x62, 0xff, 0x3d, 0x0d, - 0x0d, 0x3c, 0x64, 0x0b, 0x58, 0xbb, 0x44, 0xc5, 0x74, 0x57, 0xc4, 0xa9, 0x57, 0x0c, 0xc7, 0x24, - 0x7d, 0xe6, 0xf4, 0x5a, 0x50, 0x5b, 0xf3, 0x3c, 0xd7, 0x6b, 0x57, 0x97, 0xe7, 0xfe, 0xfc, 0xe3, - 0xf9, 0xd2, 0x0f, 0x3f, 0x9e, 0x2f, 0xfd, 0xf8, 0xe3, 0xf9, 0xd2, 0xaf, 0x7d, 0x32, 0x3f, 0xf5, - 0xc3, 0x4f, 0xe6, 0xa7, 0xfe, 0xfe, 0x93, 0xf9, 0xa9, 0x8f, 0xca, 0x83, 0xed, 0xed, 0x3a, 0xfb, - 0xb6, 0x7c, 0xe5, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x83, 0x93, 0x5a, 0xa2, 0x9d, 0x5b, 0x00, - 0x00, + // 5576 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5c, 0x4b, 0x6c, 0x1c, 0xc9, + 0x79, 0xe6, 0xbc, 0x67, 0x7e, 0x8a, 0xd4, 0xa8, 0xa4, 0xd5, 0xf6, 0xb6, 0xb8, 0x5c, 0x2d, 0xf5, + 0xf4, 0xae, 0x76, 0xa4, 0xd5, 0x8b, 0xb2, 0xac, 0x95, 0xc4, 0x97, 0x96, 0xa3, 0x07, 0xc5, 0x14, + 0x25, 0x79, 0xbd, 0x6b, 0x18, 0x6e, 0x76, 0x17, 0x87, 0x6d, 0x0d, 0xbb, 0xc7, 0xdd, 0x3d, 0x94, + 0x68, 0x27, 0x4e, 0x90, 0x07, 0x72, 0x49, 0x90, 0x9c, 0x9c, 0x20, 0xb7, 0x04, 0x09, 0x90, 0x43, + 0x10, 0xc4, 0xc8, 0xc5, 0xa7, 0x20, 0x40, 0x10, 0x20, 0xaf, 0x83, 0x93, 0x53, 0x6e, 0x36, 0x76, + 0x2f, 0xb9, 0x04, 0x48, 0x62, 0x20, 0xc8, 0x29, 0x08, 0xea, 0xd1, 0xdd, 0x55, 0xfd, 0x98, 0x9e, + 0xf1, 0xae, 0xf3, 0x40, 0xf6, 0x44, 0xd6, 0x5f, 0xff, 0xf7, 0xd5, 0xeb, 0xaf, 0xbf, 0xaa, 0xfe, + 0xae, 0x1a, 0x38, 0x3e, 0xd8, 0xbe, 0x38, 0xf0, 0xdc, 0xc0, 0xf5, 0x2f, 0x92, 0x7d, 0xe2, 0x04, + 0x7e, 0x87, 0xa5, 0x50, 0xc3, 0x70, 0x0e, 0x82, 0x83, 0x01, 0xd1, 0x4f, 0x0f, 0x9e, 0xf7, 0x2e, + 0xf6, 0xed, 0xed, 0x8b, 0x83, 0xed, 0x8b, 0x7b, 0xae, 0x45, 0xfa, 0xa1, 0x3a, 0x4b, 0x08, 0x75, + 0x7d, 0xae, 0xe7, 0xba, 0xbd, 0x3e, 0xe1, 0x79, 0xdb, 0xc3, 0x9d, 0x8b, 0x7e, 0xe0, 0x0d, 0xcd, + 0x80, 0xe7, 0x2e, 0xfc, 0xf8, 0x7b, 0x25, 0xa8, 0xad, 0x51, 0x7a, 0x74, 0x19, 0x9a, 0x7b, 0xc4, + 0xf7, 0x8d, 0x1e, 0xf1, 0xb5, 0xd2, 0xc9, 0xca, 0xf9, 0xe9, 0xcb, 0xc7, 0x3b, 0xa2, 0xa8, 0x0e, + 0xd3, 0xe8, 0x3c, 0xe2, 0xd9, 0x38, 0xd2, 0x43, 0x73, 0xd0, 0x32, 0x5d, 0x27, 0x20, 0x2f, 0x83, + 0xae, 0xa5, 0x95, 0x4f, 0x96, 0xce, 0xb7, 0x70, 0x2c, 0x40, 0x57, 0xa1, 0x65, 0x3b, 0x76, 0x60, + 0x1b, 0x81, 0xeb, 0x69, 0x95, 0x93, 0x25, 0x85, 0x92, 0x55, 0xb2, 0xb3, 0x64, 0x9a, 0xee, 0xd0, + 0x09, 0x70, 0xac, 0x88, 0x34, 0x68, 0x04, 0x9e, 0x61, 0x92, 0xae, 0xa5, 0x55, 0x19, 0x63, 0x98, + 0xd4, 0xff, 0xe1, 0x02, 0x34, 0x44, 0x1d, 0xd0, 0x1d, 0x98, 0x36, 0x38, 0x76, 0x6b, 0xd7, 0x7d, + 0xa1, 0x95, 0x18, 0xfb, 0x89, 0x44, 0x85, 0x05, 0x7b, 0x87, 0xaa, 0xac, 0x4f, 0x61, 0x19, 0x81, + 0xba, 0x30, 0x2b, 0x92, 0xab, 0x24, 0x30, 0xec, 0xbe, 0xaf, 0xfd, 0x35, 0x27, 0x99, 0xcf, 0x21, + 0x11, 0x6a, 0xeb, 0x53, 0x38, 0x01, 0x44, 0x5f, 0x81, 0xa3, 0x42, 0xb2, 0xe2, 0x3a, 0x3b, 0x76, + 0xef, 0xe9, 0xc0, 0x32, 0x02, 0xa2, 0xfd, 0x0d, 0xe7, 0x3b, 0x9d, 0xc3, 0xc7, 0x75, 0x3b, 0x5c, + 0x79, 0x7d, 0x0a, 0x67, 0x71, 0xa0, 0x7b, 0x30, 0x23, 0xc4, 0x82, 0xf4, 0x6f, 0x39, 0xe9, 0xeb, + 0x39, 0xa4, 0x11, 0x9b, 0x0a, 0x43, 0x1f, 0xc1, 0x31, 0x21, 0x78, 0x68, 0x3b, 0xcf, 0x57, 0x76, + 0x8d, 0x7e, 0x9f, 0x38, 0x3d, 0xa2, 0xfd, 0xdd, 0xe8, 0x3a, 0x2a, 0xca, 0xeb, 0x53, 0x38, 0x93, + 0x04, 0x3d, 0x86, 0xb6, 0xbb, 0xfd, 0x0d, 0x62, 0x86, 0x1d, 0xb2, 0x45, 0x02, 0xad, 0xcd, 0x78, + 0xdf, 0x4c, 0xf0, 0x3e, 0x66, 0x6a, 0x61, 0x57, 0x76, 0xb6, 0x48, 0xb0, 0x3e, 0x85, 0x53, 0x60, + 0xf4, 0x14, 0x90, 0x22, 0x5b, 0xda, 0x23, 0x8e, 0xa5, 0x5d, 0x66, 0x94, 0xa7, 0x46, 0x53, 0x32, + 0xd5, 0xf5, 0x29, 0x9c, 0x41, 0x90, 0xa2, 0x7d, 0xea, 0xf8, 0x24, 0xd0, 0xae, 0x8c, 0x43, 0xcb, + 0x54, 0x53, 0xb4, 0x4c, 0x4a, 0xfb, 0x96, 0x4b, 0x31, 0xe9, 0x1b, 0x81, 0xed, 0x3a, 0xa2, 0xbe, + 0x57, 0x19, 0xf1, 0x99, 0x6c, 0xe2, 0x48, 0x37, 0xaa, 0x71, 0x26, 0x09, 0xfa, 0x1a, 0xbc, 0x92, + 0x90, 0x63, 0xb2, 0xe7, 0xee, 0x13, 0xed, 0x1a, 0x63, 0x3f, 0x5b, 0xc4, 0xce, 0xb5, 0xd7, 0xa7, + 0x70, 0x36, 0x0d, 0x5a, 0x86, 0x43, 0x61, 0x06, 0xa3, 0xbd, 0xce, 0x68, 0xe7, 0xf2, 0x68, 0x05, + 0x99, 0x82, 0x91, 0xeb, 0xe8, 0x07, 0x9e, 0x6d, 0x32, 0x7e, 0x6a, 0x04, 0x8b, 0xa3, 0xeb, 0x18, + 0x2b, 0x0b, 0x4b, 0xc8, 0xa6, 0x41, 0x18, 0x0e, 0xfb, 0xc3, 0x6d, 0xdf, 0xf4, 0xec, 0x01, 0x95, + 0x2d, 0x59, 0x96, 0x76, 0x6b, 0x14, 0xf3, 0x96, 0xa4, 0xdc, 0x59, 0xb2, 0x68, 0xe7, 0x26, 0x09, + 0xd0, 0x47, 0x80, 0x64, 0x91, 0x68, 0xfd, 0x7b, 0x8c, 0xf6, 0x0b, 0x63, 0xd0, 0x46, 0x5d, 0x91, + 0x41, 0x83, 0x0c, 0x38, 0x26, 0x4b, 0x37, 0x5d, 0xdf, 0xa6, 0x7f, 0xb5, 0xdb, 0x8c, 0xfe, 0xed, + 0x31, 0xe8, 0x43, 0x08, 0xb5, 0x8b, 0x2c, 0xaa, 0x64, 0x11, 0x2b, 0x74, 0x56, 0x12, 0xcf, 0xd7, + 0xee, 0x8c, 0x5d, 0x44, 0x08, 0x49, 0x16, 0x11, 0xca, 0x93, 0x5d, 0xf4, 0xbe, 0xe7, 0x0e, 0x07, + 0xbe, 0x76, 0x77, 0xec, 0x2e, 0xe2, 0x80, 0x64, 0x17, 0x71, 0x29, 0xba, 0x0e, 0xcd, 0xed, 0xbe, + 0x6b, 0x3e, 0xa7, 0x83, 0x59, 0x66, 0x94, 0x5a, 0x82, 0x72, 0x99, 0x66, 0x8b, 0xe1, 0x8b, 0x74, + 0xa9, 0xdf, 0x67, 0xff, 0xaf, 0x92, 0x3e, 0x09, 0x88, 0x58, 0x55, 0x4e, 0x64, 0x42, 0xb9, 0x0a, + 0xf5, 0xfb, 0x12, 0x02, 0xad, 0xc2, 0xf4, 0x8e, 0xdd, 0x27, 0xfe, 0xd3, 0x41, 0xdf, 0x35, 0xf8, + 0x12, 0x33, 0x7d, 0xf9, 0x64, 0x26, 0xc1, 0xbd, 0x58, 0x8f, 0xb2, 0x48, 0x30, 0x74, 0x1b, 0x5a, + 0x7b, 0x86, 0xf7, 0xdc, 0xef, 0x3a, 0x3b, 0xae, 0x56, 0xcb, 0x5c, 0x37, 0x38, 0xc7, 0xa3, 0x50, + 0x6b, 0x7d, 0x0a, 0xc7, 0x10, 0xba, 0xfa, 0xb0, 0x4a, 0x6d, 0x91, 0xe0, 0x9e, 0x4d, 0xfa, 0x96, + 0xaf, 0xd5, 0x19, 0xc9, 0x1b, 0x99, 0x24, 0x5b, 0x24, 0xe8, 0x70, 0x35, 0xba, 0xfa, 0xa8, 0x40, + 0xf4, 0x01, 0x1c, 0x0d, 0x25, 0x2b, 0xbb, 0x76, 0xdf, 0xf2, 0x88, 0xd3, 0xb5, 0x7c, 0xad, 0x91, + 0xe9, 0xd8, 0x63, 0x3e, 0x49, 0x97, 0x2e, 0x3e, 0x19, 0x14, 0xd4, 0xb1, 0x85, 0x62, 0x79, 0x4a, + 0x6a, 0xcd, 0x4c, 0xc7, 0x16, 0x53, 0xcb, 0xca, 0xd4, 0xba, 0xb2, 0x48, 0x90, 0x05, 0xaf, 0x86, + 0xf2, 0x65, 0xc3, 0x7c, 0xde, 0xf3, 0xdc, 0xa1, 0x63, 0xad, 0xb8, 0x7d, 0xd7, 0xd3, 0x5a, 0x8c, + 0xff, 0x7c, 0x2e, 0x7f, 0x42, 0x7f, 0x7d, 0x0a, 0xe7, 0x51, 0xa1, 0x15, 0x38, 0x14, 0x66, 0x3d, + 0x21, 0x2f, 0x03, 0x0d, 0x32, 0x57, 0xcf, 0x98, 0x9a, 0x2a, 0x51, 0xff, 0x26, 0x83, 0x64, 0x12, + 0x6a, 0x12, 0xda, 0x74, 0x01, 0x09, 0x55, 0x92, 0x49, 0x68, 0x5a, 0x26, 0xa1, 0xab, 0xa7, 0x36, + 0x53, 0x40, 0x42, 0x95, 0x64, 0x12, 0x9a, 0xa6, 0x2b, 0x6d, 0xd4, 0x52, 0xd7, 0x7d, 0x4e, 0xed, + 0x49, 0x9b, 0xcd, 0x5c, 0x69, 0xa5, 0xde, 0x12, 0x8a, 0x74, 0xa5, 0x4d, 0x82, 0xe9, 0xfe, 0x22, + 0x94, 0x2d, 0xf5, 0xed, 0x9e, 0xa3, 0x1d, 0x1e, 0x61, 0xcb, 0x94, 0x8d, 0x69, 0xd1, 0xfd, 0x85, + 0x02, 0x43, 0x77, 0xc5, 0xb4, 0xdc, 0x22, 0xc1, 0xaa, 0xbd, 0xaf, 0x1d, 0xc9, 0x5c, 0x45, 0x62, + 0x96, 0x55, 0x7b, 0x3f, 0x9a, 0x97, 0x1c, 0x22, 0x37, 0x2d, 0x5c, 0xa3, 0xb4, 0x57, 0x0a, 0x9a, + 0x16, 0x2a, 0xca, 0x4d, 0x0b, 0x65, 0x72, 0xd3, 0x1e, 0x1a, 0x01, 0x79, 0xa9, 0xbd, 0x56, 0xd0, + 0x34, 0xa6, 0x25, 0x37, 0x8d, 0x09, 0xe8, 0xea, 0x16, 0x0a, 0x9e, 0x11, 0x2f, 0xb0, 0x4d, 0xa3, + 0xcf, 0xbb, 0xea, 0x74, 0xe6, 0x1a, 0x14, 0xf3, 0x29, 0xda, 0x74, 0x75, 0xcb, 0xa4, 0x91, 0x1b, + 0xfe, 0xc4, 0xd8, 0xee, 0x13, 0xec, 0xbe, 0xd0, 0xce, 0x14, 0x34, 0x3c, 0x54, 0x94, 0x1b, 0x1e, + 0xca, 0x64, 0xdf, 0xf2, 0x65, 0xdb, 0xea, 0x91, 0x40, 0x3b, 0x5f, 0xe0, 0x5b, 0xb8, 0x9a, 0xec, + 0x5b, 0xb8, 0x24, 0xf2, 0x00, 0xab, 0x46, 0x60, 0xec, 0xdb, 0xe4, 0xc5, 0x33, 0x9b, 0xbc, 0xa0, + 0x0b, 0xfb, 0xd1, 0x11, 0x1e, 0x20, 0xd4, 0xed, 0x08, 0xe5, 0xc8, 0x03, 0x24, 0x48, 0x22, 0x0f, + 0x20, 0xcb, 0x85, 0x5b, 0x3f, 0x36, 0xc2, 0x03, 0x28, 0xfc, 0x91, 0x8f, 0xcf, 0xa3, 0x42, 0x06, + 0x1c, 0x4f, 0x65, 0x3d, 0xf6, 0x2c, 0xe2, 0x69, 0xaf, 0xb3, 0x42, 0xce, 0x15, 0x17, 0xc2, 0xd4, + 0xd7, 0xa7, 0x70, 0x0e, 0x51, 0xaa, 0x88, 0x2d, 0x77, 0xe8, 0x99, 0x84, 0xf6, 0xd3, 0xa9, 0x71, + 0x8a, 0x88, 0xd4, 0x53, 0x45, 0x44, 0x39, 0x68, 0x1f, 0x5e, 0x8f, 0x72, 0x68, 0xc1, 0x6c, 0x15, + 0x65, 0xa5, 0x8b, 0x73, 0xc1, 0x59, 0x56, 0x52, 0x67, 0x74, 0x49, 0x49, 0xd4, 0xfa, 0x14, 0x1e, + 0x4d, 0x8b, 0x0e, 0x60, 0x5e, 0x51, 0xe0, 0xeb, 0xbc, 0x5c, 0xf0, 0x39, 0x56, 0xf0, 0xc5, 0xd1, + 0x05, 0xa7, 0x60, 0xeb, 0x53, 0xb8, 0x80, 0x18, 0x0d, 0xe0, 0x84, 0xd2, 0x19, 0xe1, 0xc4, 0x16, + 0x26, 0xf2, 0xb3, 0xac, 0xdc, 0x0b, 0xa3, 0xcb, 0x55, 0x31, 0xeb, 0x53, 0x78, 0x14, 0x25, 0xea, + 0x81, 0x96, 0x99, 0x4d, 0x47, 0xf2, 0xdb, 0x99, 0xdb, 0x9e, 0x9c, 0xe2, 0xf8, 0x58, 0xe6, 0x92, + 0x65, 0x5a, 0xbe, 0xe8, 0xce, 0x9f, 0x1b, 0xd7, 0xf2, 0xa3, 0x7e, 0xcc, 0xa3, 0x52, 0xc6, 0x8e, + 0x66, 0x3d, 0x31, 0xbc, 0x1e, 0x09, 0x78, 0x47, 0x77, 0x2d, 0xda, 0xa8, 0xef, 0x8c, 0x33, 0x76, + 0x29, 0x98, 0x32, 0x76, 0x99, 0xc4, 0xc8, 0x87, 0x39, 0x45, 0xa3, 0xeb, 0xaf, 0xb8, 0xfd, 0x3e, + 0x31, 0xc3, 0xde, 0xfc, 0x79, 0x56, 0xf0, 0x3b, 0xa3, 0x0b, 0x4e, 0x80, 0xd6, 0xa7, 0xf0, 0x48, + 0xd2, 0x54, 0x7b, 0x1f, 0xf7, 0xad, 0x84, 0xcd, 0x68, 0x63, 0xd9, 0x6a, 0x12, 0x96, 0x6a, 0x6f, + 0x4a, 0x23, 0x65, 0xab, 0x92, 0x06, 0x6d, 0xee, 0xab, 0xe3, 0xd8, 0xaa, 0x8a, 0x49, 0xd9, 0xaa, + 0x9a, 0x4d, 0x57, 0xb7, 0xa1, 0x4f, 0x3c, 0xc6, 0x71, 0xdf, 0xb5, 0x1d, 0xed, 0x8d, 0xcc, 0xd5, + 0xed, 0xa9, 0x4f, 0x3c, 0x51, 0x10, 0xd5, 0xa2, 0xab, 0x9b, 0x02, 0x53, 0x78, 0x1e, 0x92, 0x9d, + 0x40, 0x3b, 0x59, 0xc4, 0x43, 0xb5, 0x14, 0x1e, 0x2a, 0xa0, 0x2b, 0x45, 0x24, 0xd8, 0x22, 0x74, + 0x54, 0xb0, 0xe1, 0xf4, 0x88, 0xf6, 0x66, 0xe6, 0x4a, 0x21, 0xd1, 0x49, 0xca, 0x74, 0xa5, 0xc8, + 0x22, 0xa1, 0x07, 0xf7, 0x48, 0x4e, 0x77, 0x64, 0x9c, 0x7a, 0x21, 0xf3, 0xe0, 0x2e, 0x51, 0x47, + 0xaa, 0xf4, 0x0c, 0x92, 0x26, 0x40, 0x5f, 0x80, 0xea, 0xc0, 0x76, 0x7a, 0x9a, 0xc5, 0x88, 0x8e, + 0x26, 0x88, 0x36, 0x6d, 0xa7, 0xb7, 0x3e, 0x85, 0x99, 0x0a, 0xba, 0x05, 0x30, 0xf0, 0x5c, 0x93, + 0xf8, 0xfe, 0x06, 0x79, 0xa1, 0x11, 0x06, 0xd0, 0x93, 0x00, 0xae, 0xd0, 0xd9, 0x20, 0x74, 0x5d, + 0x96, 0xf4, 0xd1, 0x1a, 0xcc, 0x88, 0x94, 0x98, 0xe5, 0x3b, 0x99, 0x9b, 0xbf, 0x90, 0x20, 0x0e, + 0xe2, 0x28, 0x28, 0x7a, 0xf6, 0x11, 0x82, 0x55, 0xd7, 0x21, 0x5a, 0x2f, 0xf3, 0xec, 0x13, 0x92, + 0x50, 0x15, 0xba, 0xc7, 0x92, 0x10, 0xf4, 0xb0, 0x1f, 0xec, 0x7a, 0xc4, 0xb0, 0xb6, 0x02, 0x23, + 0x18, 0xfa, 0x9a, 0x93, 0xb9, 0x4d, 0xe3, 0x99, 0x9d, 0x27, 0x4c, 0x93, 0x6e, 0x41, 0x65, 0x0c, + 0xda, 0x80, 0x36, 0x3d, 0x08, 0x3d, 0xb4, 0xf7, 0xec, 0x00, 0x13, 0xc3, 0xdc, 0x25, 0x96, 0xe6, + 0x66, 0x1e, 0xa2, 0xe8, 0xb6, 0xb7, 0x23, 0xeb, 0xd1, 0xdd, 0x4a, 0x12, 0x8b, 0xd6, 0x61, 0x96, + 0xca, 0xb6, 0x06, 0x86, 0x49, 0x9e, 0xfa, 0x46, 0x8f, 0x68, 0x83, 0x4c, 0x0b, 0x64, 0x6c, 0xb1, + 0x16, 0xdd, 0xac, 0xa8, 0xb8, 0x90, 0xe9, 0xa1, 0x6b, 0x1a, 0x7d, 0xce, 0xf4, 0xcd, 0x7c, 0xa6, + 0x58, 0x2b, 0x64, 0x8a, 0x25, 0x4a, 0x1b, 0x79, 0xdf, 0x5b, 0xda, 0x7e, 0x41, 0x1b, 0x85, 0x9e, + 0xd2, 0x46, 0x21, 0xa3, 0x7c, 0x8e, 0x1b, 0xd8, 0x3b, 0xb6, 0x29, 0xe6, 0xaf, 0x63, 0x69, 0x5e, + 0x26, 0xdf, 0x86, 0xa4, 0xd6, 0xd9, 0xe2, 0x81, 0xa1, 0x14, 0x16, 0x3d, 0x01, 0x24, 0xcb, 0x84, + 0x51, 0xf9, 0x8c, 0x71, 0x61, 0x14, 0x63, 0x64, 0x59, 0x19, 0x78, 0x5a, 0xcb, 0x81, 0x71, 0x40, + 0x8f, 0xb7, 0xcb, 0x9e, 0x6b, 0x58, 0xa6, 0xe1, 0x07, 0x5a, 0x90, 0x59, 0xcb, 0x4d, 0xae, 0xd6, + 0x89, 0xf4, 0x68, 0x2d, 0x93, 0x58, 0xca, 0xb7, 0x47, 0xf6, 0xb6, 0x89, 0xe7, 0xef, 0xda, 0x03, + 0x51, 0xc7, 0x61, 0x26, 0xdf, 0xa3, 0x48, 0x2d, 0xae, 0x61, 0x0a, 0x8b, 0x1e, 0xc0, 0xe1, 0xc1, + 0xe5, 0x01, 0x37, 0x43, 0x41, 0xf7, 0x22, 0x73, 0x63, 0xbb, 0x79, 0x79, 0x53, 0xd8, 0x70, 0xc4, + 0x96, 0x44, 0x2e, 0x37, 0xa0, 0xb6, 0x6f, 0xf4, 0x87, 0x44, 0xff, 0x5e, 0x0d, 0x1a, 0x22, 0xdc, + 0xa9, 0x6f, 0x40, 0x95, 0xc5, 0x86, 0x8f, 0x41, 0xcd, 0x76, 0x2c, 0xf2, 0x92, 0x85, 0x95, 0x6b, + 0x98, 0x27, 0xd0, 0x25, 0x68, 0x88, 0xf0, 0xa7, 0x88, 0x58, 0xe4, 0x05, 0xb3, 0x43, 0x35, 0xfd, + 0x43, 0x68, 0x84, 0x31, 0xe2, 0x39, 0x68, 0x0d, 0x3c, 0x97, 0x5a, 0x46, 0xd7, 0x62, 0xb4, 0x2d, + 0x1c, 0x0b, 0xd0, 0xbb, 0xd0, 0xb0, 0x44, 0x14, 0x9a, 0x53, 0xbf, 0xda, 0xe1, 0x61, 0xfb, 0x4e, + 0x18, 0xb6, 0xef, 0x6c, 0xb1, 0xb0, 0x3d, 0x0e, 0xf5, 0xf4, 0x5f, 0x28, 0x41, 0x9d, 0x87, 0x8a, + 0xf5, 0x7d, 0xa8, 0x8b, 0x2e, 0xba, 0x06, 0x75, 0x93, 0xc9, 0xb4, 0x64, 0x98, 0x58, 0xa9, 0xa1, + 0x88, 0x3d, 0x63, 0xa1, 0x4c, 0x61, 0x3e, 0xf7, 0x08, 0xe5, 0x91, 0x30, 0xde, 0x83, 0x58, 0x28, + 0xff, 0x8f, 0x95, 0xfb, 0x9f, 0x25, 0x98, 0x51, 0x23, 0xd0, 0x73, 0xd0, 0x32, 0xa3, 0x98, 0xb6, + 0xe8, 0x5d, 0x53, 0x8a, 0x4f, 0x83, 0xd9, 0xb7, 0x89, 0x13, 0xb0, 0x68, 0x4d, 0x39, 0x73, 0x13, + 0x90, 0x19, 0xf1, 0xee, 0xac, 0x44, 0x30, 0x2c, 0x51, 0xe8, 0xdf, 0x01, 0x88, 0x73, 0xd0, 0xc9, + 0xc8, 0x2d, 0x6f, 0x18, 0x7b, 0x61, 0xf1, 0xb2, 0x48, 0xd2, 0xd8, 0x34, 0x82, 0x5d, 0xf1, 0xa1, + 0x44, 0x16, 0xa1, 0x0b, 0x70, 0xc4, 0xb7, 0x7b, 0x8e, 0x11, 0x0c, 0x3d, 0xf2, 0x8c, 0x78, 0xf6, + 0x8e, 0x4d, 0x2c, 0x16, 0xdc, 0x6a, 0xe2, 0x74, 0x86, 0xfe, 0x2f, 0x4d, 0xa8, 0xf3, 0xed, 0x96, + 0xfe, 0xef, 0xe5, 0xc8, 0xc6, 0xf4, 0xbf, 0x28, 0x41, 0x8d, 0x47, 0x8d, 0x67, 0xa1, 0x6c, 0x87, + 0x66, 0x56, 0xb6, 0x2d, 0x74, 0x4f, 0xb6, 0xaf, 0x4a, 0xc6, 0x5e, 0x24, 0x2b, 0x8a, 0xde, 0x79, + 0x40, 0x0e, 0x9e, 0xd1, 0x39, 0x12, 0x19, 0x1d, 0x3a, 0x0e, 0x75, 0x7f, 0xb8, 0xdd, 0xb5, 0x7c, + 0xad, 0x72, 0xb2, 0x72, 0xbe, 0x85, 0x45, 0x4a, 0xbf, 0x0f, 0xcd, 0x50, 0x19, 0xb5, 0xa1, 0xf2, + 0x9c, 0x1c, 0x88, 0xc2, 0xe9, 0xbf, 0xe8, 0x82, 0x98, 0x6b, 0xd1, 0xb4, 0x49, 0xda, 0x36, 0x2f, + 0x45, 0x4c, 0xc8, 0xaf, 0x43, 0x85, 0x6e, 0x70, 0x92, 0x4d, 0x98, 0x7c, 0x8a, 0xe4, 0xd6, 0x76, + 0x05, 0x6a, 0x3c, 0x72, 0x9f, 0x2c, 0x03, 0x41, 0xf5, 0x39, 0x39, 0xe0, 0x7d, 0xd4, 0xc2, 0xec, + 0xff, 0x5c, 0x92, 0x3f, 0xaf, 0xc0, 0x21, 0x39, 0xdc, 0xa9, 0xaf, 0x41, 0x65, 0xc9, 0x4a, 0x77, + 0xbd, 0x06, 0x0d, 0x63, 0x27, 0x20, 0x5e, 0xf4, 0x81, 0x2c, 0x4c, 0x52, 0x2f, 0xc3, 0xb8, 0xd8, + 0x38, 0xb7, 0x30, 0x4f, 0xe8, 0x1d, 0xa8, 0x8b, 0x28, 0x72, 0x92, 0x29, 0xd2, 0x2f, 0xcb, 0xfa, + 0xf7, 0xa1, 0x19, 0x05, 0x85, 0x3f, 0x6d, 0xd9, 0x1e, 0x34, 0xa3, 0xe8, 0xef, 0x31, 0xa8, 0x05, + 0x6e, 0x60, 0xf4, 0x19, 0x5d, 0x05, 0xf3, 0x04, 0x9d, 0x68, 0x0e, 0x79, 0x19, 0xac, 0x44, 0x5e, + 0xb0, 0x82, 0x63, 0x01, 0x77, 0x72, 0x64, 0x9f, 0xe7, 0x56, 0x78, 0x6e, 0x24, 0x88, 0xcb, 0xac, + 0xca, 0x65, 0x1e, 0x40, 0x5d, 0x84, 0x84, 0xa3, 0xfc, 0x92, 0x94, 0x8f, 0x96, 0xa0, 0xd6, 0xa3, + 0xf9, 0x62, 0xd4, 0xdf, 0x4e, 0xb8, 0x08, 0xbe, 0xd3, 0x5b, 0x71, 0x9d, 0x80, 0x9a, 0xb1, 0x7a, + 0xd2, 0xc5, 0x1c, 0x49, 0x87, 0xd0, 0xe3, 0xf1, 0x7d, 0x3e, 0xa3, 0x44, 0x4a, 0xff, 0x83, 0x12, + 0xb4, 0xa2, 0xef, 0x21, 0xfa, 0x87, 0x79, 0x93, 0x67, 0x09, 0x66, 0x3c, 0xa1, 0x45, 0xbd, 0x43, + 0x38, 0x85, 0x4e, 0x24, 0x6a, 0x82, 0x25, 0x1d, 0xac, 0x22, 0xf4, 0x5b, 0xb9, 0x83, 0xba, 0x00, + 0x87, 0x42, 0xd5, 0x07, 0xb1, 0xe9, 0x29, 0x32, 0x5d, 0x8f, 0xd0, 0x6d, 0xa8, 0xd8, 0x16, 0xff, + 0x3c, 0xdb, 0xc2, 0xf4, 0x5f, 0x7d, 0x07, 0x0e, 0xc9, 0x61, 0x55, 0xfd, 0x59, 0xf6, 0xec, 0xb9, + 0x43, 0x8b, 0x91, 0x42, 0xb8, 0xe5, 0xc4, 0xde, 0x31, 0x6c, 0x42, 0xac, 0x82, 0x15, 0x80, 0xfe, + 0x3b, 0x26, 0xd4, 0x58, 0x5f, 0xeb, 0x57, 0xb8, 0x9d, 0x5f, 0x80, 0x3a, 0x3b, 0x9f, 0x84, 0x1f, + 0x8b, 0x8f, 0x65, 0x0d, 0x0c, 0x16, 0x3a, 0xfa, 0x0a, 0x4c, 0x4b, 0xd1, 0x74, 0x6a, 0x98, 0x2c, + 0x23, 0x1a, 0xec, 0x30, 0x89, 0x74, 0x68, 0xd2, 0x35, 0x51, 0xf8, 0x49, 0xda, 0xcc, 0x28, 0xad, + 0x9f, 0x86, 0xba, 0x38, 0x6f, 0xe9, 0xe2, 0xeb, 0x41, 0x37, 0xea, 0x8c, 0x28, 0xad, 0x7f, 0x15, + 0x5a, 0x51, 0xd0, 0x1d, 0x3d, 0x86, 0x43, 0x22, 0xe8, 0xce, 0xcf, 0x0c, 0x54, 0x79, 0xb6, 0xc0, + 0x88, 0xe8, 0x01, 0x81, 0xc5, 0xed, 0x3b, 0x4f, 0x0e, 0x06, 0x04, 0x2b, 0x04, 0xfa, 0xaf, 0x9c, + 0x67, 0x1d, 0xac, 0x0f, 0xa0, 0x19, 0x45, 0x1a, 0x93, 0x9d, 0xbd, 0xc8, 0x3d, 0x60, 0xb9, 0x30, + 0x4c, 0xce, 0xf1, 0xd4, 0xcf, 0x32, 0x47, 0xa9, 0x9f, 0x80, 0xca, 0x03, 0x72, 0x40, 0x27, 0x02, + 0xf7, 0x97, 0x62, 0x22, 0x70, 0xbf, 0xd8, 0x85, 0xba, 0x88, 0xf8, 0x27, 0xcb, 0xbb, 0x08, 0xf5, + 0x1d, 0xfe, 0x11, 0xa1, 0xc0, 0x33, 0x0a, 0x35, 0xfd, 0x0e, 0x4c, 0xcb, 0x71, 0xfe, 0x24, 0xdf, + 0x49, 0x98, 0x36, 0xa5, 0x2f, 0x09, 0x7c, 0x18, 0x64, 0x91, 0x4e, 0x54, 0xab, 0x4b, 0x31, 0xac, + 0x65, 0x9a, 0xdb, 0x9b, 0x99, 0xdd, 0x3e, 0xc2, 0xe8, 0x1e, 0xc0, 0xe1, 0x64, 0x40, 0x3f, 0x59, + 0xd2, 0x79, 0x38, 0xbc, 0x9d, 0xf8, 0x7c, 0xc0, 0x5d, 0x5d, 0x52, 0xac, 0x77, 0xa1, 0xc6, 0x03, + 0xae, 0x49, 0x8a, 0x4b, 0x50, 0x33, 0x58, 0x40, 0x97, 0x02, 0x67, 0xa5, 0x63, 0x9d, 0x5c, 0x4b, + 0x06, 0xc5, 0x5c, 0x51, 0xb7, 0x61, 0x46, 0x8d, 0xe1, 0x26, 0x29, 0xd7, 0x61, 0x66, 0x5f, 0x89, + 0x15, 0x73, 0xea, 0x85, 0x4c, 0x6a, 0x85, 0x0a, 0xab, 0x40, 0xfd, 0x17, 0xeb, 0x50, 0x65, 0x1f, + 0x21, 0x92, 0x45, 0x5c, 0x87, 0x6a, 0x40, 0x5e, 0x86, 0x5b, 0xd1, 0x85, 0x91, 0x5f, 0x34, 0xf8, + 0x49, 0x98, 0xe9, 0xa3, 0x2f, 0x42, 0xcd, 0x0f, 0x0e, 0xfa, 0xe1, 0xa7, 0xb3, 0x53, 0xa3, 0x81, + 0x5b, 0x54, 0x15, 0x73, 0x04, 0x85, 0xb2, 0xb9, 0x20, 0x3e, 0x9a, 0x15, 0x40, 0xd9, 0x24, 0xc4, + 0x1c, 0x81, 0xee, 0x40, 0xc3, 0xdc, 0x25, 0xe6, 0x73, 0x62, 0x89, 0xaf, 0x65, 0x67, 0x46, 0x83, + 0x57, 0xb8, 0x32, 0x0e, 0x51, 0xb4, 0x6c, 0x93, 0x8d, 0x6e, 0x7d, 0x9c, 0xb2, 0xd9, 0x88, 0x63, + 0x8e, 0x40, 0x6b, 0xd0, 0xb2, 0x4d, 0xd7, 0x59, 0xdb, 0x73, 0xbf, 0x61, 0x8b, 0xcf, 0x62, 0xe7, + 0x46, 0xc3, 0xbb, 0xa1, 0x3a, 0x8e, 0x91, 0x21, 0x4d, 0x77, 0x8f, 0x9e, 0x2c, 0x9b, 0xe3, 0xd2, + 0x30, 0x75, 0x1c, 0x23, 0xf5, 0x39, 0x31, 0x9e, 0xd9, 0x93, 0xfc, 0x1e, 0xd4, 0x58, 0x97, 0xa3, + 0xf7, 0xe4, 0xec, 0x59, 0xa9, 0xa4, 0x5c, 0x8f, 0x25, 0x86, 0x2a, 0xe2, 0x61, 0xfd, 0xaf, 0xf2, + 0x4c, 0x8f, 0xc3, 0x23, 0xc6, 0x8d, 0xf3, 0xbc, 0x01, 0x0d, 0x31, 0x14, 0x6a, 0x85, 0x9b, 0xa1, + 0xc2, 0xeb, 0x50, 0xe3, 0x13, 0x33, 0xbb, 0x3d, 0x6f, 0x42, 0x2b, 0xea, 0xcc, 0xd1, 0x2a, 0xac, + 0x77, 0x72, 0x54, 0x7e, 0xb5, 0x0c, 0x35, 0xfe, 0x31, 0x26, 0xed, 0x6a, 0xe5, 0x59, 0x70, 0x6a, + 0xf4, 0xb7, 0x1d, 0x79, 0x1a, 0xdc, 0x63, 0xe7, 0x31, 0xba, 0xff, 0x8e, 0xee, 0x26, 0x9d, 0x2f, + 0x40, 0x6f, 0x86, 0xfa, 0x38, 0x86, 0x16, 0x0c, 0xe7, 0x63, 0x68, 0x45, 0x28, 0xb4, 0xac, 0x0e, + 0xe9, 0x85, 0x91, 0x43, 0x91, 0x2c, 0x52, 0x10, 0x7e, 0xb7, 0x04, 0x95, 0x55, 0x7b, 0x3f, 0xd5, + 0x0f, 0x37, 0xc2, 0x59, 0x5d, 0xe4, 0x0e, 0x56, 0xed, 0x7d, 0x65, 0x52, 0xeb, 0x6b, 0xa1, 0xc5, + 0xdd, 0x52, 0xab, 0x77, 0x76, 0xf4, 0x46, 0x2b, 0xa6, 0xe1, 0x15, 0xfb, 0x8d, 0x06, 0x54, 0xd9, + 0x77, 0xce, 0x2c, 0x3f, 0x75, 0x30, 0x28, 0xae, 0x18, 0x8b, 0xa4, 0xb0, 0x05, 0x97, 0xe9, 0x73, + 0x3f, 0x65, 0x04, 0xc5, 0x7e, 0x8a, 0x07, 0x86, 0xa8, 0x2a, 0xe6, 0x08, 0x5a, 0xe4, 0x9e, 0xbd, + 0x47, 0x84, 0x9b, 0x2a, 0x28, 0xf2, 0x91, 0xbd, 0x47, 0x30, 0xd3, 0xa7, 0xb8, 0x5d, 0xc3, 0xdf, + 0x15, 0x1e, 0xaa, 0x00, 0xb7, 0x6e, 0xf8, 0xbb, 0x98, 0xe9, 0x53, 0x9c, 0x43, 0x4f, 0x7e, 0xf5, + 0x71, 0x70, 0xf4, 0x40, 0x88, 0x99, 0x3e, 0xc5, 0xf9, 0xf6, 0xb7, 0x88, 0xf0, 0x49, 0x05, 0xb8, + 0x2d, 0xfb, 0x5b, 0x04, 0x33, 0xfd, 0xd8, 0x85, 0x37, 0xc7, 0xeb, 0x1a, 0xc9, 0x85, 0x3f, 0x81, + 0xd9, 0x40, 0x89, 0xd6, 0x8b, 0x8f, 0xed, 0x17, 0x0a, 0xc6, 0x45, 0xc1, 0xe0, 0x04, 0x07, 0x9d, + 0x04, 0xec, 0x9c, 0x9b, 0x3d, 0x09, 0x5e, 0x87, 0xda, 0x97, 0x6d, 0x2b, 0xd8, 0x55, 0xb3, 0x6b, + 0x8a, 0xcb, 0xa3, 0xc3, 0x36, 0x91, 0xcb, 0x93, 0x47, 0x9d, 0xf3, 0xac, 0x42, 0x95, 0x9a, 0xcf, + 0x64, 0x76, 0x1c, 0x5b, 0xdd, 0xa7, 0x72, 0xc0, 0x72, 0x47, 0x73, 0x9e, 0x39, 0xa8, 0x52, 0x0b, + 0xc9, 0xe9, 0x92, 0x39, 0xa8, 0x52, 0xbb, 0xcb, 0xcf, 0xa5, 0xa3, 0xad, 0xe6, 0x56, 0xc2, 0xdc, + 0xb3, 0x30, 0xab, 0x0e, 0x47, 0x0e, 0xcb, 0x9f, 0x35, 0xa0, 0xca, 0x2e, 0x0d, 0x24, 0x67, 0xe4, + 0xcf, 0xc0, 0x0c, 0x1f, 0xbf, 0x65, 0xb1, 0x05, 0x2f, 0x67, 0xde, 0x19, 0x52, 0xaf, 0x22, 0x08, + 0x13, 0x10, 0x10, 0xac, 0x32, 0x8c, 0xbf, 0xa9, 0x60, 0x54, 0x8a, 0x45, 0xde, 0x8a, 0x36, 0xaf, + 0xd5, 0x82, 0x1b, 0x2b, 0x0c, 0xcb, 0xb7, 0xc0, 0xe1, 0x4e, 0x16, 0x2d, 0x43, 0x93, 0x2e, 0xad, + 0xb4, 0xbb, 0xc4, 0xb4, 0x3d, 0x3b, 0x1a, 0xdf, 0x15, 0xda, 0x38, 0xc2, 0xd1, 0x85, 0xdd, 0x34, + 0x3c, 0x8b, 0xd5, 0x4a, 0xcc, 0xe1, 0x73, 0xa3, 0x49, 0x56, 0x42, 0x75, 0x1c, 0x23, 0xd1, 0x03, + 0x98, 0xb6, 0x48, 0x14, 0x0e, 0x10, 0x93, 0xfa, 0x0b, 0xa3, 0x89, 0x56, 0x63, 0x00, 0x96, 0xd1, + 0xb4, 0x4e, 0xe1, 0x11, 0xd0, 0x2f, 0xdc, 0x6c, 0x30, 0xaa, 0xf8, 0x62, 0x5f, 0x8c, 0xd4, 0xcf, + 0xc0, 0x8c, 0x32, 0x6e, 0x9f, 0xe9, 0xae, 0x43, 0x1e, 0x4b, 0xce, 0xb3, 0x18, 0x1d, 0x51, 0xde, + 0x51, 0xb7, 0x1d, 0xb9, 0x27, 0x12, 0x01, 0x7c, 0x08, 0xcd, 0x70, 0x60, 0xd0, 0x5d, 0xb5, 0x0e, + 0x6f, 0x15, 0xd7, 0x21, 0x1a, 0x53, 0xc1, 0xb6, 0x01, 0xad, 0x68, 0x84, 0xd0, 0x92, 0x4a, 0xf7, + 0x76, 0x31, 0x5d, 0x3c, 0xba, 0x82, 0x0f, 0xc3, 0xb4, 0x34, 0x50, 0x68, 0x45, 0x65, 0x7c, 0xa7, + 0x98, 0x51, 0x1e, 0xe6, 0x78, 0xd7, 0x13, 0x8d, 0x98, 0x3c, 0x2a, 0x95, 0x78, 0x54, 0xfe, 0xa4, + 0x01, 0xcd, 0xe8, 0xa2, 0x4e, 0xc6, 0x19, 0x73, 0xe8, 0xf5, 0x0b, 0xcf, 0x98, 0x21, 0xbe, 0xf3, + 0xd4, 0xeb, 0x63, 0x8a, 0xa0, 0x43, 0x1c, 0xd8, 0x41, 0x34, 0x55, 0xcf, 0x15, 0x43, 0x9f, 0x50, + 0x75, 0xcc, 0x51, 0xe8, 0xb1, 0x6a, 0xe5, 0xd5, 0x11, 0x1f, 0x72, 0x15, 0x92, 0x5c, 0x4b, 0xef, + 0x42, 0xcb, 0xa6, 0x5b, 0xbf, 0xf5, 0x78, 0xe5, 0x7d, 0xbb, 0x98, 0xae, 0x1b, 0x42, 0x70, 0x8c, + 0xa6, 0x75, 0xdb, 0x31, 0xf6, 0xe9, 0xbc, 0x66, 0x64, 0xf5, 0x71, 0xeb, 0x76, 0x2f, 0x06, 0x61, + 0x99, 0x01, 0xdd, 0x14, 0x7b, 0x97, 0x46, 0x81, 0x67, 0x89, 0xbb, 0x2a, 0xde, 0xbf, 0x7c, 0x90, + 0x5a, 0x69, 0xf9, 0x34, 0xbe, 0x34, 0x06, 0xcb, 0xc8, 0xd5, 0x96, 0x8e, 0x20, 0xdf, 0x19, 0xb5, + 0xc6, 0x1d, 0x41, 0x79, 0x77, 0xa4, 0x9f, 0x80, 0xca, 0x53, 0xaf, 0x9f, 0xbf, 0x56, 0xb3, 0xe1, + 0xce, 0xc9, 0x3e, 0xa5, 0xce, 0x84, 0xfc, 0x0d, 0x7d, 0x34, 0x26, 0xb9, 0x3c, 0x52, 0xa7, 0xe7, + 0x28, 0xbd, 0x27, 0x16, 0xf4, 0x6b, 0xea, 0x7c, 0x7b, 0x23, 0x31, 0xdf, 0xe8, 0x0c, 0xdb, 0xf4, + 0x08, 0xbf, 0xab, 0x20, 0xad, 0xe4, 0xe3, 0xae, 0x93, 0xf7, 0xc3, 0xfd, 0xc7, 0x44, 0x9e, 0x22, + 0xd9, 0xb7, 0x9c, 0xeb, 0x97, 0x4b, 0xd0, 0x8c, 0xee, 0x61, 0xa5, 0x83, 0xf0, 0x4d, 0xdb, 0x5f, + 0x27, 0x86, 0x45, 0x3c, 0x31, 0x6f, 0xdf, 0x2a, 0xbc, 0xe0, 0xd5, 0xe9, 0x0a, 0x04, 0x8e, 0xb0, + 0xfa, 0x49, 0x68, 0x86, 0xd2, 0x9c, 0x43, 0xd9, 0x8f, 0xca, 0x50, 0x17, 0x37, 0xb8, 0x92, 0x95, + 0xb8, 0x0d, 0xf5, 0xbe, 0x71, 0xe0, 0x0e, 0xc3, 0x23, 0xd3, 0xd9, 0x82, 0x4b, 0x61, 0x9d, 0x87, + 0x4c, 0x1b, 0x0b, 0x14, 0xfa, 0x12, 0xd4, 0xfa, 0xf6, 0x9e, 0x1d, 0x08, 0xf7, 0x71, 0xa6, 0x10, + 0xce, 0xbe, 0xf5, 0x72, 0x0c, 0x2d, 0x9c, 0x5d, 0xdc, 0x08, 0xaf, 0xdd, 0x16, 0x16, 0xfe, 0x8c, + 0x69, 0x63, 0x81, 0xd2, 0xef, 0x43, 0x9d, 0x57, 0x67, 0xb2, 0x45, 0x42, 0x6d, 0x49, 0x6c, 0xe9, + 0xac, 0x6e, 0x39, 0xbb, 0xd2, 0x79, 0xa8, 0xf3, 0xc2, 0x73, 0xac, 0xe6, 0x87, 0xaf, 0xb1, 0xf3, + 0x4e, 0x5f, 0x7f, 0x18, 0x7f, 0xe3, 0xfb, 0xf4, 0x9f, 0x2c, 0xf4, 0x27, 0x70, 0x78, 0xd5, 0x08, + 0x8c, 0x6d, 0xc3, 0x27, 0x98, 0x98, 0xae, 0x67, 0x65, 0xb2, 0x7a, 0x3c, 0x4b, 0x04, 0xa2, 0xf3, + 0x59, 0x85, 0xde, 0xe7, 0xa1, 0xc3, 0xff, 0x3d, 0xa1, 0xc3, 0x3f, 0xad, 0xe6, 0xc4, 0xf3, 0xc6, + 0x89, 0x64, 0x50, 0x83, 0x4b, 0x05, 0xf4, 0x6e, 0xaa, 0x7b, 0xef, 0xd3, 0x05, 0x48, 0x65, 0xf3, + 0x7d, 0x53, 0x8d, 0xe8, 0x15, 0x61, 0x95, 0x90, 0xde, 0xdd, 0x64, 0x48, 0xef, 0x6c, 0x01, 0x3a, + 0x15, 0xd3, 0xbb, 0xa9, 0xc6, 0xf4, 0x8a, 0x4a, 0x97, 0x83, 0x7a, 0xff, 0xcf, 0xc2, 0x68, 0xbf, + 0x95, 0x13, 0xf6, 0xf9, 0xa2, 0x1a, 0xf6, 0x19, 0x61, 0x35, 0x3f, 0xad, 0xb8, 0xcf, 0x6f, 0xd7, + 0x73, 0xe2, 0x3e, 0x8b, 0x4a, 0xdc, 0x67, 0x44, 0xcd, 0x92, 0x81, 0x9f, 0x9b, 0x6a, 0xe0, 0xe7, + 0x74, 0x01, 0x52, 0x89, 0xfc, 0x2c, 0x2a, 0x91, 0x9f, 0xa2, 0x42, 0xa5, 0xd0, 0xcf, 0xa2, 0x12, + 0xfa, 0x29, 0x02, 0x4a, 0xb1, 0x9f, 0x45, 0x25, 0xf6, 0x53, 0x04, 0x94, 0x82, 0x3f, 0x8b, 0x4a, + 0xf0, 0xa7, 0x08, 0x28, 0x45, 0x7f, 0x6e, 0xaa, 0xd1, 0x9f, 0xe2, 0xfe, 0x91, 0x06, 0xfd, 0xf3, + 0x40, 0xcd, 0x7f, 0x63, 0xa0, 0xe6, 0xd7, 0x2b, 0x39, 0x01, 0x18, 0x9c, 0x1d, 0x80, 0xb9, 0x90, + 0x3f, 0x92, 0xc5, 0x11, 0x98, 0xf1, 0x57, 0x81, 0x74, 0x08, 0xe6, 0xbd, 0x44, 0x08, 0xe6, 0x4c, + 0x01, 0x58, 0x8d, 0xc1, 0xfc, 0x9f, 0x09, 0x32, 0xfc, 0x51, 0x7d, 0xc4, 0x79, 0xfa, 0x86, 0x7c, + 0x9e, 0x1e, 0xb1, 0x92, 0xa5, 0x0f, 0xd4, 0xb7, 0xd5, 0x03, 0xf5, 0xf9, 0x31, 0xb0, 0xca, 0x89, + 0x7a, 0x33, 0xeb, 0x44, 0xdd, 0x19, 0x83, 0x25, 0xf7, 0x48, 0x7d, 0x3f, 0x7d, 0xa4, 0xbe, 0x30, + 0x06, 0x5f, 0xe6, 0x99, 0x7a, 0x33, 0xeb, 0x4c, 0x3d, 0x4e, 0xed, 0x72, 0x0f, 0xd5, 0x5f, 0x52, + 0x0e, 0xd5, 0xe7, 0xc6, 0xe9, 0xae, 0x78, 0x71, 0xf8, 0x4a, 0xce, 0xa9, 0xfa, 0xdd, 0x71, 0x68, + 0x46, 0x07, 0xb1, 0x3f, 0x3f, 0x17, 0xab, 0xc5, 0xfc, 0xe1, 0x1b, 0xd0, 0x0c, 0xef, 0xd3, 0xe8, + 0xdf, 0x84, 0x46, 0xf8, 0x6c, 0x27, 0x39, 0x73, 0x8e, 0x47, 0x87, 0x3a, 0xbe, 0x7b, 0x16, 0x29, + 0x74, 0x1b, 0xaa, 0xf4, 0x3f, 0x31, 0x2d, 0xde, 0x1a, 0xef, 0xde, 0x0e, 0x2d, 0x04, 0x33, 0x9c, + 0xfe, 0xe3, 0x63, 0x00, 0xd2, 0x6b, 0x86, 0x71, 0x8b, 0x7d, 0x9f, 0x3a, 0xb3, 0x7e, 0x40, 0x3c, + 0x76, 0x5f, 0xab, 0xf0, 0xb6, 0x7f, 0x5c, 0x02, 0xb5, 0x96, 0x80, 0x78, 0x58, 0xc0, 0xd1, 0x23, + 0x68, 0x86, 0x81, 0x54, 0xad, 0xca, 0xa8, 0xde, 0x1d, 0x9b, 0x2a, 0x0c, 0xed, 0xe1, 0x88, 0x02, + 0x2d, 0x41, 0xd5, 0x77, 0xbd, 0x40, 0xab, 0x31, 0xaa, 0x77, 0xc6, 0xa6, 0xda, 0x72, 0xbd, 0x00, + 0x33, 0x28, 0x6f, 0x9a, 0xf4, 0x58, 0x74, 0x92, 0xa6, 0x29, 0x1e, 0xfb, 0xdf, 0x2a, 0x91, 0x0f, + 0x5d, 0x11, 0xb3, 0x91, 0xdb, 0xd0, 0xc5, 0xf1, 0x47, 0x49, 0x9e, 0x95, 0x48, 0x6c, 0x82, 0xf8, + 0x48, 0xf0, 0xfd, 0xcd, 0x5b, 0xd0, 0x36, 0xdd, 0x7d, 0xe2, 0xe1, 0xf8, 0x26, 0x93, 0xb8, 0x6c, + 0x96, 0x92, 0x23, 0x1d, 0x9a, 0xbb, 0xb6, 0x45, 0xba, 0xa6, 0xf0, 0x7f, 0x4d, 0x1c, 0xa5, 0xd1, + 0x03, 0x68, 0xb2, 0x18, 0x7b, 0x18, 0xe1, 0x9f, 0xac, 0x92, 0x3c, 0xd4, 0x1f, 0x12, 0xd0, 0x82, + 0x58, 0xe1, 0xf7, 0xec, 0x80, 0xf5, 0x61, 0x13, 0x47, 0x69, 0x5a, 0x61, 0x76, 0x5d, 0x4c, 0xae, + 0x70, 0x83, 0x57, 0x38, 0x29, 0x47, 0x57, 0xe1, 0x15, 0x26, 0x4b, 0x1c, 0x31, 0x79, 0xa8, 0xbe, + 0x89, 0xb3, 0x33, 0xd9, 0xf5, 0x38, 0xa3, 0xc7, 0xaf, 0x86, 0xb3, 0xe0, 0x5d, 0x0d, 0xc7, 0x02, + 0x74, 0x01, 0x8e, 0x58, 0x64, 0xc7, 0x18, 0xf6, 0x83, 0x27, 0x64, 0x6f, 0xd0, 0x37, 0x02, 0xd2, + 0xb5, 0xd8, 0x7b, 0xd5, 0x16, 0x4e, 0x67, 0xa0, 0x4b, 0x70, 0x54, 0x08, 0xf9, 0x34, 0xa6, 0xa3, + 0xd1, 0xb5, 0xd8, 0xf3, 0xcd, 0x16, 0xce, 0xca, 0xd2, 0x7f, 0x58, 0xa5, 0x83, 0xce, 0x4c, 0xfb, + 0x7d, 0xa8, 0x18, 0x96, 0x25, 0x96, 0xcd, 0x2b, 0x13, 0x4e, 0x10, 0xf1, 0x24, 0x9b, 0x32, 0xa0, + 0xcd, 0xe8, 0x66, 0x1d, 0x5f, 0x38, 0xaf, 0x4f, 0xca, 0x15, 0x3d, 0xa3, 0x17, 0x3c, 0x94, 0x71, + 0xc8, 0xef, 0x76, 0x57, 0x7e, 0x32, 0xc6, 0xe8, 0xca, 0xb7, 0xe0, 0x41, 0xf7, 0xa1, 0xca, 0x6a, + 0xc8, 0x17, 0xd6, 0xab, 0x93, 0xf2, 0x3d, 0xe2, 0xf5, 0x63, 0x1c, 0xba, 0xc9, 0xef, 0xbe, 0x49, + 0xf7, 0x2a, 0x4b, 0xea, 0xbd, 0xca, 0x65, 0xa8, 0xd9, 0x01, 0xd9, 0x4b, 0x5f, 0xb3, 0x1d, 0x69, + 0xaa, 0xc2, 0xf3, 0x70, 0xe8, 0xc8, 0xeb, 0x7e, 0x1f, 0x46, 0x57, 0xae, 0x93, 0xfe, 0xf0, 0x2e, + 0x54, 0x29, 0x3c, 0xb5, 0x97, 0x1c, 0xa7, 0x60, 0x86, 0xd4, 0x2f, 0x43, 0x95, 0x36, 0x76, 0x44, + 0xeb, 0x44, 0x7d, 0xca, 0x51, 0x7d, 0x96, 0xa7, 0xa1, 0xe5, 0x0e, 0x88, 0xc7, 0x26, 0x86, 0xfe, + 0xcf, 0x55, 0xe9, 0x52, 0x5c, 0x57, 0xb6, 0xb1, 0x6b, 0x13, 0x7b, 0x4e, 0xd9, 0xca, 0x70, 0xc2, + 0xca, 0x6e, 0x4c, 0xce, 0x96, 0xb2, 0x33, 0x9c, 0xb0, 0xb3, 0x9f, 0x80, 0x33, 0x65, 0x69, 0x0f, + 0x15, 0x4b, 0xbb, 0x3e, 0x39, 0xa3, 0x62, 0x6b, 0xa4, 0xc8, 0xd6, 0x56, 0x55, 0x5b, 0xeb, 0x8c, + 0x37, 0xe4, 0xd1, 0xd2, 0x34, 0x86, 0xb5, 0x7d, 0x35, 0xd7, 0xda, 0x96, 0x15, 0x6b, 0x9b, 0xb4, + 0xe8, 0xcf, 0xc8, 0xde, 0xfe, 0xbe, 0x0a, 0x55, 0xba, 0x3c, 0xa2, 0x35, 0xd9, 0xd6, 0xde, 0x9d, + 0x68, 0x69, 0x95, 0xed, 0x6c, 0x23, 0x61, 0x67, 0x57, 0x27, 0x63, 0x4a, 0xd9, 0xd8, 0x46, 0xc2, + 0xc6, 0x26, 0xe4, 0x4b, 0xd9, 0xd7, 0xba, 0x62, 0x5f, 0x97, 0x27, 0x63, 0x53, 0x6c, 0xcb, 0x28, + 0xb2, 0xad, 0xbb, 0xaa, 0x6d, 0x8d, 0xb9, 0x7b, 0x63, 0x7b, 0x95, 0x31, 0xec, 0xea, 0x83, 0x5c, + 0xbb, 0xba, 0xad, 0xd8, 0xd5, 0x24, 0xc5, 0x7e, 0x46, 0x36, 0x75, 0x95, 0x6f, 0x3a, 0xc5, 0x3d, + 0xe3, 0x31, 0x37, 0x9d, 0xfa, 0x35, 0x68, 0xc5, 0xcf, 0xc1, 0x33, 0x6e, 0xe1, 0x73, 0xb5, 0xb0, + 0xd4, 0x30, 0xa9, 0x5f, 0x81, 0x56, 0xfc, 0xc4, 0x3b, 0xa3, 0x2c, 0x9f, 0x65, 0x0a, 0x94, 0x48, + 0xe9, 0x6b, 0x70, 0x24, 0xfd, 0x00, 0x35, 0x23, 0x0e, 0x2f, 0x5d, 0x21, 0x0f, 0x5f, 0x9c, 0x48, + 0x22, 0xfd, 0x05, 0xcc, 0x26, 0x9e, 0x94, 0x4e, 0xcc, 0x81, 0xae, 0x48, 0x5b, 0xe4, 0x8a, 0x38, + 0x83, 0x67, 0x5f, 0x8a, 0x8f, 0x37, 0xc2, 0xfa, 0x2a, 0xcc, 0x16, 0x54, 0x7e, 0x9c, 0x3b, 0xf1, + 0x5f, 0x87, 0xe9, 0x51, 0x75, 0xff, 0x0c, 0xee, 0xec, 0x07, 0xd0, 0x4e, 0x3d, 0x87, 0x4f, 0x16, + 0xb3, 0x09, 0xd0, 0x8b, 0x74, 0x84, 0xd1, 0x5e, 0x9a, 0xe0, 0x85, 0x02, 0xc3, 0x61, 0x89, 0x43, + 0xff, 0xfd, 0x12, 0x1c, 0x49, 0xbf, 0x85, 0x1f, 0xf7, 0xf0, 0xa3, 0x41, 0x83, 0x71, 0x45, 0x0f, + 0x3b, 0xc2, 0x24, 0x7a, 0x04, 0x87, 0xfc, 0xbe, 0x6d, 0x92, 0x95, 0x5d, 0xc3, 0xe9, 0x11, 0x5f, + 0x9c, 0x68, 0x0a, 0xde, 0xb3, 0x6f, 0xc5, 0x08, 0xac, 0xc0, 0xf5, 0x17, 0x30, 0x2d, 0x65, 0xa2, + 0x5b, 0x50, 0x76, 0x07, 0xa9, 0x7b, 0x8d, 0xf9, 0x9c, 0x8f, 0xc3, 0xf9, 0x86, 0xcb, 0xee, 0x20, + 0x3d, 0x25, 0xe5, 0xe9, 0x5b, 0x51, 0xa6, 0xaf, 0xfe, 0x00, 0x8e, 0xa4, 0x9f, 0x9b, 0x27, 0xbb, + 0xe7, 0x6c, 0x2a, 0x4a, 0xc0, 0xbb, 0x29, 0x79, 0xe4, 0x5f, 0x84, 0xc3, 0xc9, 0x47, 0xe4, 0x19, + 0x8f, 0x6e, 0xe2, 0xb7, 0x4b, 0x61, 0xb8, 0x7e, 0xe1, 0xd7, 0x4a, 0x30, 0xab, 0x36, 0x04, 0x1d, + 0x07, 0xa4, 0x4a, 0x36, 0x5c, 0x87, 0xb4, 0xa7, 0xd0, 0x2b, 0x70, 0x44, 0x95, 0x2f, 0x59, 0x56, + 0xbb, 0x94, 0x56, 0xa7, 0x6e, 0xab, 0x5d, 0x46, 0x1a, 0x1c, 0x4b, 0xf4, 0x10, 0x73, 0xa2, 0xed, + 0x0a, 0x7a, 0x0d, 0x5e, 0x49, 0xe6, 0x0c, 0xfa, 0x86, 0x49, 0xda, 0x55, 0xfd, 0x5f, 0xcb, 0x50, + 0x7d, 0xea, 0x13, 0x4f, 0xff, 0xa7, 0x72, 0xf8, 0x4a, 0xe3, 0x06, 0x54, 0xd9, 0xfb, 0x6e, 0xe9, + 0xd1, 0x62, 0x29, 0xf1, 0x68, 0x51, 0x79, 0xf8, 0x16, 0x3f, 0x5a, 0xbc, 0x01, 0x55, 0xf6, 0xa2, + 0x7b, 0x72, 0xe4, 0x2f, 0x95, 0xa0, 0x15, 0xbf, 0xae, 0x9e, 0x18, 0x2f, 0xbf, 0x0a, 0x29, 0xab, + 0xaf, 0x42, 0xde, 0x82, 0x9a, 0xc7, 0xde, 0x6f, 0x70, 0x2f, 0x93, 0x7c, 0x6b, 0xc2, 0x0a, 0xc4, + 0x5c, 0x45, 0x27, 0x30, 0x2d, 0xbf, 0x1d, 0x9f, 0xbc, 0x1a, 0xa7, 0xc5, 0x0f, 0xc7, 0x74, 0x2d, + 0x7f, 0xc9, 0xf3, 0x8c, 0x03, 0x61, 0x98, 0xaa, 0x50, 0x9f, 0x83, 0xea, 0xa6, 0xed, 0xf4, 0xb2, + 0xdf, 0x8a, 0xea, 0xdf, 0x2f, 0x41, 0x43, 0x5c, 0xde, 0xd5, 0x17, 0xa1, 0xb2, 0x41, 0x5e, 0xd0, + 0x8a, 0x88, 0x6b, 0xc3, 0xa9, 0x8a, 0x3c, 0x62, 0xad, 0x10, 0xfa, 0x38, 0x54, 0xd3, 0x6f, 0x46, + 0xcb, 0xe4, 0xe4, 0xd8, 0x1b, 0x50, 0x65, 0x4f, 0xbe, 0x27, 0x47, 0xfe, 0x6e, 0x13, 0xea, 0xfc, + 0xc1, 0xa5, 0xfe, 0xdd, 0x26, 0xd4, 0xf9, 0x33, 0x70, 0x74, 0x1b, 0x1a, 0xfe, 0x70, 0x6f, 0xcf, + 0xf0, 0x0e, 0xb4, 0xec, 0x9f, 0x0c, 0x54, 0x5e, 0x8d, 0x77, 0xb6, 0xb8, 0x2e, 0x0e, 0x41, 0xe8, + 0x1a, 0x54, 0x4d, 0x63, 0x87, 0xa4, 0x3e, 0xe7, 0x66, 0x81, 0x57, 0x8c, 0x1d, 0x82, 0x99, 0x3a, + 0xba, 0x0b, 0x4d, 0x31, 0x2c, 0xbe, 0x88, 0xe7, 0x8c, 0x2e, 0x37, 0x1c, 0xcc, 0x08, 0xa5, 0xdf, + 0x87, 0x86, 0xa8, 0x0c, 0xba, 0x13, 0x3d, 0x37, 0x4d, 0x46, 0x9e, 0x33, 0x9b, 0x70, 0xe0, 0x98, + 0x89, 0x87, 0xa7, 0x7f, 0x59, 0x86, 0x2a, 0xad, 0xdc, 0xa7, 0x66, 0x42, 0xf3, 0x00, 0x7d, 0xc3, + 0x0f, 0x36, 0x87, 0xfd, 0x3e, 0xb1, 0xc4, 0x43, 0x3a, 0x49, 0x82, 0xce, 0xc3, 0x61, 0x9e, 0xf2, + 0x77, 0xb7, 0x86, 0xa6, 0x49, 0xa2, 0xd7, 0xa0, 0x49, 0x31, 0x5a, 0x82, 0x1a, 0xfb, 0x61, 0x32, + 0xb1, 0x2b, 0x7c, 0xbb, 0xb0, 0x67, 0x3b, 0x9b, 0xb6, 0x23, 0x6a, 0xc3, 0x91, 0xba, 0x0b, 0xad, + 0x48, 0x46, 0x27, 0xe1, 0xc0, 0x76, 0x1c, 0xdb, 0xe9, 0x09, 0x8b, 0x0e, 0x93, 0x74, 0xd1, 0xa1, + 0xff, 0x8a, 0xfa, 0xd6, 0xb0, 0x48, 0x51, 0xf9, 0x8e, 0x61, 0xf7, 0x45, 0x15, 0x6b, 0x58, 0xa4, + 0x28, 0xd3, 0x50, 0x3c, 0x9e, 0xaf, 0xb2, 0x06, 0x86, 0x49, 0xfd, 0xe3, 0x52, 0xf4, 0xe6, 0x3a, + 0xeb, 0x0d, 0x66, 0x2a, 0x96, 0x34, 0x27, 0x07, 0xb4, 0xf9, 0x82, 0x20, 0x85, 0xa8, 0x8f, 0x43, + 0xdd, 0x75, 0xfa, 0xb6, 0x43, 0x44, 0xec, 0x48, 0xa4, 0x12, 0x7d, 0x5c, 0x4b, 0xf5, 0xb1, 0xc8, + 0x5f, 0xb3, 0x6c, 0x5a, 0xc5, 0x7a, 0x9c, 0xcf, 0x25, 0xe8, 0x3d, 0x68, 0x58, 0x64, 0xdf, 0x36, + 0x89, 0xaf, 0x35, 0x98, 0xe9, 0x9d, 0x1a, 0xd9, 0xb7, 0xab, 0x4c, 0x17, 0x87, 0x18, 0x3d, 0x80, + 0x3a, 0x17, 0x45, 0x4d, 0x2a, 0x49, 0x4d, 0x8a, 0x2b, 0x5d, 0x1e, 0x51, 0xe9, 0x4a, 0x41, 0xa5, + 0xab, 0xc9, 0x4a, 0x2f, 0x58, 0x00, 0xb1, 0xb9, 0xa1, 0x69, 0x68, 0x3c, 0x75, 0x9e, 0x3b, 0xee, + 0x0b, 0xa7, 0x3d, 0x45, 0x13, 0x8f, 0x77, 0x76, 0x68, 0x29, 0xed, 0x12, 0x4d, 0x50, 0x3d, 0xdb, + 0xe9, 0xb5, 0xcb, 0x08, 0xa0, 0x4e, 0x13, 0xc4, 0x6a, 0x57, 0xe8, 0xff, 0xf7, 0xd8, 0xf8, 0xb5, + 0xab, 0xe8, 0x55, 0x38, 0xda, 0x75, 0x4c, 0x77, 0x6f, 0x60, 0x04, 0xf6, 0x76, 0x9f, 0x3c, 0x23, + 0x9e, 0x6f, 0xbb, 0x4e, 0xbb, 0xa6, 0xff, 0x47, 0x89, 0x7f, 0xf5, 0xd5, 0xef, 0xc2, 0x21, 0xe5, + 0xd7, 0x1c, 0x34, 0x68, 0xf8, 0x03, 0xfe, 0xe3, 0xad, 0x62, 0xdf, 0x2d, 0x92, 0xcc, 0x4a, 0xf8, + 0xeb, 0x77, 0xb1, 0x65, 0xe1, 0x29, 0xfd, 0x1e, 0x80, 0xf4, 0x1b, 0x0e, 0xf3, 0x00, 0xdb, 0x07, + 0x01, 0xf1, 0xf9, 0xef, 0x37, 0x50, 0x8a, 0x2a, 0x96, 0x24, 0x32, 0x7f, 0x59, 0xe1, 0xd7, 0xaf, + 0x03, 0x48, 0xbf, 0xe0, 0x40, 0xe7, 0x0f, 0x4d, 0x2d, 0x27, 0xc9, 0x92, 0x62, 0xbd, 0x23, 0x5a, + 0x10, 0xfe, 0x56, 0x43, 0x58, 0x03, 0x1e, 0xa5, 0x93, 0x6b, 0xc0, 0x24, 0xfa, 0x1a, 0x40, 0xfc, + 0x73, 0x05, 0xfa, 0x62, 0xe4, 0xa2, 0xdf, 0x81, 0xaa, 0x65, 0x04, 0x86, 0xf0, 0x8e, 0xaf, 0x25, + 0x56, 0xa8, 0x18, 0x82, 0x99, 0x9a, 0xfe, 0x7b, 0x25, 0x38, 0x24, 0xff, 0x34, 0x83, 0xfe, 0x3e, + 0x54, 0xd9, 0x6f, 0x3b, 0xdc, 0x81, 0x43, 0xf2, 0x6f, 0x33, 0xa4, 0x7e, 0xd9, 0x96, 0xf3, 0xc9, + 0x50, 0xac, 0x00, 0xf4, 0x6e, 0x54, 0xa5, 0x4f, 0x4d, 0x75, 0x09, 0x1a, 0xe2, 0xa7, 0x1e, 0xf4, + 0x33, 0xd0, 0x8a, 0x7f, 0xd9, 0x81, 0xfa, 0x08, 0x2e, 0x0f, 0x47, 0x59, 0x24, 0xe9, 0xf6, 0xb5, + 0x15, 0xfd, 0xfc, 0x82, 0xfe, 0x51, 0x54, 0x95, 0x7c, 0xbb, 0x58, 0x54, 0x7e, 0x03, 0x60, 0x76, + 0xc4, 0x8f, 0x39, 0xa8, 0x2e, 0x74, 0xe1, 0x66, 0xb8, 0x4c, 0xa1, 0x19, 0x68, 0xad, 0xb8, 0x8e, + 0x43, 0xcc, 0x80, 0x58, 0xed, 0x29, 0x74, 0x18, 0xa6, 0x37, 0xdc, 0x60, 0xd3, 0xf5, 0x7d, 0x6a, + 0xb4, 0xed, 0x12, 0x6a, 0xb3, 0xae, 0x8e, 0x55, 0xca, 0x0b, 0xdf, 0x86, 0x19, 0x4c, 0xfc, 0x81, + 0xeb, 0xf8, 0xe4, 0xa7, 0xf5, 0xe3, 0xc7, 0xb9, 0x3f, 0x63, 0xbc, 0xf0, 0xfd, 0x0a, 0xd4, 0xd8, + 0xda, 0xab, 0xff, 0x71, 0x25, 0xda, 0x25, 0x64, 0xdc, 0xcc, 0x8a, 0xef, 0x4f, 0xcc, 0x4a, 0x83, + 0xa6, 0xac, 0xda, 0x72, 0x10, 0xfe, 0xb2, 0x7c, 0x6f, 0x62, 0x56, 0xfa, 0x55, 0x17, 0x15, 0xa1, + 0xdc, 0x97, 0xf8, 0x12, 0x34, 0x07, 0x9e, 0xdb, 0xf3, 0xe8, 0xf6, 0xa0, 0x9a, 0xf8, 0x2d, 0x0d, + 0x15, 0xb6, 0x29, 0xd4, 0x70, 0x04, 0xd0, 0x37, 0xa0, 0x19, 0x4a, 0x73, 0x5e, 0x8b, 0x23, 0xa8, + 0x5a, 0xae, 0x70, 0x71, 0x15, 0xcc, 0xfe, 0xa7, 0xfd, 0x22, 0x7a, 0x30, 0xdc, 0xda, 0x8b, 0xe4, + 0xc2, 0xd7, 0xc4, 0x77, 0xad, 0x19, 0x68, 0xad, 0x7a, 0xee, 0x80, 0xbd, 0x17, 0x6e, 0x4f, 0x51, + 0x87, 0xd4, 0xdd, 0x1b, 0xb8, 0x5e, 0xd0, 0x2e, 0xd1, 0xff, 0xd7, 0x5e, 0xb2, 0xff, 0xcb, 0xe8, + 0x10, 0x34, 0xb7, 0x8c, 0x7d, 0x42, 0xd5, 0xda, 0x15, 0x84, 0xe8, 0xa9, 0x92, 0xc5, 0xf2, 0xc5, + 0xc2, 0xd2, 0xae, 0x52, 0xa2, 0x47, 0x76, 0x8f, 0x6f, 0x96, 0xdb, 0xb5, 0x85, 0xa5, 0xf0, 0xfe, + 0x42, 0x13, 0xaa, 0x62, 0x73, 0x3e, 0x0d, 0x0d, 0x3c, 0x64, 0xab, 0x5b, 0xbb, 0x44, 0xc5, 0x74, + 0xcb, 0xc4, 0xa9, 0x57, 0x0c, 0xc7, 0x24, 0x7d, 0xe6, 0x11, 0x5b, 0x50, 0x5b, 0xf3, 0x3c, 0xd7, + 0x6b, 0x57, 0x97, 0xe7, 0xfe, 0xea, 0xe3, 0xf9, 0xd2, 0x0f, 0x3e, 0x9e, 0x2f, 0xfd, 0xe8, 0xe3, + 0xf9, 0xd2, 0x6f, 0x7e, 0x32, 0x3f, 0xf5, 0x83, 0x4f, 0xe6, 0xa7, 0xfe, 0xf1, 0x93, 0xf9, 0xa9, + 0x0f, 0xcb, 0x83, 0xed, 0xed, 0x3a, 0xfb, 0xf0, 0x7c, 0xe5, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, + 0x18, 0xd9, 0x91, 0xb5, 0xba, 0x5b, 0x00, 0x00, } func (m *Event) Marshal() (dAtA []byte, err error) { @@ -20674,7 +20682,14 @@ func (m *EventP2PStatusUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.Status != 0 { i = encodeVarintEvents(dAtA, i, uint64(m.Status)) i-- - dAtA[i] = 0x8 + dAtA[i] = 0x10 + } + if len(m.SpaceId) > 0 { + i -= len(m.SpaceId) + copy(dAtA[i:], m.SpaceId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.SpaceId))) + i-- + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -24847,6 +24862,10 @@ func (m *EventP2PStatusUpdate) Size() (n int) { } var l int _ = l + l = len(m.SpaceId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } if m.Status != 0 { n += 1 + sovEvents(uint64(m.Status)) } @@ -47374,6 +47393,38 @@ func (m *EventP2PStatusUpdate) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SpaceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SpaceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } diff --git a/pb/protos/events.proto b/pb/protos/events.proto index 7b6005a80..23bfebdea 100644 --- a/pb/protos/events.proto +++ b/pb/protos/events.proto @@ -1066,12 +1066,13 @@ message Event { message P2PStatus { message Update { - Status status = 1; + string spaceId = 1; + Status status = 2; } enum Status { - Possible = 0; - Connected = 1; + Connected = 0; + NotPossible = 1; NotConnected = 2; } } diff --git a/space/spacecore/clientserver/mock_clientserver/mock_ClientServer.go b/space/spacecore/clientserver/mock_clientserver/mock_ClientServer.go new file mode 100644 index 000000000..74f3b3fbf --- /dev/null +++ b/space/spacecore/clientserver/mock_clientserver/mock_ClientServer.go @@ -0,0 +1,311 @@ +// Code generated by mockery. DO NOT EDIT. + +package mock_clientserver + +import ( + app "github.com/anyproto/any-sync/app" + + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// MockClientServer is an autogenerated mock type for the ClientServer type +type MockClientServer struct { + mock.Mock +} + +type MockClientServer_Expecter struct { + mock *mock.Mock +} + +func (_m *MockClientServer) EXPECT() *MockClientServer_Expecter { + return &MockClientServer_Expecter{mock: &_m.Mock} +} + +// Close provides a mock function with given fields: ctx +func (_m *MockClientServer) Close(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClientServer_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type MockClientServer_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockClientServer_Expecter) Close(ctx interface{}) *MockClientServer_Close_Call { + return &MockClientServer_Close_Call{Call: _e.mock.On("Close", ctx)} +} + +func (_c *MockClientServer_Close_Call) Run(run func(ctx context.Context)) *MockClientServer_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockClientServer_Close_Call) Return(err error) *MockClientServer_Close_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClientServer_Close_Call) RunAndReturn(run func(context.Context) error) *MockClientServer_Close_Call { + _c.Call.Return(run) + return _c +} + +// Init provides a mock function with given fields: a +func (_m *MockClientServer) Init(a *app.App) error { + ret := _m.Called(a) + + if len(ret) == 0 { + panic("no return value specified for Init") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*app.App) error); ok { + r0 = rf(a) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClientServer_Init_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Init' +type MockClientServer_Init_Call struct { + *mock.Call +} + +// Init is a helper method to define mock.On call +// - a *app.App +func (_e *MockClientServer_Expecter) Init(a interface{}) *MockClientServer_Init_Call { + return &MockClientServer_Init_Call{Call: _e.mock.On("Init", a)} +} + +func (_c *MockClientServer_Init_Call) Run(run func(a *app.App)) *MockClientServer_Init_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*app.App)) + }) + return _c +} + +func (_c *MockClientServer_Init_Call) Return(err error) *MockClientServer_Init_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClientServer_Init_Call) RunAndReturn(run func(*app.App) error) *MockClientServer_Init_Call { + _c.Call.Return(run) + return _c +} + +// Name provides a mock function with given fields: +func (_m *MockClientServer) Name() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Name") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockClientServer_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name' +type MockClientServer_Name_Call struct { + *mock.Call +} + +// Name is a helper method to define mock.On call +func (_e *MockClientServer_Expecter) Name() *MockClientServer_Name_Call { + return &MockClientServer_Name_Call{Call: _e.mock.On("Name")} +} + +func (_c *MockClientServer_Name_Call) Run(run func()) *MockClientServer_Name_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockClientServer_Name_Call) Return(name string) *MockClientServer_Name_Call { + _c.Call.Return(name) + return _c +} + +func (_c *MockClientServer_Name_Call) RunAndReturn(run func() string) *MockClientServer_Name_Call { + _c.Call.Return(run) + return _c +} + +// Port provides a mock function with given fields: +func (_m *MockClientServer) Port() int { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Port") + } + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// MockClientServer_Port_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Port' +type MockClientServer_Port_Call struct { + *mock.Call +} + +// Port is a helper method to define mock.On call +func (_e *MockClientServer_Expecter) Port() *MockClientServer_Port_Call { + return &MockClientServer_Port_Call{Call: _e.mock.On("Port")} +} + +func (_c *MockClientServer_Port_Call) Run(run func()) *MockClientServer_Port_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockClientServer_Port_Call) Return(_a0 int) *MockClientServer_Port_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClientServer_Port_Call) RunAndReturn(run func() int) *MockClientServer_Port_Call { + _c.Call.Return(run) + return _c +} + +// Run provides a mock function with given fields: ctx +func (_m *MockClientServer) Run(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Run") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClientServer_Run_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Run' +type MockClientServer_Run_Call struct { + *mock.Call +} + +// Run is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockClientServer_Expecter) Run(ctx interface{}) *MockClientServer_Run_Call { + return &MockClientServer_Run_Call{Call: _e.mock.On("Run", ctx)} +} + +func (_c *MockClientServer_Run_Call) Run(run func(ctx context.Context)) *MockClientServer_Run_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockClientServer_Run_Call) Return(err error) *MockClientServer_Run_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClientServer_Run_Call) RunAndReturn(run func(context.Context) error) *MockClientServer_Run_Call { + _c.Call.Return(run) + return _c +} + +// ServerStarted provides a mock function with given fields: +func (_m *MockClientServer) ServerStarted() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ServerStarted") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// MockClientServer_ServerStarted_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ServerStarted' +type MockClientServer_ServerStarted_Call struct { + *mock.Call +} + +// ServerStarted is a helper method to define mock.On call +func (_e *MockClientServer_Expecter) ServerStarted() *MockClientServer_ServerStarted_Call { + return &MockClientServer_ServerStarted_Call{Call: _e.mock.On("ServerStarted")} +} + +func (_c *MockClientServer_ServerStarted_Call) Run(run func()) *MockClientServer_ServerStarted_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockClientServer_ServerStarted_Call) Return(_a0 bool) *MockClientServer_ServerStarted_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClientServer_ServerStarted_Call) RunAndReturn(run func() bool) *MockClientServer_ServerStarted_Call { + _c.Call.Return(run) + return _c +} + +// NewMockClientServer creates a new instance of MockClientServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockClientServer(t interface { + mock.TestingT + Cleanup(func()) +}) *MockClientServer { + mock := &MockClientServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/space/spacecore/localdiscovery/localdiscovery.go b/space/spacecore/localdiscovery/localdiscovery.go index 5190dfef5..56551b16b 100644 --- a/space/spacecore/localdiscovery/localdiscovery.go +++ b/space/spacecore/localdiscovery/localdiscovery.go @@ -13,6 +13,7 @@ import ( "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/peerstatus" "github.com/anyproto/any-sync/nodeconf" "github.com/anyproto/any-sync/util/periodicsync" "github.com/libp2p/zeroconf/v2" @@ -21,12 +22,17 @@ import ( "github.com/anyproto/anytype-heart/core/anytype/config" "github.com/anyproto/anytype-heart/core/event" "github.com/anyproto/anytype-heart/net/addrs" - "github.com/anyproto/anytype-heart/pb" "github.com/anyproto/anytype-heart/space/spacecore/clientserver" ) var interfacesSortPriority = []string{"en", "wlan", "wl", "eth", "lo"} +type StatusUpdater interface { + BroadcastStatus(status peerstatus.Status) + SendPeerUpdate(spaceIds []string) + BroadcastPeerUpdate() +} + type localDiscovery struct { server *zeroconf.Server peerId string @@ -40,13 +46,14 @@ type localDiscovery struct { drpcServer clientserver.ClientServer nodeConf nodeconf.Configuration - ipv4 []string - ipv6 []string - manualStart bool - started bool - notifier Notifier - m sync.Mutex - eventSender event.Sender + ipv4 []string + ipv6 []string + manualStart bool + started bool + notifier Notifier + m sync.Mutex + eventSender event.Sender + peerToPeerStatusUpdater StatusUpdater } func New() LocalDiscovery { @@ -62,15 +69,15 @@ func (l *localDiscovery) Init(a *app.App) (err error) { l.nodeConf = a.MustComponent(config.CName).(*config.Config).GetNodeConf() l.peerId = a.MustComponent(accountservice.CName).(accountservice.Service).Account().PeerId l.periodicCheck = periodicsync.NewPeriodicSync(30, 0, l.checkAddrs, log) - l.drpcServer = a.MustComponent(clientserver.CName).(clientserver.ClientServer) + l.drpcServer = app.MustComponent[clientserver.ClientServer](a) l.eventSender = app.MustComponent[event.Sender](a) + l.peerToPeerStatusUpdater = app.MustComponent[StatusUpdater](a) return } func (l *localDiscovery) Run(ctx context.Context) (err error) { if l.manualStart && len(l.nodeConf.Nodes) > 0 { // let's wait for the explicit command to enable local discovery - l.sendP2PStatusEvent(pb.EventP2PStatus_NotConnected) return } @@ -79,6 +86,7 @@ func (l *localDiscovery) Run(ctx context.Context) (err error) { func (l *localDiscovery) Start() (err error) { if !l.drpcServer.ServerStarted() { + l.peerToPeerStatusUpdater.BroadcastStatus(peerstatus.NotPossible) return } l.m.Lock() @@ -136,6 +144,9 @@ func (l *localDiscovery) Close(ctx context.Context) (err error) { func (l *localDiscovery) checkAddrs(ctx context.Context) (err error) { newAddrs, err := addrs.GetInterfacesAddrs() + if len(newAddrs.Interfaces) == 0 { + l.peerToPeerStatusUpdater.BroadcastStatus(peerstatus.NotPossible) + } if err != nil { return } @@ -199,6 +210,7 @@ func (l *localDiscovery) readAnswers(ch chan *zeroconf.ServiceEntry) { for entry := range ch { if entry.Instance == l.peerId { log.Debug("discovered self") + l.peerToPeerStatusUpdater.BroadcastPeerUpdate() continue } var portAddrs []string @@ -225,13 +237,10 @@ func (l *localDiscovery) browse(ctx context.Context, ch chan *zeroconf.ServiceEn err error newAddrs addrs.InterfacesAddrs ) - defer func() { - if err != nil { - l.sendP2PStatusEvent(pb.EventP2PStatus_NotConnected) - } - }() - l.sendP2PStatusEvent(pb.EventP2PStatus_Connected) newAddrs, err = addrs.GetInterfacesAddrs() + if len(newAddrs.Interfaces) == 0 { + l.peerToPeerStatusUpdater.BroadcastStatus(peerstatus.NotPossible) + } if err != nil { return } @@ -244,15 +253,3 @@ func (l *localDiscovery) browse(ctx context.Context, ch chan *zeroconf.ServiceEn log.Error("browsing failed", zap.Error(err)) } } - -func (l *localDiscovery) sendP2PStatusEvent(status pb.EventP2PStatusStatus) { - l.eventSender.Broadcast(&pb.Event{Messages: []*pb.EventMessage{ - { - Value: &pb.EventMessageValueOfP2PStatusUpdate{ - P2PStatusUpdate: &pb.EventP2PStatusUpdate{ - Status: status, - }, - }, - }, - }}) -} diff --git a/space/spacecore/localdiscovery/localdiscovery_android.go b/space/spacecore/localdiscovery/localdiscovery_android.go index e665ca654..18cafbf6f 100644 --- a/space/spacecore/localdiscovery/localdiscovery_android.go +++ b/space/spacecore/localdiscovery/localdiscovery_android.go @@ -8,6 +8,7 @@ import ( "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/peerstatus" "go.uber.org/zap" "github.com/anyproto/anytype-heart/core/anytype/config" @@ -40,10 +41,11 @@ type localDiscovery struct { peerId string port int - notifier Notifier - drpcServer clientserver.ClientServer - manualStart bool - m sync.Mutex + peerToPeerStatusUpdater StatusUpdater + notifier Notifier + drpcServer clientserver.ClientServer + manualStart bool + m sync.Mutex } func (l *localDiscovery) PeerDiscovered(peer DiscoveredPeer, own OwnAddresses) { @@ -53,6 +55,9 @@ func (l *localDiscovery) PeerDiscovered(peer DiscoveredPeer, own OwnAddresses) { } // TODO: move this to android side newAddrs, err := addrs.GetInterfacesAddrs() + if len(newAddrs.Interfaces) == 0 { + l.peerToPeerStatusUpdater.BroadcastStatus(peerstatus.NotPossible) + } if err != nil { return } @@ -83,6 +88,7 @@ func (l *localDiscovery) Init(a *app.App) (err error) { l.peerId = a.MustComponent(accountservice.CName).(accountservice.Service).Account().PeerId l.drpcServer = a.MustComponent(clientserver.CName).(clientserver.ClientServer) l.manualStart = a.MustComponent(config.CName).(*config.Config).DontStartLocalNetworkSyncAutomatically + l.peerToPeerStatusUpdater = app.MustComponent[StatusUpdater](a) return } diff --git a/space/spacecore/localdiscovery/localdiscovery_test.go b/space/spacecore/localdiscovery/localdiscovery_test.go new file mode 100644 index 000000000..24c3c16ad --- /dev/null +++ b/space/spacecore/localdiscovery/localdiscovery_test.go @@ -0,0 +1,101 @@ +package localdiscovery + +import ( + "context" + "testing" + + "github.com/anyproto/any-sync/accountservice/mock_accountservice" + "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/object/accountdata" + "github.com/anyproto/any-sync/nodeconf/mock_nodeconf" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + "github.com/anyproto/anytype-heart/core/anytype/config" + "github.com/anyproto/anytype-heart/core/event/mock_event" + "github.com/anyproto/anytype-heart/core/syncstatus/p2p" + "github.com/anyproto/anytype-heart/space/spacecore/clientserver/mock_clientserver" + "github.com/anyproto/anytype-heart/tests/testutil" +) + +type fixture struct { + LocalDiscovery + nodeConf *mock_nodeconf.MockService + eventSender *mock_event.MockSender + peerToPeerStatusUpdater *p2p.Observers + clientServer *mock_clientserver.MockClientServer + account *mock_accountservice.MockService +} + +func newFixture(t *testing.T) *fixture { + ctrl := gomock.NewController(t) + c := &config.Config{} + nodeConf := mock_nodeconf.NewMockService(ctrl) + eventSender := mock_event.NewMockSender(t) + peerToPeerStatusUpdater := p2p.NewObservers() + + clientServer := mock_clientserver.NewMockClientServer(t) + accountKeys, err := accountdata.NewRandom() + assert.Nil(t, err) + + account := mock_accountservice.NewAccountServiceWithAccount(ctrl, accountKeys) + a := &app.App{} + ctx := context.Background() + a.Register(c). + Register(testutil.PrepareMock(ctx, a, nodeConf)). + Register(testutil.PrepareMock(ctx, a, eventSender)). + Register(peerToPeerStatusUpdater). + Register(account). + Register(testutil.PrepareMock(ctx, a, clientServer)) + + discovery := New() + err = discovery.Init(a) + assert.Nil(t, err) + + f := &fixture{ + LocalDiscovery: discovery, + nodeConf: nodeConf, + eventSender: eventSender, + peerToPeerStatusUpdater: peerToPeerStatusUpdater, + clientServer: clientServer, + account: account, + } + return f +} + +func TestLocalDiscovery_Init(t *testing.T) { + t.Run("init success", func(t *testing.T) { + // given + f := newFixture(t) + + // when + f.clientServer.EXPECT().ServerStarted().Return(true) + f.clientServer.EXPECT().Port().Return(6789) + + err := f.Run(context.Background()) + assert.Nil(t, err) + + // then + err = f.Close(context.Background()) + assert.Nil(t, err) + }) +} + +func TestLocalDiscovery_checkAddrs(t *testing.T) { + t.Run("checkAddrs - server run successfully", func(t *testing.T) { + // given + f := newFixture(t) + f.clientServer.EXPECT().ServerStarted().Return(true) + f.clientServer.EXPECT().Port().Return(6789) + + err := f.Run(context.Background()) + assert.Nil(t, err) + + // when + ld := f.LocalDiscovery.(*localDiscovery) + err = ld.checkAddrs(context.Background()) + + // then + assert.Nil(t, err) + }) +} diff --git a/space/spacecore/peer.go b/space/spacecore/peer.go index 24ca8c043..dab7b6067 100644 --- a/space/spacecore/peer.go +++ b/space/spacecore/peer.go @@ -39,6 +39,7 @@ func (s *service) PeerDiscovered(peer localdiscovery.DiscoveredPeer, own localdi } log.Debug("got peer ids from peer", zap.String("peer", peer.PeerId), zap.Strings("spaces", resp.SpaceIds)) s.peerStore.UpdateLocalPeer(peer.PeerId, resp.SpaceIds) + s.peerToPeerStatus.SendPeerUpdate(resp.SpaceIds) } func (s *service) addSchema(addrs []string) (res []string) { diff --git a/space/spacecore/peermanager/manager.go b/space/spacecore/peermanager/manager.go index d50fb14b1..5e4b662fc 100644 --- a/space/spacecore/peermanager/manager.go +++ b/space/spacecore/peermanager/manager.go @@ -25,6 +25,10 @@ var ( ErrPeerFindDeadlineExceeded = errors.New("peer find deadline exceeded") ) +type StatusUpdater interface { + SendPeerUpdate(spaceIds []string) +} + type clientPeerManager struct { spaceId string responsibleNodeIds []string @@ -35,18 +39,20 @@ type clientPeerManager struct { watchingPeers map[string]struct{} rebuildResponsiblePeers chan struct{} availableResponsiblePeers chan struct{} + peerToPeerStatusObserver StatusUpdater ctx context.Context ctxCancel context.CancelFunc sync.Mutex } -func (n *clientPeerManager) Init(_ *app.App) (err error) { +func (n *clientPeerManager) Init(a *app.App) (err error) { n.responsibleNodeIds = n.peerStore.ResponsibleNodeIds(n.spaceId) n.ctx, n.ctxCancel = context.WithCancel(context.Background()) n.rebuildResponsiblePeers = make(chan struct{}, 1) n.watchingPeers = make(map[string]struct{}) n.availableResponsiblePeers = make(chan struct{}) + n.peerToPeerStatusObserver = app.MustComponent[StatusUpdater](a) return } @@ -143,15 +149,20 @@ func (n *clientPeerManager) getStreamResponsiblePeers(ctx context.Context) (peer peerIds = []string{p.Id()} } peerIds = append(peerIds, n.peerStore.LocalPeerIds(n.spaceId)...) + var needUpdate bool for _, peerId := range peerIds { p, err := n.p.pool.Get(ctx, peerId) if err != nil { n.peerStore.RemoveLocalPeer(peerId) log.Warn("failed to get peer from stream pool", zap.String("peerId", peerId), zap.Error(err)) + needUpdate = true continue } peers = append(peers, p) } + if needUpdate { + n.peerToPeerStatusObserver.SendPeerUpdate([]string{n.spaceId}) + } // set node error if no local peers if len(peers) == 0 { err = fmt.Errorf("failed to get peers for stream") @@ -181,15 +192,20 @@ func (n *clientPeerManager) fetchResponsiblePeers() { } peerIds := n.peerStore.LocalPeerIds(n.spaceId) + var needUpdate bool for _, peerId := range peerIds { p, err := n.p.pool.Get(n.ctx, peerId) if err != nil { n.peerStore.RemoveLocalPeer(peerId) log.Warn("failed to get local from net pool", zap.String("peerId", peerId), zap.Error(err)) + needUpdate = true continue } peers = append(peers, p) } + if needUpdate { + n.peerToPeerStatusObserver.SendPeerUpdate([]string{n.spaceId}) + } n.Lock() defer n.Unlock() diff --git a/space/spacecore/peermanager/manager_test.go b/space/spacecore/peermanager/manager_test.go index ecb469e9d..571196577 100644 --- a/space/spacecore/peermanager/manager_test.go +++ b/space/spacecore/peermanager/manager_test.go @@ -9,8 +9,15 @@ import ( "time" "github.com/anyproto/any-sync/net/peer" + "github.com/anyproto/any-sync/net/pool/mock_pool" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "storj.io/drpc" + + "github.com/anyproto/anytype-heart/core/syncstatus/p2p" + "github.com/anyproto/anytype-heart/core/syncstatus/p2p/mock_p2p" + "github.com/anyproto/anytype-heart/space/spacecore/peerstore" ) func TestClientPeerManager_GetResponsiblePeers_Deadline(t *testing.T) { @@ -79,6 +86,99 @@ func TestClientPeerManager_GetResponsiblePeers_Deadline(t *testing.T) { }) } +func Test_fetchResponsiblePeers(t *testing.T) { + spaceId := "spaceId" + t.Run("no local peers", func(t *testing.T) { + // given + f := newFixtureManager(t, spaceId) + + // when + f.pool.EXPECT().GetOneOf(gomock.Any(), gomock.Any()).Return(newTestPeer("id"), nil) + f.cm.fetchResponsiblePeers() + + // then + f.p2pStatusSender.AssertNotCalled(t, "SendPeerUpdate") + }) + t.Run("local peers connected", func(t *testing.T) { + // given + f := newFixtureManager(t, spaceId) + f.store.UpdateLocalPeer("peerId", []string{spaceId}) + + // when + f.pool.EXPECT().GetOneOf(gomock.Any(), gomock.Any()).Return(newTestPeer("id"), nil) + f.pool.EXPECT().Get(f.cm.ctx, "peerId").Return(newTestPeer("id1"), nil) + f.cm.fetchResponsiblePeers() + + // then + f.p2pStatusSender.AssertNotCalled(t, "SendPeerUpdate") + }) + t.Run("local peer not connected", func(t *testing.T) { + // given + f := newFixtureManager(t, spaceId) + f.store.UpdateLocalPeer("peerId", []string{spaceId}) + + // when + f.pool.EXPECT().GetOneOf(gomock.Any(), gomock.Any()).Return(newTestPeer("id"), nil) + f.pool.EXPECT().Get(f.cm.ctx, "peerId").Return(nil, fmt.Errorf("error")) + f.p2pStatusSender.EXPECT().SendPeerUpdate().Return() + f.cm.fetchResponsiblePeers() + + // then + f.p2pStatusSender.AssertCalled(t, "SendPeerUpdate") + }) +} + +func Test_getStreamResponsiblePeers(t *testing.T) { + spaceId := "spaceId" + t.Run("no local peers", func(t *testing.T) { + // given + f := newFixtureManager(t, spaceId) + + // when + f.pool.EXPECT().GetOneOf(gomock.Any(), gomock.Any()).Return(newTestPeer("id"), nil) + f.pool.EXPECT().Get(gomock.Any(), gomock.Any()).Return(newTestPeer("id"), nil) + peers, err := f.cm.getStreamResponsiblePeers(context.Background()) + + // then + assert.Nil(t, err) + assert.Len(t, peers, 1) + f.p2pStatusSender.AssertNotCalled(t, "SendPeerUpdate") + }) + t.Run("local peers connected", func(t *testing.T) { + // given + f := newFixtureManager(t, spaceId) + f.store.UpdateLocalPeer("peerId", []string{spaceId}) + + // when + f.pool.EXPECT().GetOneOf(gomock.Any(), gomock.Any()).Return(newTestPeer("id"), nil) + f.pool.EXPECT().Get(f.cm.ctx, "peerId").Return(newTestPeer("id1"), nil) + f.pool.EXPECT().Get(f.cm.ctx, "id").Return(newTestPeer("id"), nil) + peers, err := f.cm.getStreamResponsiblePeers(context.Background()) + + // then + assert.Nil(t, err) + assert.Len(t, peers, 2) + f.p2pStatusSender.AssertNotCalled(t, "SendPeerUpdate") + }) + t.Run("local peer not connected", func(t *testing.T) { + // given + f := newFixtureManager(t, spaceId) + f.store.UpdateLocalPeer("peerId", []string{spaceId}) + + // when + f.pool.EXPECT().GetOneOf(gomock.Any(), gomock.Any()).Return(newTestPeer("id"), nil) + f.pool.EXPECT().Get(f.cm.ctx, "peerId").Return(nil, fmt.Errorf("error")) + f.pool.EXPECT().Get(f.cm.ctx, "id").Return(newTestPeer("id"), nil) + f.p2pStatusSender.EXPECT().SendPeerUpdate().Return() + peers, err := f.cm.getStreamResponsiblePeers(context.Background()) + + // then + assert.Nil(t, err) + assert.Len(t, peers, 1) + f.p2pStatusSender.AssertCalled(t, "SendPeerUpdate") + }) +} + func newTestPeer(id string) *testPeer { return &testPeer{ id: id, @@ -154,3 +254,34 @@ func (t *testPeer) IsClosed() bool { func (t *testPeer) CloseChan() <-chan struct{} { return t.closed } + +type fixture struct { + cm *clientPeerManager + pool *mock_pool.MockPool + p2pStatusSender *mock_p2p.MockStatusUpdateSender + store peerstore.PeerStore +} + +func newFixtureManager(t *testing.T, spaceId string) *fixture { + ctrl := gomock.NewController(t) + pool := mock_pool.NewMockPool(ctrl) + provider := &provider{pool: pool} + store := peerstore.New() + observers := p2p.NewObservers() + p2pStatusSender := mock_p2p.NewMockStatusUpdateSender(t) + observers.AddObserver(spaceId, p2pStatusSender) + cm := &clientPeerManager{ + p: provider, + spaceId: spaceId, + peerStore: store, + peerToPeerStatusObserver: observers, + watchingPeers: map[string]struct{}{}, + ctx: context.Background(), + } + return &fixture{ + cm: cm, + pool: pool, + p2pStatusSender: p2pStatusSender, + store: store, + } +} diff --git a/space/spacecore/peerstore/peerstore.go b/space/spacecore/peerstore/peerstore.go index 1213906e2..24d648267 100644 --- a/space/spacecore/peerstore/peerstore.go +++ b/space/spacecore/peerstore/peerstore.go @@ -27,7 +27,6 @@ type PeerStore interface { func New() PeerStore { return &peerStore{ localPeerIdsBySpace: map[string][]string{}, - responsibleIds: map[string][]string{}, spacesByLocalPeerIds: map[string][]string{}, Mutex: sync.Mutex{}, } @@ -40,7 +39,6 @@ type peerStore struct { localPeerIds []string localPeerIdsBySpace map[string][]string spacesByLocalPeerIds map[string][]string - responsibleIds map[string][]string observers []Observer sync.Mutex } diff --git a/space/spacecore/service.go b/space/spacecore/service.go index 9a7298048..86e21b82f 100644 --- a/space/spacecore/service.go +++ b/space/spacecore/service.go @@ -30,6 +30,7 @@ import ( "github.com/anyproto/anytype-heart/core/anytype/config" "github.com/anyproto/anytype-heart/core/block/object/treesyncer" + "github.com/anyproto/anytype-heart/core/syncstatus/p2p" "github.com/anyproto/anytype-heart/core/wallet" "github.com/anyproto/anytype-heart/space/spacecore/clientspaceproto" "github.com/anyproto/anytype-heart/space/spacecore/localdiscovery" @@ -50,6 +51,10 @@ func New() SpaceCoreService { return &service{} } +type StatusUpdater interface { + SendPeerUpdate(spaceIds []string) +} + type PoolManager interface { UnaryPeerPool() pool.Pool StreamPeerPool() pool.Pool @@ -83,6 +88,7 @@ type service struct { poolManager PoolManager streamHandler *streamHandler syncStatusService syncStatusService + peerToPeerStatus StatusUpdater } type syncStatusService interface { @@ -119,6 +125,7 @@ func (s *service) Init(a *app.App) (err error) { ocache.WithTTL(time.Duration(s.conf.GCTTL)*time.Second), ) + s.peerToPeerStatus = app.MustComponent[StatusUpdater](a) err = spacesyncproto.DRPCRegisterSpaceSync(a.MustComponent(server.CName).(server.DRPCServer), &rpcHandler{s}) if err != nil { return @@ -244,7 +251,7 @@ func (s *service) Delete(ctx context.Context, spaceID string) (err error) { } func (s *service) loadSpace(ctx context.Context, id string) (value ocache.Object, err error) { - cc, err := s.commonSpace.NewSpace(ctx, id, commonspace.Deps{TreeSyncer: treesyncer.NewTreeSyncer(id)}) + cc, err := s.commonSpace.NewSpace(ctx, id, commonspace.Deps{TreeSyncer: treesyncer.NewTreeSyncer(id), PeerStatus: p2p.NewP2PStatus(id)}) if err != nil { return } diff --git a/util/testMock/mockSource/source_mock.go b/util/testMock/mockSource/source_mock.go index 98bf9f873..fbec0f603 100644 --- a/util/testMock/mockSource/source_mock.go +++ b/util/testMock/mockSource/source_mock.go @@ -5,6 +5,7 @@ // // mockgen -package mockSource -destination source_mock.go github.com/anyproto/anytype-heart/core/block/source Service,Source // + // Package mockSource is a generated GoMock package. package mockSource